Parking-24.uz Partner APIv1

SINXRON — darhol ASINXRON — keyin yetkaziladi POST /benefits/grants CMS auth · rule (so'm→daqiqa) 202 · status: accepted / rejected 202 4xx invalid_* · scope_denied · occurred_at_required accepted relay_status: pending sinov kaliti relay_status: sandbox applied applied daqiqa ochiq sessiyaga · chiqishda hisob kamayadi rejected · expired parking_session_not_found, near_miss…
Bitta grant ikki bosqichdan o'tadi. Sinxron — so'rovingizga darhol javob: 202 va status (yoki 4xx so'rov xato bo'lsa). Asinxron — imtiyoz boksga keyin yetkaziladi va yakuniy holat relay_status da. accepted «bonus berildi» degani emas — haqiqiy natijani relay_status aytadi (so'rab turing yoki webhook oling). Sinov kaliti hech qachon boksga bormaydi — relay_status: sandbox.

Siz haydovchi sizning obyektingizda qancha sarflaganini xabar qilasiz. Biz buning evaziga necha daqiqa bepul parkovka tegishini hal qilamiz va uni mashinaning ochiq parkovka sessiyasiga ilamiz.

Siz hech qachon daqiqa yubormaysiz va bizning tariflarimizni bilishingiz shart emas.

  • Manzil: https://api.parking-24.uz/partner/v1/
  • Integratsiya/sinov: https://api.dev.parking-24.uz/partner/v1/
  • Interaktiv spetsifikatsiya: /swagger/partner/alohida login va parol bilan ochiladi; bu API kaliti EMAS, ikkisini birga yuboramiz
  • Hammasi JSON. Pul har doim tiyin birligida (1 so'm = 100).

Avval sinov manziliga quring. U alohida muhit: o'z ma'lumoti va o'z kalitlari bilan — biriga berilgan kalit ikkinchisida ishlamaydi.


1. Kalit olish

Kalitni biz beramiz, har hamkorga alohida. Parking-24.uz dagi aloqachingizga murojaat qiling va qaysi tizimingizga kerakligini ayting — xaridni xabar qiladigan POS va takliflarni o'qiydigan mobil ilova alohida kalit oladi, chunki huquqlar (scope) kompaniyaga emas, kalitga biriktiriladi.

Kalit shunday ko'rinadi:

key_id  = pk_7f3a9c21
secret  = 4b8e...  (64 ta hex belgi)

Maxfiy qismni xavfsiz saqlang. Bizda faqat uning xeshi qoladi, shuning uchun uni qayta ayta olmaymiz. Yo'qolsa yangi kalit beramiz — eskisini tiklab bo'lmaydi.

Bir vaqtda ikkita kalit faol bo'la oladi. Almashtirishda shundan foydalaning: yangi kalitni oling, joylang, keyin eskisini bekor qilishni so'rang. Uzilish talab qiladigan almashtirishni hech kim qilmaydi.

Har kalit faqat o'ziga berilgan amallarga ishlaydi — yozadigan (POS) va o'qiydigan (ilova) kalitlar alohida. Agar 403 scope_denied ko'rsangiz, berilgan kalit shu chaqiruv uchun emas; bizga ayting, to'g'ri kalitni beramiz.

Bitta kalit — barcha nuqtalaringiz uchun. Kalit kompaniyaga tegishli, bitta parkovkaga emas: har so'rovda venue_id bilan qaysi nuqtaga ekanini ko'rsatasiz, shu bir kalit hammasiga yozadi. Zaryad shoxobchalari (charger) bizga ko'rinmaydi — ular sizniki; xohlasangiz attributes ichiga charger_id qo'shing (biz uni faqat saqlaymiz, qoida o'qimaydi). Ya'ni bitta app-kalit → ko'p parkovka (venue_id) → har parkovkada ko'p charger.


2. Autentifikatsiya

Har so'rovda:

Authorization: Bearer <key_id>.<secret>

Butun sxema shu — TLS ustidan bitta Bearer token, maxfiy qismi ichida.

Xatolar:

HTTP error.code Ma'nosi
401 invalid_client kalit noma'lum, maxfiy qism xato yoki kalit bekor qilingan
401 key_expired kalitning amal muddati o'tgan
403 scope_denied kalit to'g'ri, lekin bu chaqiruv uchun huquqi yo'q

invalid_client «bunday kalit yo'q» bilan «maxfiy qism xato» ni ataylab ajratmaydi va ikkalasiga bir xil vaqtda javob beradi.

Har chaqiruv kalitning «oxirgi ishlatilgan» vaqtini yangilaydi — uxlab qolgan kalitni ko'rish uchun.


3. Sarf faktini yozish

POST /partner/v1/benefits/grants
Authorization: Bearer pk_7f3a9c21.4b8e...
Idempotency-Key: 2f9c1a44-...
Content-Type: application/json

{
  "partner_ref": "charge-88213",
  "venue_id": "MST-CHL-01",
  "plate_text": "01A123AA",
  "basis_amount_minor": 12000000,
  "occurred_at": "2026-09-08T14:31:00Z",
  "attributes": { "kwh": 18.4, "idle_minutes": 12, "receipt": "R-4471" }
}
Maydon Majburiy Namuna Izoh
partner_ref ha charge-88213 bu hodisaning sizdagi raqami. Haqiqiy idempotentlik kaliti shu — §4 ga qarang
venue_id ha MST-CHL-01 kelishilgan nuqta raqami; qaysi parkovkaga tegishli ekanini belgilaydi
plate_text ha 01A123AA haydovchi ilovangizda kiritgan holicha. 6-10 belgi, raqam va A-Z
basis_amount_minor ha 12000000 haydovchi sarflagan summa, tiyinda. Har doim pul — hech qachon daqiqa emas
occurred_at ha 2026-09-08T14:31:00Z RFC3339 — haydovchi haqiqatan sarflagan payt (chek yetib kelgan vaqt emas)
attributes yo'q { "kwh": 18.4 } yozuvda qolishini xohlagan hamma narsa: kVt·soat, chek raqami, band turgan daqiqa. Qoida buni o'qimaydi — u audit va hisobot uchun saqlanadi

Nega pul, daqiqa emas

Ikki birlik ataylab qat'iy. Siz har doim so'm yuborasiz, biz har doim daqiqa beramiz. «Bir soat bepul» tarif o'zgarganda ham bir soat bo'lib qolishi kerak, va siz bizning narxlarimizni kuzatib yurishingiz shart emas. O'girish qoidasi biz tomonda va uni siz hech narsa joylamasdan o'zgartirish mumkin.

Raqam normalizatsiya qilinmaydi

Raqamni siz yuborgan holicha solishtiramiz — faqat katta harfga o'giramiz va bo'shliqni olib tashlaymiz. Tuzatmaymiz. Parkovkada yaqin raqam bo'lsa (bitta belgi farq), imtiyozni o'sha mashinaga jimgina bermaymiz — yozuv near_miss_needs_review bilan ushlab turiladi va operator ko'rib chiqadi.

Sababi: raqam ilovangizda bir marta ro'yxatdan o'tadi, ya'ni xato doimiy bo'ladi. Jim tuzatish uni har tashrifda begona mashinaga berib yurardi.

Javob

202 Accepted

{
  "grant_id": "3f2a...",
  "status": "accepted",
  "granted_minutes": 60,
  "relay_status": "pending",
  "message": ""
}

202 — «qabul qildik va yozdik», «haydovchida bepul daqiqa bor» EMAS. Parkovkaga yetkazish keyin bo'ladi va u muvaffaqiyatsiz tugashi yoki rad etilishi mumkin. Haqiqiy natijani relay_status aytadi; uni so'rab turing (§5).

status — bizning birinchi bosqichdagi qarorimiz:

status message Ma'nosi
accepted qoida daqiqa berdi, yetkazishga navbatga qo'yildi
rejected venue_unknown venue_id sizga tegishli emas
rejected venue_disabled nuqta bor, lekin o'chirilgan
accepted no_benefit_for_amount summa birinchi pog'onadan past — 0 daqiqa, va bu to'g'ri javob

So'rov xatolari (4xx, {"error":{...}}):

error.code Sababi
invalid_request tana o'qilmadi yoki to'g'ri JSON emas
plate_text_required plate_text yo'q
invalid_plate_format 0-9A-Z dan 6-10 belgi emas
invalid_amount basis_amount_minor manfiy
occurred_at_required occurred_at yo'q
invalid_partner_ref partner_ref yo'q yoki shakli noto'g'ri

4. Xavfsiz qayta urinish

Idempotentlik kaliti — partner_ref. Bir xilini ikki marta yuborsangiz birinchi natija qaytadi va message da duplicate partner_ref — returning the original result turadi. Hech narsa yaratilmaydi va ikki marta sanalmaydi. Status baribir 202.

Idempotency-Key sarlavhasi qabul qilinadi, lekin sizni himoya qiladigan narsa — partner_ref. Uni haqiqiy hodisa uchun barqaror va yagona qiling; o'z tranzaksiya raqamingiz eng tabiiy tanlov. Qayta urinishda yangisini yaratmang.

Tarmoq xatosida va 5xx da qayta urinib ko'ring — 5xx bizning tomondagi o'tkinchi holat, sizniki emas. Bir necha soniya kuting va o'sha partner_ref bilan qayta yuboring (idempotentlik ikki marta sanashdan saqlaydi); to'xtovsiz sikl o'rniga oshib boruvchi kutish qo'ying (masalan 1s → 2s → 4s). Muhimi: 5xx da error.code ga tayanmang502/503/504 oldingi proksidan kelib, JSON {error:{…}} konvertini olib kelmasligi mumkin; faqat status kodiga qarab qayta urinish qarorini bering. 4xx da urinmang: tana xato va uni takrorlash yordam bermaydi.

Chastota chegarasi

Har kalit uchun daqiqasiga 600 ta so'rov. Byudjet kalitga tegishli, hamkorga emas — ya'ni kassangiz va mobil ilovangiz bitta byudjet uchun kurashmaydi.

Chegaradan oshsa 429 keladi, error.code da rate_limited va sarlavhada Retry-After (sekundlarda). O'shancha kuting — darhol qayta urinish keyingi oynani ham yoqib yuboradi.

Daqiqasiga 600 — sekundiga o'nta, ya'ni normal integratsiya buni sezmaydi. U noto'g'ri sozlangan siklni to'xtatish uchun, sizga kvota sotish uchun emas.


5. Natijani o'qish

Natijani o'qish — ixtiyoriy. Minimal integratsiya 202 ni yuborib qo'yaqolsa bo'ladi (fire-and-forget) — imtiyozni baribir biz yetkazamiz. Bu yerni so'rab turing yoki webhook oling (§6) faqat yetkazishni tasdiqlamoqchi bo'lsangiz — masalan haydovchiga bepul parking ilanganini ko'rsatish uchun.

GET /partner/v1/benefits/grants/{partner_ref}
GET /partner/v1/benefits/grants?venue_id=&plate_text=&relay_status=&page=&page_size=

Siz faqat o'z yozuvlaringizni ko'rasiz: hamkor kodi tokendan olinadi, so'rovdan emas.

Muhim maydon — relay_status:

relay_status Ma'nosi Yakunimi?
pending navbatda, yoki parkovkaga yetib bo'lmadi va qayta urinladi yo'q
applied parkovka daqiqalarni ochiq sessiyaga iladi ha
rejected parkovka rad etdi — relay_reason ga qarang ha
pending_operator parkovkada odam qaroriga qo'yildi siz uchun ha; natija biz tomonda hal bo'ladi
expired qayta urinish oynasida yetkazilmadi ha
not_applicable yetkaziladigan narsa yo'q — so'rovni biz o'zimiz rad etdik; status va message ni o'qing ha
sandbox sinov kaliti, ya'ni parkovkaga umuman yuborilmaydi (§9) ha

Yakuniy BO'LMAGAN yagona qiymat — pending. So'rab turadigan bo'lsangiz, faqat shu qiymat uchun qaytish ma'noli.

Parkovka rad etganda relay_reason:

Sabab Ma'nosi
parking_session_not_found mashina hozir ichkarida emas
near_miss_needs_review o'xshash raqam ichkarida; operator tasdiqlashi kerak
session_cap_reached bu tashrifda bepul daqiqa chegarasi to'lgan
venue_unknown parkovka bu nuqtani konfiguratsiyasida hali olmagan
sponsor_balance_exhausted qoplovchi balans bo'sh; operator qaroriga qo'yildi
location_unknown, tenant_resolution_failed marshrutlash hal bo'lmadi — bizga ayting
relay_ttl_exceeded parkovka qayta urinish oynasi davomida yetib bo'lmas qoldi

Rad etish — normal natija, xato emas. Eng ko'p uchraydigani parking_session_not_found: haydovchi shunchaki hozir bizda turgan emas. Bunga ogohlantirish qo'ymang.

So'rash o'rniga xabar olish

Bizga HTTPS manzil bering — grant yakuniy relay_status ga yetganda o'sha manzilga POST qilamiz:

{ "grant_id": "3f2a...", "partner_ref": "charge-88213", "relay_status": "applied" }

Bu tana — turtki, ma'lumot emas. Unda ishonib saqlaydigan hech narsa yo'q: grantni yuqoridagi so'rov bilan qayta o'qing va haqiqat deb o'shani oling.

Shuning uchun imzo yo'q: xabarda soxtalashtirishga arziydigan narsa yo'q va takrorlangan yoki soxta chaqiruv hujumchiga hech nima bermaydi. Shu sababdan yo'qolgan xabar ham ma'lumot yo'qolishi emas — siz baribir so'rab bilasiz.

Har qanday 2xx bilan javob bering. Qolganida besh marta qayta urinamiz, keyin to'xtatamiz va o'z tomonimizda belgilaymiz. Takrorni qabul qiling: u dizayn bo'yicha zararsiz.

Rad etilgan yozuvlar saqlanadi va ularni o'qish foydali: hech qachon mos kelmaydigan raqam — odatda ilovangizda noto'g'ri ro'yxatdan o'tgan raqam.


6. Takliflarni ko'rsatish

GET /partner/v1/offers?parking_uid=PRK-AIRPORT-1     (scope: offers.read)
GET /partner/v1/parkings?page=1&page_size=50         (scope: parkings.read)

parking_uid — bizda allaqachon ishlatadigan ommaviy lokatsiya raqami, yangi identifikator emas.

Javob — tuzilgan ma'lumot, tayyor matn emas: hamkor nomi, summa-daqiqa qoidasi va bitta tashrifdagi chegara. Uni o'z tilingizda va o'z ohangingizda chiqarasiz.

{
  "items": [{
    "partner_code": "example",
    "partner_name": "Example Partner",
    "venue_id": "MST-CHL-01",
    "parking_uid": "PRK-AIRPORT-1",
    "rule": { "mode": "tiered", "tiers": [
      { "from_amount_minor": 5000000,  "minutes": 30 },
      { "from_amount_minor": 10000000, "minutes": 60 }
    ]},
    "session_cap_minutes": 120
  }]
}

Ikki chegara, ikkalasi ham ataylab:

Shaxsiy progress yo'q. «Siz 80 000 sarfladingiz, yana 20 000 qoldi» deb ayta olmaymiz — buning uchun sizning jonli xarid holatingiz kerak, u esa bizda yo'q. Biz taklifni ko'rsatamiz, progressni siz ko'rsatasiz.

E'lon qilish har nuqta uchun alohida va sukut bo'yicha yopiq. Hamkor o'z taklifini uchinchi tomon ilovasida ko'rsatishni istamasligi mumkin, shuning uchun nuqta ochiq deb belgilanmaguncha bu yerda hech narsa chiqmaydi.

Noma'lum parking_uid uchun bo'sh ro'yxat qaytadi, 404 emas — katalogingdagi har parkovka uchun so'rasang ham loglaring xatoga to'lmaydi.

GET /parkings har qatorda has_offers beradi, shunda ro'yxatda belgi qo'yish uchun har parkovkaga alohida /offers chaqirish kerak bo'lmaydi.


7. Versiya siyosati

/v1/ barqaror. Buzuvchi o'zgarishda /v2/ ochiladi va v1 kelishilgan muddat ishlab turadi.

Maydon qo'shish buzuvchi emas. Olib tashlash yoki ma'nosini o'zgartirish — buzuvchi.


8. Xatolar

Har xato bir xil konvertda:

{ "error": { "code": "scope_denied", "message": "this key does not carry benefits.write", "trace_id": "..." } }

Mantiqni code bo'yicha quring. message odam uchun, matni o'zgarishi mumkin va umuman bo'lmasligi ham mumkin — bir qancha xatolar, jumladan invalid_client va invalid_plate_format, faqat code va trace_id bilan keladi. Yo'qligini bo'sh satr deb oling va uni haydovchiga xom ko'rsatmang.

trace_id siz yuborgan X-Trace-Id sarlavhasini qaytaradi. Uni yuboring — bizga muammo haqida aytganingizda aynan shu qiymat kerakli so'rovni topishga imkon beradi. Yubormasangiz maydon bo'sh qaytadi.


9. Prodga chiqish

  1. Sizga sandbox deb belgilangan kalit beramiz va siz shunga qurasiz.
  2. venue_id ro'yxati va o'girish qoidasi kelishiladi.
  3. /offers da ko'rinishini xohlagan nuqtalaringizni e'lon qilamiz.
  4. Prod kaliti beriladi, siz joylaysiz, biz sandbox kalitini bekor qilamiz.

Sandbox kaliti nima qiladi

Sandbox kaliti bilan yozilgan fakt haqiqiy parkovkaga hech qachon yetkazilmaydi. Qolgan hammasi haqiqiy: kalit tekshiriladi, tana validatsiyadan o'tadi, qoida ishlaydi va haqiqiy daqiqa qaytaradi, partner_ref idempotentligi ham prodagi kabi. Faqat oxirgi bo'g'in ulanmaydi.

Ajratish oson: sandbox granti javobida "sandbox": true va yakuniy relay_statussandbox bo'ladi. U hech qachon applied bo'lmaydi va muddati ham o'tmaydi.

Ya'ni butun shartnomani — autentifikatsiya, huquqlar, validatsiya xatolari, pog'ona hisobi, qayta urinish — haqiqiy mashinaga bepul vaqt berish xavfisiz sinab ko'rasiz.

Alohida sandbox manzili yo'q: bir xil manzil, boshqa kalit.


Namunalar — curl bilan har bir holat

Quyida har bir holat: nima yuborasiz va nima qaytadi — dev muhitida jonli olingan. O'z javoblaringizni shu bilan solishtiring. Prod bir xil, faqat host api.parking-24.uz. $KEY — yozuvchi kalitingiz (benefits.write), $READ_KEY — o'quvchi (benefits.read+…). Har javob siz yuborgan X-Trace-Id ni qaytaradi (bu yerda qisqalik uchun tushirilgan).

Report a purchase → 202 accepted

curl -X POST "https://api.dev.parking-24.uz/partner/v1/benefits/grants" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: your-id" \
  -d '{
        "partner_ref": "charge-1789745211",
        "venue_id": "MST-STAND-B",
        "plate_text": "01C777CC",
        "basis_amount_minor": 12000000,
        "occurred_at": "2026-09-08T14:31:00Z",
        "attributes": {
          "kwh": 18.4,
          "charger_id": "CH-07"
        }
      }'
HTTP 202
{
  "grant_id": "01a0b520-a96a-7202-974e-084ab3da0397",
  "status": "accepted",
  "granted_minutes": 60,
  "relay_status": "pending"
}

Same partner_ref again → 202, original returned (idempotent)

curl -X POST "https://api.dev.parking-24.uz/partner/v1/benefits/grants" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: your-id" \
  -d '{
        "partner_ref": "charge-1789745211",
        "venue_id": "MST-STAND-B",
        "plate_text": "01C777CC",
        "basis_amount_minor": 99000000,
        "occurred_at": "2026-09-08T14:31:00Z"
      }'
HTTP 202
{
  "grant_id": "01a0b520-a96a-7202-974e-084ab3da0397",
  "status": "accepted",
  "granted_minutes": 60,
  "relay_status": "pending",
  "message": "duplicate partner_ref — returning the original result"
}

Amount below the first tier → 202 accepted, 0 minutes

curl -X POST "https://api.dev.parking-24.uz/partner/v1/benefits/grants" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: your-id" \
  -d '{
        "partner_ref": "charge-1789745211-z",
        "venue_id": "MST-STAND-B",
        "plate_text": "01C777CC",
        "basis_amount_minor": 0,
        "occurred_at": "2026-09-08T14:31:00Z"
      }'
HTTP 202
{
  "grant_id": "01a0b520-a9c9-744e-bd0a-bd2d1b311f82",
  "status": "accepted",
  "granted_minutes": 0,
  "relay_status": "not_applicable",
  "message": "no_benefit_for_amount"
}

Unknown venue → 202 rejected

curl -X POST "https://api.dev.parking-24.uz/partner/v1/benefits/grants" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: your-id" \
  -d '{
        "partner_ref": "charge-1789745211-v",
        "venue_id": "MST-NOPE-99",
        "plate_text": "01C777CC",
        "basis_amount_minor": 5000000,
        "occurred_at": "2026-09-08T14:31:00Z"
      }'
HTTP 202
{
  "grant_id": "01a0b520-a9e3-7c79-88ba-4823005d792c",
  "status": "rejected",
  "granted_minutes": 0,
  "relay_status": "not_applicable",
  "message": "venue_unknown"
}

Read one grant back → 200

curl "https://api.dev.parking-24.uz/partner/v1/benefits/grants/charge-1789745211" \
  -H "Authorization: Bearer $KEY" \
  -H "X-Trace-Id: your-id"
HTTP 200
{
  "grant_id": "01a0b520-a96a-7202-974e-084ab3da0397",
  "partner_code": "example",
  "partner_ref": "charge-1789745211",
  "venue_id": "MST-STAND-B",
  "location_id": "01a05929-d7da-78b6-ae73-4f4bf0c690c8",
  "sponsor_code": "example",
  "plate_text": "01C777CC",
  "basis_amount_minor": 12000000,
  "occurred_at": "2026-09-08T14:31:00Z",
  "granted_minutes": 60,
  "attributes": {
    "charger_id": "CH-07",
    "kwh": 18.4
  },
  "accept_status": "accepted",
  "relay_status": "pending",
  "created_at": "2026-09-18T15:26:51Z",
  "updated_at": "2026-09-18T15:26:51Z"
}

List your grants → 200

curl "https://api.dev.parking-24.uz/partner/v1/benefits/grants?venue_id=MST-STAND-B&page=1&page_size=2" \
  -H "Authorization: Bearer $KEY" \
  -H "X-Trace-Id: your-id"
HTTP 200
{
  "items": [
    {
      "grant_id": "01a0b520-a9c9-744e-bd0a-bd2d1b311f82",
      "partner_code": "example",
      "partner_ref": "charge-1789745211-z",
      "venue_id": "MST-STAND-B",
      "location_id": "01a05929-d7da-78b6-ae73-4f4bf0c690c8",
      "sponsor_code": "example",
      "plate_text": "01C777CC",
      "basis_amount_minor": 0,
      "occurred_at": "2026-09-08T14:31:00Z",
      "granted_minutes": 0,
      "accept_status": "accepted",
      "reject_reason": "no_benefit_for_amount",
      "relay_status": "not_applicable",
      "created_at": "2026-09-18T15:26:51Z",
      "updated_at": "2026-09-18T15:26:51Z"
    },
    {
      "grant_id": "01a0b520-a96a-7202-974e-084ab3da0397",
      "partner_code": "example",
      "partner_ref": "charge-1789745211",
      "venue_id": "MST-STAND-B",
      "location_id": "01a05929-d7da-78b6-ae73-4f4bf0c690c8",
      "sponsor_code": "example",
      "plate_text": "01C777CC",
      "basis_amount_minor": 12000000,
      "occurred_at": "2026-09-08T14:31:00Z",
      "granted_minutes": 60,
      "attributes": {
        "charger_id": "CH-07",
        "kwh": 18.4
      },
      "accept_status": "accepted",
      "relay_status": "pending",
      "created_at": "2026-09-18T15:26:51Z",
      "updated_at": "2026-09-18T15:26:51Z"
    }
  ],
  "page": 1,
  "page_size": 2,
  "total": 16,
  "total_pages": 8
}

Offers catalogue → 200

curl "https://api.dev.parking-24.uz/partner/v1/offers?parking_uid=42ADA199" \
  -H "Authorization: Bearer $KEY" \
  -H "X-Trace-Id: your-id"
HTTP 200
{
  "items": [
    {
      "partner_code": "example",
      "partner_name": "Example Partner",
      "venue_id": "MST-BDZ-01",
      "venue_label": "Bodomzor zaryadlash stansiyasi",
      "parking_uid": "42ADA199",
      "rule": {
        "mode": "tiered",
        "tiers": [
          {
            "from_amount_minor": 1,
            "minutes": 60
          }
        ],
        "cap_minutes": 60
      },
      "session_cap_minutes": 60
    }
  ]
}

Unknown parking → 200 empty list (not 404)

curl "https://api.dev.parking-24.uz/partner/v1/offers?parking_uid=YOQ-BUNDAY" \
  -H "Authorization: Bearer $KEY" \
  -H "X-Trace-Id: your-id"
HTTP 200
{
  "items": []
}

List parkings → 200

curl "https://api.dev.parking-24.uz/partner/v1/parkings?page=1&page_size=2" \
  -H "Authorization: Bearer $KEY" \
  -H "X-Trace-Id: your-id"
HTTP 200
{
  "items": [
    {
      "parking_uid": "DA4FBBE7",
      "name": "Asosiy kirish (Shimoliy)",
      "address": "Riviera Plaza, Shimoliy darvoza",
      "timezone": "Asia/Tashkent",
      "has_offers": false
    },
    {
      "parking_uid": "1424D286",
      "name": "CSpace-Yunusobod",
      "address": "Yunusobod",
      "timezone": "+5",
      "has_offers": false
    }
  ],
  "page": 1,
  "page_size": 2,
  "total": 10,
  "total_pages": 5
}

Malformed plate → 400

curl -X POST "https://api.dev.parking-24.uz/partner/v1/benefits/grants" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: your-id" \
  -d '{
        "partner_ref": "x-1789745211-p",
        "venue_id": "MST-STAND-B",
        "plate_text": "01 A 123",
        "basis_amount_minor": 12000000,
        "occurred_at": "2026-09-08T14:31:00Z"
      }'
HTTP 400
{
  "error": {
    "code": "invalid_plate_format"
  }
}

Missing occurred_at → 400

curl -X POST "https://api.dev.parking-24.uz/partner/v1/benefits/grants" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: your-id" \
  -d '{
        "partner_ref": "x-1789745211-t",
        "venue_id": "MST-STAND-B",
        "plate_text": "01C777CC",
        "basis_amount_minor": 12000000
      }'
HTTP 400
{
  "error": {
    "code": "occurred_at_required"
  }
}

Wrong secret → 401 (same code for unknown/wrong/revoked)

curl "https://api.dev.parking-24.uz/partner/v1/parkings?page=1&page_size=1" \
  -H "Authorization: Bearer pk_7f3a9c21.wrong-secret-here" \
  -H "X-Trace-Id: your-id"
HTTP 401
{
  "error": {
    "code": "invalid_client"
  }
}

Read-only key writes → 403 scope_denied

curl -X POST "https://api.dev.parking-24.uz/partner/v1/benefits/grants" \
  -H "Authorization: Bearer $READ_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: your-id" \
  -d '{
        "partner_ref": "x-1789745211-s",
        "venue_id": "MST-STAND-B",
        "plate_text": "01C777CC",
        "basis_amount_minor": 12000000,
        "occurred_at": "2026-09-08T14:31:00Z"
      }'
HTTP 403
{
  "error": {
    "code": "scope_denied",
    "message": "this key does not carry benefits.write"
  }
}

Expired key → 401 key_expired

curl "https://api.dev.parking-24.uz/partner/v1/parkings?page=1&page_size=1" \
  -H "Authorization: Bearer $EXPIRED_KEY" \
  -H "X-Trace-Id: your-id"
HTTP 401
{
  "error": {
    "code": "key_expired"
  }
}
Parking-24.uz — Partner API v1 api.dev.parking-24.uz/partner/v1