API v1 — maʼlumotnoma
Bu hujjat — har endpointning aniq tavsifi. Boshlash uchun qo'llanma — README.md; xatolar — errors.md; mashina o'qiydigan shartnoma — GET /api/v1/openapi.json (repo da openapi-integrators.json; kod bilan bir xil ekanini CI tekshiradi).
Manzil: https://muhrdor.uz. Hamma so'rov va javob — JSON (Content-Type: application/json), UTF-8. Vaqtlar — ISO 8601, UTC (2026-09-30T10:15:00Z). Baytlar — base64 (*_b64) yoki hex (*_hex).
0. Umumiy qoidalar
Kirish. Xizmat akkaunti (client_id + client_secret, panel «API kalitlari») tokenga almashtiriladi; keyin har so'rovda Authorization: Bearer <access_token>. API tokeni faqat shu hujjatdagi endpointlarni ochadi; boshqa har qanday /api/v1/* → 401 unauthorized.
Huquq. Xizmat akkaunti imzolash uchun kalit egasi imzolagan huquq akti kerak (panel «Huquqlar → Huquq berish → Xizmat akkaunti»). Akt saytlarni va hujjat turlarini nomlaydi; aktdan tashqari har urinish rad etiladi va egasining jurnaliga tushadi.
Xato shakli. Har xato:
{"error": {"code": "policy_denied", "reason": "SYSTEM_NOT_ALLOWED", "message_uz": "…",
"request_id": "…", "detail": "hujjat=didox.uz/xml:report, sayt=didox.uz, ruxsat=my.soliq.uz"}}code barqaror (dasturda shunga tayaning), reason — aniqlashtirish (policy_denied uchun sabab; invalid_request uchun maydon yo'li), message_uz — odam uchun (parse qilinmaydi), request_id — murojaatda ko'rsating, detail — faqat identifikatorlar, hech qachon hujjat mazmuni. retryable faqat signing_failed da.
Chegaralar.
| Nima | Chegara | Oshsa |
|---|---|---|
| Token so'rovi | 30 ta berilgan token/soat va 30 ta noto'g'ri secret/soat — har client_id ga; 100/soat — har IP | 429 too_many_requests |
| Imzolash | daqiqasiga 600 hujjat (so'rov emas), har xizmat akkaunti | 429 + Retry-After (soniya) |
| Bitta so'rov | 200 hujjat, jami 5 MiB (dekodlangan) | 422 invalid_request / document_too_large |
| Token muddati | 1 soat | 401 unauthorized → yangi token |
| Idempotentlik oynasi | 10 daqiqa | — |
1. Token
POST /api/v1/token
Xizmat akkauntining bearer tokeni (OAuth 2.0 client_credentials shakli, JSON tanada). Avtorizatsiya sarlavhasi kerak emas.
So'rov:
| Maydon | Tur | Talab | Izoh |
|---|---|---|---|
grant_type | string | majburiy | faqat "client_credentials" |
client_id | string | majburiy | sa_ + 24 hex |
client_secret | string | majburiy | 1..128 belgi |
Javob 200:
{"access_token": "…43 belgi…", "token_type": "Bearer", "expires_in": 3600}Xatolar: 401 invalid_client (noto'g'ri juftlik; akkaunt yoki mas'ul shaxs o'chirilgan; secret token berilayotgan paytda rotatsiya qilingan), 422 invalid_request (shakl), 429.
Eslatmalar: refresh token yo'q — muddati tugagach yangisini oling; har so'rovga token olmang. Rotatsiya yoki o'chirish barcha tokenlarni darhol o'ldiradi. Har berilgan token mas'ul shaxs jurnalida auth.login {client: "api"} sifatida.
2. Mijozning o'zi
GET /api/v1/client/me
Javob 200:
| Maydon | Izoh |
|---|---|
client_id | akkaunt identifikatori |
name | mas'ul shaxs bergan nom |
org_id | mas'ul shaxsning tashkiloti (akkauntning o'z jurnali shu zanjirda) |
owner_name | mas'ul shaxs ismi (bo'sh bo'lishi mumkin) |
expires_at | shu tokenning muddati |
GET /api/v1/client/credentials
Akkaunt imzolashi mumkin bo'lgan kalitlar — har amaldagi akt uchun bitta yozuv (bekor qilingan, muddati o'tgan va hali boshlanmagan aktlar ko'rsatilmaydi). Javob 200: {"credentials": [ … ]}, har element:
| Maydon | Izoh |
|---|---|
credential_id | imzolashda credential_id sifatida |
access | doim "grant" |
grant_ref | akt xeshi (sign.by_grant da ham shu) |
systems | akt saytlari, masalan ["my.soliq.uz"] |
doc_kinds | akt hujjat turlari, masalan ["hisobot", "login"] |
valid_from, valid_to | akt davri (null — chegarasiz) |
label, status | kalit nomi va holati (active bo'lsa imzolaydi) |
org_id, org_name, org_kind | kalit tashkiloti (personal | legal) |
owner_user_id | kalit egasi |
certificate_b64, serial, serial_hex, not_before, not_after, subject_attrs | sertifikat (DER, base64), seriya (o'nlik va hex), muddat, subyekt atributlari [[oid, qiymat], …] |
single_key, single_key_mode | yagona kalit holati (unverified bo'lsa sayt hujjatlari imzolanmaydi — egasi kabinet tekshiruvini o'tkazsin) |
profile, origin | algoritm profili; hsm | imported |
Ro'yxat ma'lumot uchun: hujjat o'tadimi — imzolash paytida aktning o'zi bo'yicha hal bo'ladi.
3. Imzolash
POST /api/v1/signatures/sign
Sarlavhalar: Authorization, Content-Type: application/json, Idempotency-Key (xizmat akkaunti uchun majburiy: 8..64 belgi A-Z a-z 0-9 . _ -; har yangi so'rovga yangi).
So'rov:
| Maydon | Tur | Talab | Izoh |
|---|---|---|---|
credential_id | string | majburiy | client/credentials dan |
site | string | amalda majburiy | hujjat qaysi sayt uchun (my.soliq.uz, didox.uz, …); aktda bo'lishi shart, bo'sh — rad |
documents | array | 1..200 | quyida |
page_url | string | ixtiyoriy | https://<site>/… — jurnalga havola sifatida; query tashlanadi; boshqa host — 422 |
confirm_token | string | shaxs sessiyasida, siyosat so'rasa (serverda MUHRDOR_CONFIRMATION=on bo'lsa; standart off — preview confirm: null qaytaradi) | preview bergan tasdiq tokeni (ADR-0061); xizmat akkauntiga tegishli emas |
documents[i]:
| Maydon | Tur | Sukut | Izoh |
|---|---|---|---|
content_b64 | string | — | hujjat baytlari (base64) |
detached | bool | true | true — PKCS#7 da kontent yo'q; false — kontent ichida (attached) |
append_to_b64 | string | — | ikkinchi imzo: mavjud attached PKCS#7; imzolanadigan kontent undan olinadi (content_b64 e'tiborsiz), natija — ikki imzoli PKCS#7 |
external_ref | string | — | sizning identifikatoringiz: ≤64, A-Z a-z 0-9 . _ : / -; javobda qaytadi va jurnalda saqlanadi (shaxsiy ma'lumot yozmang) |
Javob 200: {"results": [ … ]} — har hujjat uchun, index bo'yicha:
| Maydon | Muvaffaqiyat | Rad |
|---|---|---|
index | ✔ | ✔ |
external_ref | ✔ | ✔ |
audit_ref | ✔ (sign.requested yozuvi xeshi) | ✔ (rad yozuvi ham jurnalda) |
doc_class | ✔ (my.soliq.uz/hisobot, didox.uz/factura, xml:<ildiz>, …) | — |
pkcs7_b64 | ✔ DER PKCS#7 (E-IMZO shakli, CA zanjiri bilan) | — |
signature_hex | ✔ xom imzo | — |
signer_serial, signer_serial_hex | ✔ sertifikat seriyasi | — |
error | null | {code, reason, message_uz, request_id, detail} |
Holat kodi. Bir hujjatli so'rovda xato — xatoning o'z HTTP holati (403, 422, 502…). Ko'p hujjatli so'rovda — 200 va har elementda alohida natija yoki error; har hujjat alohida siyosat qarori — biri rad etilsa, boshqalari imzolanadi.
Idempotentlik. Xuddi shu kalit + xuddi shu tana (confirm_token hisobga olinmaydi), 10 daqiqa ichida → birinchi javob (imzo va jurnal yozuvi qaytarilmaydi). Boshqa tana → 422 idempotency_mismatch. Birinchisi hali bajarilayotgan bo'lsa → 409 request_in_progress (bir soniyadan keyin qayta). Xato bilan tugagan so'rov kalitni bo'shatadi. Server imzodan keyin, javobni saqlashdan oldin yiqilsa — kalit 10 daqiqa band, keyingi qayta yuborish ikkinchi imzo bo'ladi (errors.md).
Siyosat (403 policy_denied). Xizmat akkauntida sayt ishonchi yo'q: site akt systems ida bo'lishi va hujjat turi akt doc_kinds ida bo'lishi shart. Tuzilma turlari (xml:<ildiz>, json:<xesh>) faqat aktda aniq nomlangan bo'lsa. Sabablar — errors.md (NO_GRANT, SYSTEM_NOT_ALLOWED, DOC_KIND_NOT_ALLOWED, UNCLASSIFIED_CONTENT, EXPIRED, GRANT_REVOKED, CONSTRAINT_VIOLATED, SINGLE_KEY_UNVERIFIED, …). Platforma aktlari ({"type":"grant",…}) → 422 invalid_request reason: "platform act".
Hujjat turlari (adapterlar).
| Sayt | doc_class turlari |
|---|---|
my.soliq.uz | hisobot, login |
didox.uz | factura, qabul, hujjat, login |
xt-xarid.uz | hash32 |
my.it-park.uz | login |
| boshqa XML/JSON | xml:<ildiz element> / json:<xesh> — akt nomlasa |
Jurnal. Kalit tashkiloti zanjirida: sign.requested {client:"api"} → hsm.sign → sign.completed {client:"api", external_ref?}; rad — sign.denied {client:"api"}. Kalit boshqa tashkilotniki bo'lsa, akkaunt tashkiloti zanjirida sign.by_grant. Mazmun saqlanmaydi: kontent va imzolangan atributlar xeshi, tur, sayt, page_url, hujjat raqami/davri (maydon xaritasidan, summasiz), external_ref, akt havolasi.
POST /api/v1/signatures/preview
ADR-0061. Xuddi sign tanasi (credential_id, site, documents; page_url siz) — javob imzosiz: hujjatlar tasnifi va siyosat qarori. HSM ga tegilmaydi, jurnalga yozilmaydi, bir martalik akt sarflanmaydi; byudjet sign bilan bir xil (hujjat soni).
Javob 200:
| Maydon | Izoh |
|---|---|
allowed | siyosat ruxsat beradimi |
reason, reason_uz, detail | rad bo'lsa — sabab (errors.md jadvali) |
confirm | null yoki {reason, reason_uz, token} — shaxsning tasdig'i kerak: ONE_SHOT (bir martalik akt), BULK (10 dan ko'p hujjat), UNCLASSIFIED_DOC (tuzilma turi, akt *), NEW_SITE_FOR_KEY (kalit shu saytda 30 kunda imzolamagan). token 120 soniya, bir martalik, aynan shu hujjatlarga bog'langan — sign ga confirm_token sifatida |
documents[i] | index, doc_class, generic, size, sha256_prefix, doc_ref — ko'rsatish uchun, mazmunsiz |
key | {label, org_name} |
site | so'rovdagi sayt |
Xizmat akkaunti uchun confirm har doim null — mashina so'ralmaydi; sign tokensiz ishlaydi. Shaxs sessiyasida confirm bor ekan, tokensiz sign → 409 confirmation_required (reason: CONFIRMATION_REQUIRED); boshqa hujjat bilan yoki ikkinchi marta yuborilgan token ham shu javobni oladi.
GET /api/v1/signatures/{audit_ref}?credential_id=<kalit>
Saqlangan natija — audit_ref bo'yicha, faqat shu akkaunt imzolagan bo'lsa. credential_id kalit tashkilotining zanjirini tanlaydi (berilmasa — akkauntning o'z tashkiloti; akt bilan imzo odatda kalit tashkilotida).
Javob 200:
| Maydon | Izoh |
|---|---|
audit_ref | so'rov yozuvi |
completed_ref | sign.completed yozuvi xeshi |
pkcs7_sha256 | PKCS#7 xeshi (faylning o'zi saqlanmaydi — .p7b ni o'zingiz saqlang) |
signature_b64, signed_attrs_der_b64 | imzo va imzolangan atributlar (DER) |
signer_serial, signer_serial_hex | sertifikat seriyasi |
404 not_found — boshqa imzolovchining yozuvi yoki noto'g'ri audit_ref/credential_id.
POST /api/v1/signatures/pkcs7-info
PKCS#7 ni o'qish (tekshiruvsiz): kim, qachon imzolagan. So'rov {"pkcs7_b64": "…"}. Javob:
{"attached": true, "signers": [{"serial_hex": "5a3f…", "subject_x500": "cn=…,serialnumber=…", "signing_time": "2026-09-30T10:15:00+00:00"}]}subject_x500 — E-IMZO uslubidagi k=v,… qator (kichik harflar). 422 cms_invalid — o'qilmadi.
POST /api/v1/signatures/attach-timestamp
Saytning TSA (RFC 3161) tokenini imzoga qo'shish. So'rov: pkcs7_b64, signer_serial_hex (qaysi imzolovchiga), token_b64. Javob: {"pkcs7_b64": "…"} — id-aa-timeStampToken imzolanmagan atribut sifatida. Kalit ishlatilmaydi, jurnal yozuvi yo'q. 422 signer_not_found, 422 cms_invalid.
4. Shartnoma
GET /api/v1/openapi.json
Ochiq (tokensiz). OpenAPI 3.1, faqat yuqoridagi 7 ta operatsiya. Repo dagi nusxasi docs/api/openapi-integrators.json — kod o'zgarsa CI uni yangilashni talab qiladi.
5. Oqim qisqacha
token ──► client/credentials ──► sign (Idempotency-Key) ──► .p7b saqlash │ │ └── 401 bo'lsa yangi token └── tarmoq uzilsa: XUDDI SHU kalit bilan qayta
Tayyor misol: examples/muhrdor_sign.py (faqat standart kutubxona; partiyalar, kalitni saqlab qayta yuborish, Retry-After).
6. Versiya va o'zgarishlar
/api/v1 — barqaror: maydonlar qo'shilishi mumkin, olib tashlanmaydi va ma'nosi o'zgarmaydi; code lar o'zgarmaydi. Katta o'zgarish — yangi prefiks. O'zgarishlar docs/decisions/ (ADR) va docs/STATUS.md da.