Start sellingGet the app

OpenAPI 3.1REST · v1

Dona API

Connect a shop to your own system: products, stock and prices, orders, account health and events — through one REST API. Built for 1C, MoySklad, BILLZ, SmartUp, Linko and AI agents.

The shop owner creates a key in the seller portal: Settings › API. The key is shown once.

Base URL
https://api.dona.im/seller-api/v1
Key kinds
  • dona_sk_live_…integration key — your ERP, 1C, POS or own system
  • dona_ak_live_…AI-agent key — MCP only
  • dona_it_live_…vendor install token — coming soon

The API is opening in stages. The Status page shows what is on now; the Changelog shows what was added.

First callget/me
curl
curl -sS https://api.dona.im/seller-api/v1/me \
  -H "Authorization: Bearer $DONA_KEY"
200Example response
{
  "key": { … },
  "shop": { … },
  "tier": "basic",
  "api_access": { … },
  "writes_enabled": false,
  "limits": {
    "requests_per_min": 300,
    "burst": 60,
    "requests_per_day": 100000,
    "writes_per_min": 60,
    "writes_per_day": 20000,
    "in_flight": 4,
    "seller_requests_per_min": 900,
    "remaining": { … }
  },
  "attention": { … },
  "docs_url": "https://dona.uz/uz/developers"
}
Contents

0108

Quick start

4 steps

Four steps: a key, the first call, a safe rehearsal write, and the event feed.

  1. Create a key

    Seller portal › Settings › API › Create key. Tick only the scopes you need. Before creating the first key, the shop owner must accept Annex 2 (API and automated access) in the seller portal.

    Until the shop owner accepts the current Annex 2, every door — REST, MCP and OAuth — answers 403 agreement_required with accept_url, the portal page where it is accepted.

  2. Check who you are

    GET /me returns the key, the shop, its tier (Basic/Advanced), scopes and live limits.

    GET /me
    curl -sS https://api.dona.im/seller-api/v1/me \
      -H "Authorization: Bearer $DONA_KEY"
    GET /me response (the example from the spec)
    200
    {
      "key": {
        "id": "0199760c-2f4b-7a3e-9d21-5c8e4b1f0a37",
        "name": "billz-prod",
        "kind": "sk",
        "prefix": "dona_sk_live_3fK",
        "scopes": [
          "catalog:read",
          "catalog:stock",
          "orders:read"
        ],
        "expires_at": "2027-09-24T00:00:00+05:00",
        "ip_allow": []
      },
      "shop": {
        "id": "0198f3a2-6c1d-7b40-8e55-2a9c3d4e5f60",
        "name": "Bahor Textile",
        "status": "approved",
        "documents_status": "approved",
        "kyc_status": "approved",
        "can_sell": true
      },
      "tier": "basic",
      "api_access": {
        "state": "enabled",
        "reason": null
      },
      "writes_enabled": false,
      "limits": {
        "requests_per_min": 300,
        "burst": 60,
        "requests_per_day": 100000,
        "writes_per_min": 60,
        "writes_per_day": 20000,
        "in_flight": 4,
        "seller_requests_per_min": 900,
        "remaining": {
          "requests_per_min": 287,
          "requests_per_day": 99412,
          "writes_per_min": 60,
          "writes_per_day": 20000
        }
      },
      "attention": {
        "open": 3,
        "action_required": 2
      },
      "docs_url": "https://dona.uz/uz/developers"
    }
  3. Rehearse a stock write

    With Dona-Dry-Run: true a write is validated and its effect computed — and nothing is saved.

    While writes are closed (writes_enabled), the server answers 503 limits_unavailable — not a key error.

    POST /stock
    curl -sS -X POST https://api.dona.im/seller-api/v1/stock \
      -H "Authorization: Bearer $DONA_KEY" \
      -H "Idempotency-Key: $(uuidgen)" \
      -H "Dona-Dry-Run: true" \
      -H "Content-Type: application/json" \
      -d '{"items":[{"seller_sku":"A-100","quantity":12}]}'
  4. Follow the changes

    GET /events?cursor=… is the primary channel. Webhooks push the same events.

    GET /events
    curl -sS "https://api.dona.im/seller-api/v1/events?cursor=$LAST_EVENT_ID&limit=200" \
      -H "Authorization: Bearer $DONA_KEY"

0208

Reference

14 groups · 61 operations

Every operation is rendered from the OpenAPI contract when the site is built. The badges show the key tier and the scope each operation needs.

Basic
Any valid key
Advanced
Identity, documents and shop approved, no active sanction
Public
No key
Common parameters (listed by name on each operation)11
  • Idempotency-Keyheader · string

    1–255 chars. Scope = this key × route. Terminal 2xx/4xx replayed byte-identical for 24 h; a 5xx is never stored. Missing ⇒ 400 invalid_body with details[{field:"Idempotency-Key",code:"required"}].

  • Accept-Languageheader · string

    Localises message in error bodies and single-language renderings. Default uz.

  • X-Dona-Integrationheader · string

    name/version of the calling integration; stored (≤ 128 chars) and searchable in the request journal.

  • Dona-Dry-Runheader · string

    true ⇒ validate + guard + compute the effect, then roll back. Zero side-effect rows (no events, no activity, no counters). Same as ?dry_run=true. Only the literal true / false; any other value ⇒ 400 invalid_body + details[{field:"Dona-Dry-Run", code:"invalid"}] (C51).

  • dry_runquery · string

    Alias of the Dona-Dry-Run header. Only the literal strings true / false — 1, 0, TRUE, yes, an empty value ⇒ 400 invalid_body + details[{field:"dry_run", code:"invalid"}] (C51). A string enum, not a boolean, so no SDK generator serialises it as 1/0.

  • Dona-Sellerheader · string (uuid)

    Vendor-app install keys only (dona_it_live_…, S6) — and then REQUIRED on every request, public routes included: the id of the shop the install key belongs to. Missing ⇒ 400 invalid_body + details[{field:"Dona-Seller", code:"required"}]; sent more than once, or not ONE id in the canonical form the API prints (lower-case, 36 characters — no braces, no urn:uuid:, no padding) ⇒ 400 invalid_body + details[{field:"Dona-Seller", code:"invalid"}]; naming any other shop — even one that installed the same app ⇒ 404 not_found (never 403; nothing is read). Ignored on a seller key (dona_sk_).

  • If-None-Matchheader · string

    An ETag from a previous identical request ⇒ 304 with rate headers (cost 0.5).

  • cursorquery · string

    Opaque keyset cursor from next_cursor.

  • limitquery · integer

    Page size, default 50, max 100 (clamped, with Dona-API-Warn). limit > 50 costs 1 + ceil(limit/50).

  • updated_sincequery · string

    ISO 8601 with offset, or epoch milliseconds. The visible high-water mark is capped at now() − 10 s so a slow transaction is never skipped.

  • pagequery · integer

    0-based page alias (Uzum parity) — slower than cursor, ≤ 1 000 pages; mutually exclusive with cursor.

Account

1 operation

The key's own view of itself and its shop.

get/meBasic

Who am I — key, shop, tier, limits

BasicScope: anyCost: key-rate · 1

The first call of every integration: the key's own row, the shop's three verdicts, the derived tier, the live limits with remaining budget, and the open attention counts. Fails closed: a DB error answers 503 auth_unavailable.

Parameters (3)
Response 200 · Me
  • keyobjectrequired
  • shopobjectrequired
  • tierstringrequired

    Key tier, derived per request (never cached): ADVANCED = kyc approved AND documents approved (a waiver never counts) AND shop approved AND no active punishment; seller_api_access.tier_override may lower or raise it but never lifts a sanction.

    basic · advanced

  • api_accessobjectrequired
  • writes_enabledbooleanrequired

    app_config.seller_api.writes_enabled (seeded false until D5).

  • limitsLimitsrequired
  • attentionobjectrequired
  • docs_urlstring (uri)required

    The developer docs home in the key owner's language (https://dona.uz/<uz|ru|en>/developers; the owner's users.language, else Accept-Language, else uz).

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/me" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "key": {
    "id": "0199760c-2f4b-7a3e-9d21-5c8e4b1f0a37",
    "name": "billz-prod",
    "kind": "sk",
    "prefix": "dona_sk_live_3fK",
    "scopes": [
      "catalog:read",
      "catalog:stock",
      "orders:read"
    ],
    "expires_at": "2027-09-24T00:00:00+05:00",
    "ip_allow": []
  },
  "shop": {
    "id": "0198f3a2-6c1d-7b40-8e55-2a9c3d4e5f60",
    "name": "Bahor Textile",
    "status": "approved",
    "documents_status": "approved",
    "kyc_status": "approved",
    "can_sell": true
  },
  "tier": "basic",
  "api_access": {
    "state": "enabled",
    "reason": null
  },
  "writes_enabled": false,
  "limits": {
    "requests_per_min": 300,
    "burst": 60,
    "requests_per_day": 100000,
    "writes_per_min": 60,
    "writes_per_day": 20000,
    "in_flight": 4,
    "seller_requests_per_min": 900,
    "remaining": {
      "requests_per_min": 287,
      "requests_per_day": 99412,
      "writes_per_min": 60,
      "writes_per_day": 20000
    }
  },
  "attention": {
    "open": 3,
    "action_required": 2
  },
  "docs_url": "https://dona.uz/uz/developers"
}

Catalog

10 operations

Products, variants, media, publish/delist. Writes go through the shared guarded seams.

get/productsBasic

List products

BasicScope: catalog:readCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

Keyset list of the shop's products (deleted_at IS NULL). Deletions never appear in a delta — read /products/deleted.

Parameters (12)
Response 200 · ProductPage
  • itemsProduct[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/products" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
      "title": {
        "uz": "Erkaklar futbolkasi, paxta",
        "ru": "Футболка мужская, хлопок"
      },
      "description": {
        "uz": "100% paxta, Oʻzbekistonda tikilgan."
      },
      "status": "active",
      "category_id": "01972a10-1111-7aaa-8bbb-0123456789ab",
      "brand": "Bahor",
      "seller_sku": "A-200",
      "barcode": "4780000000012",
      "external_id": "1c:0f2a9b",
      "price_uzs": 189000,
      "compare_at_uzs": 229000,
      "stock": 10,
      "has_variants": true,
      "variants": [
        {
          "id": "0199612e-8a0c-7d11-a0b2-9e8f7a6b5c4d",
          "options": {
            "color": "Qizil",
            "size": "M"
          },
          "sku": "A-200-R-M",
          "barcode": "4780000000029",
          "price_uzs": 189000,
          "compare_at_uzs": null,
          "stock": 4,
          "status": "active",
          "version": "2026-09-24T13:59:41.120318+05:00"
        }
      ],
      "ikpu": "06109100001000000",
      "package_code": "1520227",
      "image_urls": [
        "https://media.dona.im/p/0199612e/1.webp"
      ],
      "video_url": null,
      "attributes": {
        "material": "paxta"
      },
      "created_via": "api",
      "hold": null,
      "open_issues": 0,
      "version": "2026-09-24T13:59:41.120318+05:00",
      "created_at": "2026-09-20T10:11:12+05:00",
      "updated_at": "2026-09-24T13:59:41+05:00"
    }
  ],
  "next_cursor": "eyJ1IjoiMjAyNi0wOS0yNFQwODo1OTo0MSJ9",
  "has_more": true
}
post/productsBasic

Create a product (lands as draft / ai_review)

BasicScope: catalog:writeCost: write-rate · 1

UpsertExternalProduct with created_via='api'. Lands draft/ai_review; publish:true asks to publish, and a pending shop's publish waits in product_shop_holds (hold.reason=shop_not_activated). The plausibility guard may answer 202 held_for_review (e.g. price_floor). Kill switch: writes_enabled.

Parameters (6)
Request body · ProductCreate
  • titleLocalizedTextrequired

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

    LocalizedText
    • uzstring
    • rustring
    • enstring
  • descriptionLocalizedText

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

    LocalizedText
    • uzstring
    • rustring
    • enstring
  • category_idstring (uuid)required
  • brandstring
  • seller_skustringrequired
  • barcodestring
  • external_idstring

    Your system's id, e.g. 1c:0f2a….

  • price_uzsinteger (int64)required
  • compare_at_uzsinteger | null
  • stockintegerrequired
  • ikpustring
  • package_codestring
  • image_urlsstring (uri)[]
  • video_urlstring (uri)
  • attributesobject
  • variantsVariantInput[]
    VariantInput[]
    • optionsobjectrequired
    • skustring
    • barcodestring
    • price_uzsinteger (int64)required
    • compare_at_uzsinteger | null
    • stockintegerrequired
  • publishboolean

    Ask to publish after create; a pending shop's product is held until documents approval.

Response 201 · ProductCreated
  • idstring (uuid)required
  • statusstringrequired
  • holdobject | nullrequired
  • issuesIssue[]required
  • created_via"api"required
Responses
  • 201Created
  • 202Held for review — nothing written.
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/products" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"title":{"uz":"uz","ru":"string","en":"string"},"category_id":"0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e","seller_sku":"A-100","price_uzs":1,"stock":12}'
Example response
{
  "id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
  "status": "draft",
  "hold": {
    "reason": "shop_not_activated"
  },
  "issues": [],
  "created_via": "api"
}
get/products/{id}Basic

Get a product

BasicScope: catalog:readCost: key-rate · 1

One product with its variants.

Parameters (4)
Response 200 · Product
  • idstring (uuid)required
  • titleLocalizedTextrequired

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

  • descriptionLocalizedText | nullrequired
  • statusstringrequired

    draft, ai_review, active, hidden today; tolerate new values.

  • category_idstring | nullrequired
  • brandstring | nullrequired
  • seller_skustring | nullrequired
  • barcodestring | nullrequired
  • external_idstring | nullrequired
  • price_uzsinteger (int64)required

    Integer soʻm (no decimals).

  • compare_at_uzsinteger | nullrequired

    Must exceed price_uzs (0020 CHECK).

  • stockintegerrequired

    Sum over variants when has_variants.

  • has_variantsbooleanrequired
  • variantsVariant[]required
  • ikpustring | nullrequired
  • package_codestring | nullrequired
  • image_urlsstring (uri)[]required
  • video_urlstring | nullrequired
  • attributesobjectrequired
  • created_viastringrequired

    manual, mass_upload, api, … (products_created_via_check).

  • holdobject | nullrequired
  • open_issuesintegerrequired

    Open listing_issues + product_violations.

  • versionstringrequired

    Opaque (today the full-precision updated_at); echo verbatim.

  • created_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/products/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
  "title": {
    "uz": "Erkaklar futbolkasi, paxta",
    "ru": "Футболка мужская, хлопок"
  },
  "description": {
    "uz": "100% paxta, Oʻzbekistonda tikilgan."
  },
  "status": "active",
  "category_id": "01972a10-1111-7aaa-8bbb-0123456789ab",
  "brand": "Bahor",
  "seller_sku": "A-200",
  "barcode": "4780000000012",
  "external_id": "1c:0f2a9b",
  "price_uzs": 189000,
  "compare_at_uzs": 229000,
  "stock": 10,
  "has_variants": true,
  "variants": [
    {
      "id": "0199612e-8a0c-7d11-a0b2-9e8f7a6b5c4d",
      "options": {
        "color": "Qizil",
        "size": "M"
      },
      "sku": "A-200-R-M",
      "barcode": "4780000000029",
      "price_uzs": 189000,
      "compare_at_uzs": null,
      "stock": 4,
      "status": "active",
      "version": "2026-09-24T13:59:41.120318+05:00"
    }
  ],
  "ikpu": "06109100001000000",
  "package_code": "1520227",
  "image_urls": [
    "https://media.dona.im/p/0199612e/1.webp"
  ],
  "video_url": null,
  "attributes": {
    "material": "paxta"
  },
  "created_via": "api",
  "hold": null,
  "open_issues": 0,
  "version": "2026-09-24T13:59:41.120318+05:00",
  "created_at": "2026-09-20T10:11:12+05:00",
  "updated_at": "2026-09-24T13:59:41+05:00"
}
patch/products/{id}Basic

Update a product

BasicScope: catalog:writeCost: write-rate · 1

ApplyExternalFields → gateEditActivation. A price change runs the plausibility guard (may 202). Kill switch: writes_enabled.

Parameters (7)
Request body · ProductUpdate
  • titleLocalizedText

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

    LocalizedText
    • uzstring
    • rustring
    • enstring
  • descriptionLocalizedText

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

    LocalizedText
    • uzstring
    • rustring
    • enstring
  • category_idstring (uuid)
  • brandstring
  • barcodestring
  • external_idstring
  • price_uzsinteger (int64)
  • compare_at_uzsinteger | null
  • stockinteger

    Refused (details[].code=stock_not_editable) when the product has variants — use POST /stock with variant_id.

  • ikpustring
  • package_codestring
  • image_urlsstring (uri)[]
  • video_urlstring | null
  • attributesobject
  • versionstring

    Optional optimistic lock; stale ⇒ 409 version_conflict.

Response 200 · Product
  • idstring (uuid)required
  • titleLocalizedTextrequired

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

  • descriptionLocalizedText | nullrequired
  • statusstringrequired

    draft, ai_review, active, hidden today; tolerate new values.

  • category_idstring | nullrequired
  • brandstring | nullrequired
  • seller_skustring | nullrequired
  • barcodestring | nullrequired
  • external_idstring | nullrequired
  • price_uzsinteger (int64)required

    Integer soʻm (no decimals).

  • compare_at_uzsinteger | nullrequired

    Must exceed price_uzs (0020 CHECK).

  • stockintegerrequired

    Sum over variants when has_variants.

  • has_variantsbooleanrequired
  • variantsVariant[]required
  • ikpustring | nullrequired
  • package_codestring | nullrequired
  • image_urlsstring (uri)[]required
  • video_urlstring | nullrequired
  • attributesobjectrequired
  • created_viastringrequired

    manual, mass_upload, api, … (products_created_via_check).

  • holdobject | nullrequired
  • open_issuesintegerrequired

    Open listing_issues + product_violations.

  • versionstringrequired

    Opaque (today the full-precision updated_at); echo verbatim.

  • created_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
  • 202Held for review — nothing written.
Request
curl -sS -X PATCH "https://api.dona.im/seller-api/v1/products/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"title":{"uz":"uz","ru":"string","en":"string"},"description":{"uz":"uz","ru":"string","en":"string"},"category_id":"0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e"}'
Example response
{
  "id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
  "title": {
    "uz": "Erkaklar futbolkasi, paxta",
    "ru": "Футболка мужская, хлопок"
  },
  "description": {
    "uz": "100% paxta, Oʻzbekistonda tikilgan."
  },
  "status": "active",
  "category_id": "01972a10-1111-7aaa-8bbb-0123456789ab",
  "brand": "Bahor",
  "seller_sku": "A-200",
  "barcode": "4780000000012",
  "external_id": "1c:0f2a9b",
  "price_uzs": 189000,
  "compare_at_uzs": 229000,
  "stock": 10,
  "has_variants": true,
  "variants": [
    {
      "id": "0199612e-8a0c-7d11-a0b2-9e8f7a6b5c4d",
      "options": {
        "color": "Qizil",
        "size": "M"
      },
      "sku": "A-200-R-M",
      "barcode": "4780000000029",
      "price_uzs": 189000,
      "compare_at_uzs": null,
      "stock": 4,
      "status": "active",
      "version": "2026-09-24T13:59:41.120318+05:00"
    }
  ],
  "ikpu": "06109100001000000",
  "package_code": "1520227",
  "image_urls": [
    "https://media.dona.im/p/0199612e/1.webp"
  ],
  "video_url": null,
  "attributes": {
    "material": "paxta"
  },
  "created_via": "api",
  "hold": null,
  "open_issues": 0,
  "version": "2026-09-24T13:59:41.120318+05:00",
  "created_at": "2026-09-20T10:11:12+05:00",
  "updated_at": "2026-09-24T13:59:41+05:00"
}
get/products/{id}/issuesBasic

Why a product is held, rejected or flagged

BasicScope: catalog:readCost: key-rate · 1

Open listing_issues, product_shop_holds and product_violations for one product.

Parameters (4)
Response 200 · ProductIssues
  • product_idstring (uuid)required
  • itemsIssue[]required
Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/products/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/issues" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "product_id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
  "items": [
    {
      "id": "01996200-aaaa-7bbb-8ccc-0123456789ef",
      "source": "listing_issue",
      "severity": "error",
      "type": "missing_field",
      "field": "ikpu",
      "detail": "IKPU kodi kiritilmagan",
      "created_at": "2026-09-24T10:00:00+05:00"
    }
  ]
}
get/products/deletedBasic

Tombstones since a time

BasicScope: catalog:readCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

Soft-deleted products (deleted_at) since since — deltas never carry deletions.

Parameters (6)
Response 200 · TombstonePage
  • itemsTombstone[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/products/deleted?since=<since>" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
      "seller_sku": "A-199",
      "external_id": null,
      "deleted_at": "2026-09-23T18:00:00+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
post/products/{id}/publishBasic

Publish

BasicScope: catalog:writeCost: write-rate · 1

gateBlocksActivation. Pending shop ⇒ 200 with hold.reason=shop_not_activated (goes live on documents approval). A gate failure ⇒ 400 invalid_body with details[] (the same issues /issues lists). A price below the catalogue floor ⇒ 202 held_for_review (price_floor; activation waits for an approver). Kill switch: writes_enabled.

Parameters (7)
Response 200 · ProductState
  • idstring (uuid)required
  • statusstringrequired
  • holdobject | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
  • 202Held for review — nothing written.
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/products/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/publish" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true"
Example response
{
  "id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
  "status": "active",
  "hold": null,
  "updated_at": "2026-09-24T14:05:12+05:00"
}
post/products/{id}/delistBasic

Delist (hide) — never a hard delete

BasicScope: catalog:writeCost: write-rate · 1

Sets the product delisted (the portal's delist). Delisting more than 30 % of the shop's live products at once ⇒ 202 held_for_review (delist_30pct). There is no hard delete on this API. Kill switch: writes_enabled.

Parameters (7)
Response 200 · ProductState
  • idstring (uuid)required
  • statusstringrequired
  • holdobject | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
  • 202Held for review — nothing written.
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/products/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/delist" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true"
Example response
{
  "id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
  "status": "delisted",
  "hold": null,
  "updated_at": "2026-09-24T14:05:12+05:00"
}
post/products/batchBasic

Batch create/update (async job)

BasicScope: catalog:writeCost: write-rate · 10

≤ 100 commands ⇒ 202 + a job; poll GET /jobs/{id} (≥ 5 s; Retry-After set). ≤ 2 running jobs per key, ≤ 20/day per shop. Results use the per-line shape. Kill switch: writes_enabled.

Parameters (6)
Request body · BatchRequest
  • itemsBatchCommand[]required
    BatchCommand[]
    • opstringrequired

      create · update

    • product_idstring (uuid)

      Required when op=update.

    • createProductCreate
      ProductCreate
      • titleLocalizedTextrequired

        jsonb {uz,ru,en}; readers fall back uz → ru → en.

      • descriptionLocalizedText

        jsonb {uz,ru,en}; readers fall back uz → ru → en.

      • category_idstring (uuid)required
      • brandstring
      • seller_skustringrequired
      • barcodestring
      • external_idstring

        Your system's id, e.g. 1c:0f2a….

      • price_uzsinteger (int64)required
      • compare_at_uzsinteger | null
      • stockintegerrequired
      • ikpustring
      • package_codestring
      • image_urlsstring (uri)[]
      • video_urlstring (uri)
      • attributesobject
      • variantsVariantInput[]
      • publishboolean

        Ask to publish after create; a pending shop's product is held until documents approval.

    • updateProductUpdate

      Partial update: an absent field is unchanged.

      ProductUpdate
      • titleLocalizedText

        jsonb {uz,ru,en}; readers fall back uz → ru → en.

      • descriptionLocalizedText

        jsonb {uz,ru,en}; readers fall back uz → ru → en.

      • category_idstring (uuid)
      • brandstring
      • barcodestring
      • external_idstring
      • price_uzsinteger (int64)
      • compare_at_uzsinteger | null
      • stockinteger

        Refused (details[].code=stock_not_editable) when the product has variants — use POST /stock with variant_id.

      • ikpustring
      • package_codestring
      • image_urlsstring (uri)[]
      • video_urlstring | null
      • attributesobject
      • versionstring

        Optional optimistic lock; stale ⇒ 409 version_conflict.

Response 202 · JobAccepted
  • job_idstring (uuid)required
  • status"pending"required
  • linksobjectrequired
Responses
  • 202Accepted
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/products/batch" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"op":"create"}]}'
Example response
{
  "job_id": "01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d",
  "status": "pending",
  "links": {
    "self": "/seller-api/v1/jobs/01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d"
  }
}
post/media/upload-urlBasic

Presigned media upload

BasicScope: catalog:writeCost: write-rate · 1

Presigned R2 PUT (≤ 60/min, ≤ 20 MB). No Idempotency-Key: a second call just mints another URL. Kill switch: writes_enabled.

Parameters (3)
Request body · MediaUploadRequest
  • content_typestringrequired

    image/jpeg · image/png · image/webp · video/mp4

  • size_bytesintegerrequired

    ≤ 20 MB; signed into the PUT so storage enforces it.

Response 200 · MediaUploadUrl
  • keystringrequired
  • upload_urlstring (uri)required

    Presigned PUT — send the exact bytes, no Authorization header.

  • public_urlstring (uri)required

    Use this in image_urls.

  • expires_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/media/upload-url" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content_type":"image/jpeg","size_bytes":1}'
Example response
{
  "key": "p/0198f3a2/01997b40.webp",
  "upload_url": "https://r2.dona.im/ozb-media/p/0198f3a2/01997b40.webp?X-Amz-Expires=900&X-Amz-Signature=3f9a0c",
  "public_url": "https://media.dona.im/p/0198f3a2/01997b40.webp",
  "expires_at": "2026-09-24T14:20:12+05:00"
}

Stock & prices

3 operations

Absolute, bulk, guarded (catalog.ApplyGuardedStockPrice).

get/stockBasic

Stock, SKU-level

BasicScope: catalog:readCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

One row per product without variants, one per variant otherwise. cursor or 0-based page (slower).

Parameters (9)
Response 200 · StockLineViewPage
  • itemsStockLineView[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/stock" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "product_id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
      "variant_id": "0199612e-8a0c-7d11-a0b2-9e8f7a6b5c4d",
      "seller_sku": "A-200-R-M",
      "barcode": "4780000000029",
      "external_id": null,
      "quantity": 4,
      "version": "2026-09-24T13:59:41.120318+05:00",
      "updated_at": "2026-09-24T13:59:41+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
post/stockBasic

Set absolute stock (≤ 1 000 lines)

BasicScope: catalog:stockCost: write-rate · ceil(lines/10)

Synchronous, through catalog.ApplyGuardedStockPrice. 200 with per-line results — the request is refused whole only for shape/budget (400/413/422). ?atomic=true = all-or-nothing (any held line ⇒ 202 for the batch). |Δ| > 10× and > 1 000, or zeroing > 50 % of lines ⇒ held. One object ≤ 1 write / 10 s ⇒ line error: object_cooldown. Kill switch: writes_enabled.

Parameters (7)
Request body · StockRequest
  • itemsStockLine[]required
    StockLine[]
    • product_idstring (uuid)
    • seller_skustring
    • barcodestring
    • external_idstring
    • variant_idstring (uuid)
    • quantityintegerrequired

      ABSOLUTE stock (not a delta).

    • versionstring

      Optional; stale ⇒ line error: version_conflict.

    • warehousestring

      Reserved — one stock location per shop. Any value ⇒ the whole request 422 warehouse_unsupported.

Response 200 · BulkResult
  • resultsLineResult[]required
  • summaryobjectrequired
  • dry_runbooleanrequired
Responses
  • 200OK
  • 202<code>atomic=true</code> and a line was held — nothing written.
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/stock" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"product_id":"0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e","quantity":12}]}'
Example response
{
  "results": [
    {
      "index": 0,
      "status": "ok",
      "seller_sku": "A-100",
      "quantity": 12,
      "version": "2026-09-24T14:05:12.004211+05:00"
    },
    {
      "index": 1,
      "status": "error",
      "barcode": "4780000000012",
      "variant_id": "0199612e-8a0c-7d11-a0b2-9e8f7a6b5c4d",
      "error": "version_conflict",
      "message": "Mahsulot boshqa yozuv bilan oʻzgargan."
    },
    {
      "index": 2,
      "status": "held",
      "product_id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
      "approval_id": "01997b31-1b2c-7d3e-8f4a-5b6c7d8e9f0a",
      "rule": "stock_jump_10x"
    }
  ],
  "summary": {
    "ok": 1,
    "error": 1,
    "held": 1
  },
  "dry_run": false
}
post/pricesBasic

Set absolute prices (≤ 1 000 lines)

BasicScope: catalog:stockCost: write-rate · ceil(lines/10)

As POST /stock. Price < catalog.price_floor_uzs (1 000) or > 100× drop vs current/last-sold ⇒ held; compare_at_uzs must exceed price_uzs. Kill switch: writes_enabled.

Parameters (7)
Request body · PriceRequest
  • itemsPriceLine[]required
    PriceLine[]
    • product_idstring (uuid)
    • seller_skustring
    • barcodestring
    • external_idstring
    • variant_idstring (uuid)
    • price_uzsinteger (int64)required

      ABSOLUTE price.

    • compare_at_uzsinteger | null

      Must exceed price_uzs; null clears.

    • versionstring
Response 200 · BulkResult
  • resultsLineResult[]required
  • summaryobjectrequired
  • dry_runbooleanrequired
Responses
  • 200OK
  • 202<code>atomic=true</code> and a line was held — nothing written.
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/prices" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"product_id":"0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e","price_uzs":1}]}'
Example response
{
  "results": [
    {
      "index": 0,
      "status": "ok",
      "seller_sku": "A-100",
      "price_uzs": 189000,
      "compare_at_uzs": 229000,
      "version": "2026-09-24T14:05:12.004211+05:00"
    },
    {
      "index": 1,
      "status": "held",
      "product_id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
      "approval_id": "01997b31-1b2c-7d3e-8f4a-5b6c7d8e9f0a",
      "rule": "price_floor"
    }
  ],
  "summary": {
    "ok": 1,
    "error": 0,
    "held": 1
  },
  "dry_run": false
}

Orders

13 operations

The orders:read projection has no buyer PII; orders:pii (ADVANCED, sk) adds it.

get/ordersBasic

List orders (no buyer PII)

BasicScope: orders:readCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1; unfiltered (no updated_since) above 2/min ×3

The orders:read projection. status takes the portal buckets (all, pending, paid, ready_to_ship, to_ship, shipped, delivered, cancelled, return_refund). from/to span ≤ 90 d, to exclusive.

Parameters (11)
  • cursorquery · string
  • pagequery · integer
  • limitquery · integer
  • updated_sincequery · string
  • statusquery · string

    Portal bucket.

  • schemequery · string

    Derived fulfilment scheme.

  • fromquery · string (date-time)

    created_at ≥ from.

  • toquery · string (date-time)

    created_at < to.

  • Accept-Languageheader · string
  • Dona-Sellerheader · string (uuid)
  • X-Dona-Integrationheader · string
Response 200 · OrderPage
  • itemsOrder[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/orders" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
      "order_code": "AB1758704000XZ",
      "status": "paid",
      "payment_status": "paid",
      "payment_method": "cod",
      "scheme": "third_party",
      "total_uzs": 259000,
      "accepted_at": null,
      "declined_at": null,
      "shipped_at": null,
      "delivered_at": null,
      "cancelled_by": null,
      "decline_reason_code": null,
      "accept_deadline": "2026-09-25T09:12:00+05:00",
      "ship_by_deadline": null,
      "items": [
        {
          "id": "01997b2e-41c1-7a22-9b3c-4d5e6f7a8b9c",
          "product_id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
          "variant_id": "0199612e-8a0c-7d11-a0b2-9e8f7a6b5c4d",
          "seller_sku": "A-200-R-M",
          "title": {
            "uz": "Erkaklar futbolkasi, paxta",
            "ru": "Футболка мужская, хлопок"
          },
          "quantity": 2,
          "cancelled_quantity": 0,
          "unit_price_uzs": 129500
        }
      ],
      "meta": {
        "uzum_status": "CREATED"
      },
      "created_at": "2026-09-24T09:12:00+05:00",
      "updated_at": "2026-09-24T09:12:31+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
get/orders/{id}Basic

Get an order (PII only with orders:pii)

BasicScope: orders:readCost: key-rate · 1

orders:read ⇒ Order. A key that ALSO holds orders:pii (ADVANCED, sk only, S4) gets OrderWithPii (adds recipient) and the call is logged pii=true. When the orders:pii door would refuse this request (the shop is not ADVANCED now, or the key's recipient is third-party — D7) the ORDER is still answered, without recipient, and Dona-API-Warn: recipient withheld: <tier_required|pii_third_party_pending_counsel|…> says why (C59).

Parameters (4)
Response 200 · Order | OrderWithPii

One of: Order · OrderWithPii

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "order_code": "AB1758704000XZ",
  "status": "paid",
  "payment_status": "paid",
  "payment_method": "cod",
  "scheme": "third_party",
  "total_uzs": 259000,
  "accepted_at": null,
  "declined_at": null,
  "shipped_at": null,
  "delivered_at": null,
  "cancelled_by": null,
  "decline_reason_code": null,
  "accept_deadline": "2026-09-25T09:12:00+05:00",
  "ship_by_deadline": null,
  "items": [
    {
      "id": "01997b2e-41c1-7a22-9b3c-4d5e6f7a8b9c",
      "product_id": "0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e",
      "variant_id": "0199612e-8a0c-7d11-a0b2-9e8f7a6b5c4d",
      "seller_sku": "A-200-R-M",
      "title": {
        "uz": "Erkaklar futbolkasi, paxta",
        "ru": "Футболка мужская, хлопок"
      },
      "quantity": 2,
      "cancelled_quantity": 0,
      "unit_price_uzs": 129500
    }
  ],
  "meta": {
    "uzum_status": "CREATED"
  },
  "created_at": "2026-09-24T09:12:00+05:00",
  "updated_at": "2026-09-24T09:12:31+05:00",
  "recipient": {
    "name": "Dilnoza K.",
    "phone": "+998901234567",
    "address": "Toshkent sh., Yunusobod t., 11-kvartal, 5-uy, 23-xonadon"
  }
}
get/orders/{id}/timelineBasic

Order timeline

BasicScope: orders:readCost: key-rate · 1

order_events in order.

Parameters (4)
Response 200 · OrderTimeline
  • order_idstring (uuid)required
  • itemsOrderTimelineEntry[]required
Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/timeline" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "order_id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "items": [
    {
      "id": "01997b2e-7f00-7123-8456-789abcdef012",
      "type": "placed",
      "actor": "buyer",
      "note": null,
      "occurred_at": "2026-09-24T09:12:00+05:00"
    },
    {
      "id": "01997b2f-0102-7345-9678-9abcdef01234",
      "type": "paid",
      "actor": "system",
      "note": null,
      "occurred_at": "2026-09-24T09:12:31+05:00"
    }
  ]
}
post/orders/{id}/acceptBasic

Accept

BasicScope: orders:writeCost: write-rate · 1

The intake.go accept body, extracted. Needs a pickup address. 409: order_not_acceptable, no_pickup_address. Kill switch: writes_enabled.

Parameters (7)
Response 200 · OrderTransition
  • idstring (uuid)required
  • statusstringrequired
  • payment_statusstringrequired
  • accepted_atstring | nullrequired
  • shipped_atstring | nullrequired
  • cancelled_bystring | nullrequired

    seller · buyer · system

  • decline_reason_codestring | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/accept" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true"
Example response
{
  "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "status": "paid",
  "payment_status": "paid",
  "accepted_at": "2026-09-24T14:05:12+05:00",
  "shipped_at": null,
  "cancelled_by": null,
  "decline_reason_code": null,
  "updated_at": "2026-09-24T14:05:12+05:00"
}
post/orders/{id}/readyBasic

Mark ready to ship

BasicScope: orders:writeCost: write-rate · 1

Sets ready_to_ship. 409: order_not_shippable. Kill switch: writes_enabled.

Parameters (7)
Response 200 · OrderTransition
  • idstring (uuid)required
  • statusstringrequired
  • payment_statusstringrequired
  • accepted_atstring | nullrequired
  • shipped_atstring | nullrequired
  • cancelled_bystring | nullrequired

    seller · buyer · system

  • decline_reason_codestring | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/ready" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true"
Example response
{
  "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "status": "ready_to_ship",
  "payment_status": "paid",
  "accepted_at": "2026-09-24T14:05:12+05:00",
  "shipped_at": null,
  "cancelled_by": null,
  "decline_reason_code": null,
  "updated_at": "2026-09-24T14:05:12+05:00"
}
post/orders/{id}/shipBasic

Ship

BasicScope: orders:writeCost: write-rate · 1

409: order_not_shippable, order_awaiting_courier. Kill switch: writes_enabled.

Parameters (7)
Response 200 · OrderTransition
  • idstring (uuid)required
  • statusstringrequired
  • payment_statusstringrequired
  • accepted_atstring | nullrequired
  • shipped_atstring | nullrequired
  • cancelled_bystring | nullrequired

    seller · buyer · system

  • decline_reason_codestring | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/ship" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true"
Example response
{
  "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "status": "shipped",
  "payment_status": "paid",
  "accepted_at": "2026-09-24T14:05:12+05:00",
  "shipped_at": "2026-09-24T14:05:12+05:00",
  "cancelled_by": null,
  "decline_reason_code": null,
  "updated_at": "2026-09-24T14:05:12+05:00"
}
post/orders/{id}/handoverBasic

Hand over to the courier

BasicScope: orders:writeCost: write-rate · 1

sellerMayTransitionSQL rules. 409: order_not_shippable. Kill switch: writes_enabled.

Parameters (7)
Response 200 · OrderTransition
  • idstring (uuid)required
  • statusstringrequired
  • payment_statusstringrequired
  • accepted_atstring | nullrequired
  • shipped_atstring | nullrequired
  • cancelled_bystring | nullrequired

    seller · buyer · system

  • decline_reason_codestring | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/handover" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true"
Example response
{
  "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "status": "shipped",
  "payment_status": "paid",
  "accepted_at": "2026-09-24T14:05:12+05:00",
  "shipped_at": "2026-09-24T14:05:12+05:00",
  "cancelled_by": null,
  "decline_reason_code": null,
  "updated_at": "2026-09-24T14:05:12+05:00"
}
post/orders/{id}/notesBasic

Add a seller note

BasicScope: orders:writeCost: write-rate · 1

Appends to order_notes. Kill switch: writes_enabled.

Parameters (7)
Request body · NoteRequest
  • textstringrequired
Response 201 · NoteCreated
  • idstring (uuid)required
  • order_idstring (uuid)required
  • created_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 201Created
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/notes" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"text":"string"}'
Example response
{
  "id": "01997b2f-0102-7345-9678-9abcdef01234",
  "order_id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "created_at": "2026-09-24T14:05:12+05:00"
}
post/orders/{id}/declineAdvanced

Decline (before acceptance) — money-reversing

AdvancedScope: orders:cancelCost: write-rate · 1

An order NOT YET ACCEPTED. The portal decline's own body (order.DeclineOrderInTx) on the marketplace pool, in the write pipeline (stamped transaction, wallet-cutover fence): restock, the buyer's kiwi/vouchers/delivery money back, the declined/seller timeline row; the card refund at the provider and the buyer notice run after the commit (never on a dry run). The reason is stored in decline_reason_code. ADVANCED, derived on THIS request (a documents-waived or downgraded shop is 403 tier_required). 400 invalid_decline_reason (details[comment: required] for other without words); 409 order_not_acceptable (an accepted or closed order — the portal names it order_not_decidable; cancel an accepted one), order_has_active_return. Another shop's order ⇒ 404. Kill switch: writes_enabled. Served by the marketplace process only: a SERVER_ROLE=seller-api server answers 503 role_unavailable (after the key, scope and tier; before the dry-run flag, the switch, the body and the idempotency claim — nothing changes; D3).

Parameters (7)
Request body · DeclineRequest
  • reasonstringrequired

    Uzum subset (OUT_OF_STOCK, OUT_OF_PACKAGE, OUT_OF_TIME, OTHER) mapped onto the native declineReasons, OR a native lowercase code. OTHER/other needs comment. Unknown ⇒ 400 invalid_decline_reason.

    OUT_OF_STOCK · OUT_OF_PACKAGE · OUT_OF_TIME · OTHER · out_of_stock · inventory_mismatch · product_damaged · store_unavailable · cannot_fulfill_in_time · duplicate_order · fraud_suspected · other

  • commentstring
Response 200 · OrderTransition
  • idstring (uuid)required
  • statusstringrequired
  • payment_statusstringrequired
  • accepted_atstring | nullrequired
  • shipped_atstring | nullrequired
  • cancelled_bystring | nullrequired

    seller · buyer · system

  • decline_reason_codestring | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/decline" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"reason":"OUT_OF_STOCK"}'
Example response
{
  "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "status": "cancelled",
  "payment_status": "paid",
  "accepted_at": null,
  "shipped_at": null,
  "cancelled_by": "seller",
  "decline_reason_code": "out_of_stock",
  "updated_at": "2026-09-24T14:05:12+05:00"
}
post/orders/{id}/cancelAdvanced

Cancel (after acceptance) — money-reversing

AdvancedScope: orders:cancelCost: write-rate · 1

An ACCEPTED order: the portal seller-cancel's own statements (order.SellerCancelOrderTx) — same reasons (C15) and money effects as decline. The reason is stored in the order's cancel_reason (with the comment), NOT in decline_reason_code, which stays null (C52); the cancelled/seller timeline row, the card refund and the buyer notice run after the commit. 409 order_not_cancellable — a delivered or cancelled order, or one NOT YET ACCEPTED (details[{field:"status", code:"not_accepted"}]: decline it) — and order_has_active_return. Kill switch: writes_enabled. 503 role_unavailable from a SERVER_ROLE=seller-api server, as decline.

Parameters (7)
Request body · DeclineRequest
  • reasonstringrequired

    Uzum subset (OUT_OF_STOCK, OUT_OF_PACKAGE, OUT_OF_TIME, OTHER) mapped onto the native declineReasons, OR a native lowercase code. OTHER/other needs comment. Unknown ⇒ 400 invalid_decline_reason.

    OUT_OF_STOCK · OUT_OF_PACKAGE · OUT_OF_TIME · OTHER · out_of_stock · inventory_mismatch · product_damaged · store_unavailable · cannot_fulfill_in_time · duplicate_order · fraud_suspected · other

  • commentstring
Response 200 · OrderTransition
  • idstring (uuid)required
  • statusstringrequired
  • payment_statusstringrequired
  • accepted_atstring | nullrequired
  • shipped_atstring | nullrequired
  • cancelled_bystring | nullrequired

    seller · buyer · system

  • decline_reason_codestring | nullrequired
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/cancel" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Dona-Dry-Run: true" \
  -H "Content-Type: application/json" \
  -d '{"reason":"OUT_OF_STOCK"}'
Example response
{
  "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "status": "cancelled",
  "payment_status": "paid",
  "accepted_at": "2026-09-24T14:05:12+05:00",
  "shipped_at": null,
  "cancelled_by": "seller",
  "decline_reason_code": null,
  "updated_at": "2026-09-24T14:05:12+05:00"
}
get/orders/{id}/label.pdfAdvanced

Shipping label (PDF, contains PII)

AdvancedScope: orders:piiCost: key-rate · 1

The portal's sticker for one order: prints the buyer's name, phone and address. sk keys only (orders:pii on any other kind ⇒ 403 insufficient_scope + details[scope_not_allowed_for_kind]); ADVANCED now (403 tier_required); a key minted for a THIRD-PARTY recipient ⇒ 403 tier_required + details[pii_recipient: pii_third_party_pending_counsel] (D7). Logged pii=true (always journaled, never sampled); never stored by the API. Normal key-rate bucket (C54). 409 no_tracking_number (+ orders: the order codes without a carrier number yet — print later), no_items_selected, all_items_delayed (nothing left to put in the parcel) (C53). A server draws at most 2 documents at once: past that 429 api_busy (Dona-Rate-Limited-Reason: global, Retry-After: 2) before any read, and a batch is not charged.

Parameters (4)
Response 200

Response type: application/pdf

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/label.pdf" \
  -H "Authorization: Bearer $DONA_KEY"
get/orders/{id}/invoice.pdfAdvanced

Invoice (PDF, contains PII)

AdvancedScope: orders:piiCost: key-rate · 1

The invoice for one order, drawn as a PDF from the SAME figures as the portal's invoice page (one loader). Doors, 409s and the 429 api_busy bound as label.pdf (C53, C54). ⚠ PII set WIDER than the label's: like the portal's invoice it prints the PURCHASER's account name and phone (the buyer who is invoiced) — on a gift order that is not the recipient the label and the recipient block name (Form A Q12 states both).

Parameters (4)
Response 200

Response type: application/pdf

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/orders/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/invoice.pdf" \
  -H "Authorization: Bearer $DONA_KEY"
post/orders/labelsAdvanced

Labels for ≤ 100 orders (one PDF)

AdvancedScope: orders:piiCost: key-rate · 10

A read with a body — no Idempotency-Key. Any foreign/missing id ⇒ 404 not_found for the whole call. Doors and 409s as label.pdf (C53); no_tracking_number names every offending order. Cost 10 on the key-rate bucket (C54), charged only once the batch holds a drawing slot (429 api_busy as label.pdf).

Parameters (3)
Request body · LabelsRequest
  • order_idsstring (uuid)[]required
Response 200

Response type: application/pdf

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/orders/labels" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"order_ids":["0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e"]}'

Returns

2 operations

Read-only in v1.

get/returnsBasic

List returns

BasicScope: returns:readCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

Read-only in v1 — approving a return is orders.refund, never issuable.

Parameters (7)
Response 200 · ReturnPage
  • itemsReturn[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/returns" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01997c10-5d2e-7f30-8c41-5a6b7c8d9e0f",
      "order_id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
      "status": "requested",
      "type": "return",
      "reason_code": "wrong_size",
      "refund_uzs": 129500,
      "due_at": "2026-09-27T12:00:00+05:00",
      "items": [
        {
          "order_item_id": "01997b2e-41c1-7a22-9b3c-4d5e6f7a8b9c",
          "quantity": 1
        }
      ],
      "reason_text": null,
      "created_at": "2026-09-24T12:00:00+05:00",
      "updated_at": "2026-09-24T12:00:00+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
get/returns/{id}Basic

Get a return

BasicScope: returns:readCost: key-rate · 1

Includes the buyer's reason_text (detail only).

Parameters (4)
Response 200 · Return
  • idstring (uuid)required
  • order_idstring (uuid)required
  • statusstringrequired

    requested · approved · refunded · rejected · disputed · closed

  • typestringrequired
  • reason_codestringrequired
  • refund_uzsinteger (int64)required

    Integer soʻm (no decimals).

  • due_atstring | nullrequired
  • itemsobject[]required
  • reason_textstring | nullrequired

    Buyer-authored; detail only (null on lists).

  • created_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/returns/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "01997c10-5d2e-7f30-8c41-5a6b7c8d9e0f",
  "order_id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "status": "requested",
  "type": "return",
  "reason_code": "wrong_size",
  "refund_uzs": 129500,
  "due_at": "2026-09-27T12:00:00+05:00",
  "items": [
    {
      "order_item_id": "01997b2e-41c1-7a22-9b3c-4d5e6f7a8b9c",
      "quantity": 1
    }
  ],
  "reason_text": "Oʻlcham kichik keldi.",
  "created_at": "2026-09-24T12:00:00+05:00",
  "updated_at": "2026-09-24T12:00:00+05:00"
}

Health

3 operations

Account health, metrics, verification verdicts.

get/account/healthBasic

Account health summary

BasicScope: health:readCost: key-rate · 1

accounthealth.BuildSummary projection.

Parameters (3)
Response 200 · HealthSummary
  • health_labelstringrequired

    good · fair · poor

  • reputation_scorenumber | nullrequired
  • penalty_pointsintegerrequired
  • penalty_maxintegerrequired
  • punishment_activebooleanrequired
  • suppressionobject | nullrequired
  • metricsMetric[]required
  • open_attentionintegerrequired
  • computed_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/account/health" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "health_label": "good",
  "reputation_score": 87.4,
  "penalty_points": 0,
  "penalty_max": 12,
  "punishment_active": false,
  "suppression": null,
  "metrics": [
    {
      "id": "late_shipment_rate",
      "group": "fulfilment",
      "current_value": "3.1%",
      "target": "< 10%",
      "applied_to": [
        "own_fleet",
        "third_party"
      ],
      "passing": true,
      "has_detail": true
    }
  ],
  "open_attention": 3,
  "computed_at": "2026-09-24T14:05:12+05:00"
}
get/account/metrics/{metric_id}Basic

One metric with weekly history

BasicScope: health:readCost: key-rate · 1

Unknown metric id ⇒ 404 not_found.

Parameters (4)
Response 200 · MetricDetail

Response type: application/json

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/account/metrics/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "late_shipment_rate",
  "group": "fulfilment",
  "current_value": "3.1%",
  "target": "< 10%",
  "applied_to": [
    "own_fleet",
    "third_party"
  ],
  "passing": true,
  "has_detail": true,
  "history": [
    {
      "week_start": "2026-09-14",
      "value": 0.031
    }
  ]
}
get/account/verificationBasic

The three verdicts and what ADVANCED still needs

BasicScope: health:readCost: key-rate · 1

From seller_api_shop_v.

Parameters (3)
Response 200 · Verification
  • statusstringrequired

    pending · approved · suspended · blocked · restricted · closed

  • documents_statusstringrequired
  • kyc_statusstringrequired
  • documents_waivedbooleanrequired
  • bank_confirmedbooleanrequired
  • can_sellbooleanrequired
  • tierstringrequired

    Key tier, derived per request (never cached): ADVANCED = kyc approved AND documents approved (a waiver never counts) AND shop approved AND no active punishment; seller_api_access.tier_override may lower or raise it but never lifts a sanction.

    basic · advanced

  • tier_missingstring[]required

    Empty when ADVANCED. A waiver never satisfies documents_approved.

    kyc_approved · documents_approved · shop_approved · no_active_punishment

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/account/verification" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "status": "approved",
  "documents_status": "waived",
  "kyc_status": "approved",
  "documents_waived": true,
  "bank_confirmed": true,
  "can_sell": true,
  "tier": "basic",
  "tier_missing": [
    "documents_approved"
  ]
}

Attention

3 operations

The seller-keyed "errors, warnings, attention required" channel (seller_attention_items).

get/attentionBasic

Errors, warnings, attention required

BasicScope: attention:readCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

Sorted action_required desc, severity desc, deadline_at nulls last, opened_at desc. Array filters repeat the key (kind=stock_out&kind=stock_low).

Parameters (12)
Response 200 · AttentionPage
  • itemsAttentionItem[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

  • countsAttentionCountsrequired
Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/attention" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01997b30-0a1b-7c2d-9e3f-4a5b6c7d8e9f",
      "kind": "order_accept_due",
      "severity": "high",
      "action_required": true,
      "resolves_by": "deadline",
      "subject": {
        "type": "order",
        "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b"
      },
      "title": {
        "uz": "Buyurtmani qabul qiling",
        "ru": "Примите заказ",
        "en": "Accept the order"
      },
      "body": {
        "uz": "Qabul muddati 2 soatdan kam qoldi.",
        "ru": "До срока принятия меньше 2 часов.",
        "en": "Less than 2 h left to accept."
      },
      "deadline_at": "2026-09-25T09:12:00+05:00",
      "action_url": "/orders/01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
      "opened_at": "2026-09-25T07:12:05+05:00",
      "updated_at": "2026-09-25T07:12:05+05:00",
      "acked_at": null,
      "acked_via": null,
      "resolved_at": null,
      "resolved_reason": null
    }
  ],
  "next_cursor": null,
  "has_more": false,
  "counts": {
    "open": 3,
    "action_required": 2,
    "by_severity": {
      "low": 1,
      "medium": 0,
      "high": 2,
      "critical": 0
    }
  }
}
get/attention/{id}Basic

One attention item

BasicScope: attention:readCost: key-rate · 1

—

Parameters (4)
Response 200 · AttentionItem
  • idstring (uuid)required
  • kindstringrequired

    documents_pending · documents_rejected · kyc_reopened · bank_pending · agreement_owed · shop_suspended · shop_restricted · shop_blocked · suppressed · penalty_points · product_held · product_rejected · product_violation · listing_issue · order_accept_due · order_ship_overdue · return_decision_due · stock_out · stock_low · health_warning · health_suppressed · live_ban · live_strike · write_held · key_expiring · key_suspended · key_dormant · ip_blocked · webhook_failing · api_anomaly

  • severitystringrequired

    low · medium · high · critical

  • action_requiredbooleanrequired

    True only when the SELLER can clear it.

  • resolves_bystringrequired

    condition · decision · deadline · expiry · ack

  • subjectobject | nullrequired
  • titleLocalizedTextrequired

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

  • bodyLocalizedTextrequired

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

  • deadline_atstring | nullrequired
  • action_urlstring | nullrequired

    Portal deep link.

  • opened_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • acked_atstring | nullrequired
  • acked_viastring | nullrequired

    portal · api · mcp

  • resolved_atstring | nullrequired
  • resolved_reasonstring | nullrequired

    condition_cleared · decided · deadline_passed · expired · acked

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/attention/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "01997b30-0a1b-7c2d-9e3f-4a5b6c7d8e9f",
  "kind": "order_accept_due",
  "severity": "high",
  "action_required": true,
  "resolves_by": "deadline",
  "subject": {
    "type": "order",
    "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b"
  },
  "title": {
    "uz": "Buyurtmani qabul qiling",
    "ru": "Примите заказ",
    "en": "Accept the order"
  },
  "body": {
    "uz": "Qabul muddati 2 soatdan kam qoldi.",
    "ru": "До срока принятия меньше 2 часов.",
    "en": "Less than 2 h left to accept."
  },
  "deadline_at": "2026-09-25T09:12:00+05:00",
  "action_url": "/orders/01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "opened_at": "2026-09-25T07:12:05+05:00",
  "updated_at": "2026-09-25T07:12:05+05:00",
  "acked_at": null,
  "acked_via": null,
  "resolved_at": null,
  "resolved_reason": null
}
post/attention/{id}/ackBasic

Acknowledge

BasicScope: attention:readCost: write-rate · 1

Idempotent by nature (no Idempotency-Key). Ack never clears a condition; only ip_blocked and api_anomaly resolve on ack (resolved_reason=acked). Counts against the writes/min bucket; not gated by writes_enabled.

Parameters (4)
Response 200 · AttentionItem
  • idstring (uuid)required
  • kindstringrequired

    documents_pending · documents_rejected · kyc_reopened · bank_pending · agreement_owed · shop_suspended · shop_restricted · shop_blocked · suppressed · penalty_points · product_held · product_rejected · product_violation · listing_issue · order_accept_due · order_ship_overdue · return_decision_due · stock_out · stock_low · health_warning · health_suppressed · live_ban · live_strike · write_held · key_expiring · key_suspended · key_dormant · ip_blocked · webhook_failing · api_anomaly

  • severitystringrequired

    low · medium · high · critical

  • action_requiredbooleanrequired

    True only when the SELLER can clear it.

  • resolves_bystringrequired

    condition · decision · deadline · expiry · ack

  • subjectobject | nullrequired
  • titleLocalizedTextrequired

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

  • bodyLocalizedTextrequired

    jsonb {uz,ru,en}; readers fall back uz → ru → en.

  • deadline_atstring | nullrequired
  • action_urlstring | nullrequired

    Portal deep link.

  • opened_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • acked_atstring | nullrequired
  • acked_viastring | nullrequired

    portal · api · mcp

  • resolved_atstring | nullrequired
  • resolved_reasonstring | nullrequired

    condition_cleared · decided · deadline_passed · expired · acked

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/attention/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/ack" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "01997b30-0a1b-7c2d-9e3f-4a5b6c7d8e9f",
  "kind": "order_accept_due",
  "severity": "high",
  "action_required": true,
  "resolves_by": "deadline",
  "subject": {
    "type": "order",
    "id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b"
  },
  "title": {
    "uz": "Buyurtmani qabul qiling",
    "ru": "Примите заказ",
    "en": "Accept the order"
  },
  "body": {
    "uz": "Qabul muddati 2 soatdan kam qoldi.",
    "ru": "До срока принятия меньше 2 часов.",
    "en": "Less than 2 h left to accept."
  },
  "deadline_at": "2026-09-25T09:12:00+05:00",
  "action_url": "/orders/01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
  "opened_at": "2026-09-25T07:12:05+05:00",
  "updated_at": "2026-09-25T07:12:05+05:00",
  "acked_at": "2026-09-24T14:05:12+05:00",
  "acked_via": "api",
  "resolved_at": null,
  "resolved_reason": null
}

Events

1 operation

/events — the primary, pollable change feed.

get/eventsBasic

The change feed (primary channel)

BasicScope: events:readCost: key-rate · 1

Poll ?cursor=<last event id>; omit it for the oldest retained event or pass since. Events are readable 30 days from occurred_at. A cursor older than the window ⇒ 400 invalid_body with details[{field:"cursor",code:"cursor_expired"}] and meta.oldest_event_id: full-reconcile via updated_since, then restart there. Ordered by id (near, not exact, occurred_at order). limit ≤ 200.

Parameters (8)
  • cursorquery · string (uuid)

    Last event id seen.

  • sincequery · string

    ISO 8601 or epoch-ms; ignored when cursor is set.

  • typesquery · string[]

    Repeatable; exact names or prefixes (order.*).

  • limitquery · integer

    Default 100, max 200.

  • If-None-Matchheader · string
  • Accept-Languageheader · string
  • Dona-Sellerheader · string (uuid)
  • X-Dona-Integrationheader · string
Response 200 · EventPage
  • itemsEvent[]required
  • next_cursorstring | nullrequired

    The id of the LAST event returned (or your own cursor echoed when items is empty). Store it after your batch commits; never null once you have seen an event.

  • has_morebooleanrequired

    True ⇒ call again immediately.

  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/events" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01997b2e-7f00-7123-8456-789abcdef012",
      "type": "order.paid",
      "occurred_at": "2026-09-24T09:12:31+05:00",
      "seller_id": "0198f3a2-6c1d-7b40-8e55-2a9c3d4e5f60",
      "api_version": "v1",
      "data": {
        "order_id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
        "status": "paid",
        "payment_status": "paid",
        "payment_method": "card",
        "scheme": "own_fleet",
        "total_uzs": 259000,
        "accept_deadline": "2026-09-25T09:12:00+05:00",
        "ship_by_deadline": null,
        "updated_at": "2026-09-24T09:12:31+05:00"
      },
      "links": {
        "self": "/seller-api/v1/orders/01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b"
      }
    }
  ],
  "next_cursor": "01997b2e-7f00-7123-8456-789abcdef012",
  "has_more": false
}

Webhooks

9 operations

Signed push of the same events (Standard Webhooks).

get/webhooksBasic

List endpoints (≤ 5)

BasicScope: webhooks:manageCost: key-rate · 1

Never a secret.

Parameters (3)
Response 200 · WebhookPage
  • itemsWebhook[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/webhooks" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
      "url": "https://erp.example.uz/dona/webhooks",
      "event_types": [
        "order.*",
        "attention.opened"
      ],
      "status": "active",
      "failing_since": null,
      "disabled_reason": null,
      "consecutive_failures": 0,
      "last_delivered_at": "2026-09-24T09:12:33+05:00",
      "success_rate_24h": 1,
      "secret_rotating_until": null,
      "created_at": "2026-09-22T10:00:00+05:00",
      "updated_at": "2026-09-24T09:12:33+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
post/webhooksBasic

Register an endpoint (secret shown once)

BasicScope: webhooks:manageCost: write-rate · 1

Dona sends a signed ping immediately; your endpoint must answer 2xx within 1 s or the create is 422 webhook_url_invalid. ≤ 5 endpoints per shop: a 6th is 422 webhook_url_invalid with details[{field:"url",code:"endpoint_limit"}]. Kill switch: webhooks_enabled.

Parameters (4)
Request body · WebhookCreate
  • urlstring (uri)required

    https:// with a CA-signed certificate; private, loopback, link-local and metadata addresses are refused.

  • event_typesstring[]

    Exact event names or prefixes (order.*); ["*"] (or omitted on create) = every event.

Response 201 · WebhookWithSecret

Response type: application/json

Responses
  • 201Created
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/webhooks" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.uz/dona-webhook"}'
Example response
{
  "id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
  "url": "https://erp.example.uz/dona/webhooks",
  "event_types": [
    "order.*",
    "attention.opened"
  ],
  "status": "active",
  "failing_since": null,
  "disabled_reason": null,
  "consecutive_failures": 0,
  "last_delivered_at": "2026-09-24T09:12:33+05:00",
  "success_rate_24h": 1,
  "secret_rotating_until": null,
  "created_at": "2026-09-22T10:00:00+05:00",
  "updated_at": "2026-09-24T09:12:33+05:00",
  "secret": "whsec_MfKQ9r2ysWbG2xZt8k1r5sFh3Dq0Vv7L"
}
get/webhooks/{id}Basic

Get an endpoint

BasicScope: webhooks:manageCost: key-rate · 1

—

Parameters (4)
Response 200 · Webhook
  • idstring (uuid)required
  • urlstring (uri)required
  • event_typesstring[]required

    Exact event names or prefixes (order.*); ["*"] (or omitted on create) = every event.

  • statusstringrequired

    active · paused · disabled

  • failing_sincestring | nullrequired
  • disabled_reasonstring | nullrequired

    ladder_exhausted · success_rate · owner · staff · ssrf_recheck

  • consecutive_failuresintegerrequired
  • last_delivered_atstring | nullrequired
  • success_rate_24hnumber | nullrequired
  • secret_rotating_untilstring | nullrequired

    While set, deliveries carry two signatures.

  • created_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/webhooks/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
  "url": "https://erp.example.uz/dona/webhooks",
  "event_types": [
    "order.*",
    "attention.opened"
  ],
  "status": "active",
  "failing_since": null,
  "disabled_reason": null,
  "consecutive_failures": 0,
  "last_delivered_at": "2026-09-24T09:12:33+05:00",
  "success_rate_24h": 1,
  "secret_rotating_until": null,
  "created_at": "2026-09-22T10:00:00+05:00",
  "updated_at": "2026-09-24T09:12:33+05:00"
}
patch/webhooks/{id}Basic

Change url / event types / pause

BasicScope: webhooks:manageCost: write-rate · 1

A new url is re-verified with a ping (same 1 s rule).

Parameters (5)
Request body · WebhookUpdate
  • urlstring (uri)
  • event_typesstring[]

    Exact event names or prefixes (order.*); ["*"] (or omitted on create) = every event.

  • statusstring

    paused = seller-side pause (deliveries accrue 30 d); active resumes a paused endpoint. A disabled endpoint is re-enabled only by a successful /ping.

    active · paused

Response 200 · Webhook
  • idstring (uuid)required
  • urlstring (uri)required
  • event_typesstring[]required

    Exact event names or prefixes (order.*); ["*"] (or omitted on create) = every event.

  • statusstringrequired

    active · paused · disabled

  • failing_sincestring | nullrequired
  • disabled_reasonstring | nullrequired

    ladder_exhausted · success_rate · owner · staff · ssrf_recheck

  • consecutive_failuresintegerrequired
  • last_delivered_atstring | nullrequired
  • success_rate_24hnumber | nullrequired
  • secret_rotating_untilstring | nullrequired

    While set, deliveries carry two signatures.

  • created_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X PATCH "https://api.dona.im/seller-api/v1/webhooks/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.uz/dona-webhook","event_types":["string"],"status":"active"}'
Example response
{
  "id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
  "url": "https://erp.example.uz/dona/webhooks",
  "event_types": [
    "order.*",
    "attention.opened"
  ],
  "status": "paused",
  "failing_since": null,
  "disabled_reason": null,
  "consecutive_failures": 0,
  "last_delivered_at": "2026-09-24T09:12:33+05:00",
  "success_rate_24h": 1,
  "secret_rotating_until": null,
  "created_at": "2026-09-22T10:00:00+05:00",
  "updated_at": "2026-09-24T09:12:33+05:00"
}
delete/webhooks/{id}Basic

Remove an endpoint

BasicScope: webhooks:manageCost: write-rate · 1

Soft delete (deleted_at); delivery history is kept its 30 days. Repeating it is 404 not_found.

Parameters (4)
Response 204
Responses
  • 204Deleted
Request
curl -sS -X DELETE "https://api.dona.im/seller-api/v1/webhooks/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
post/webhooks/{id}/pingBasic

Re-run the verification ping

BasicScope: webhooks:manageCost: write-rate · 1

≤ 10/min per endpoint. A 2xx within 1 s re-enables a disabled or auto-paused endpoint and closes its breaker. No Idempotency-Key.

Parameters (4)
Response 200 · WebhookPing
  • okbooleanrequired
  • response_statusinteger | nullrequired
  • response_msinteger | nullrequired
  • failurestring | nullrequired

    timeout · tls · dns · status

  • webhookWebhookrequired
Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/webhooks/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/ping" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "ok": true,
  "response_status": 200,
  "response_ms": 184,
  "failure": null,
  "webhook": {
    "id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
    "url": "https://erp.example.uz/dona/webhooks",
    "event_types": [
      "order.*",
      "attention.opened"
    ],
    "status": "active",
    "failing_since": null,
    "disabled_reason": null,
    "consecutive_failures": 0,
    "last_delivered_at": "2026-09-24T09:12:33+05:00",
    "success_rate_24h": 1,
    "secret_rotating_until": null,
    "created_at": "2026-09-22T10:00:00+05:00",
    "updated_at": "2026-09-24T09:12:33+05:00"
  }
}
post/webhooks/{id}/rotate-secretBasic

Rotate the signing secret (24 h dual signing)

BasicScope: webhooks:manageCost: write-rate · 1

New whsec_ shown once; for 24 h both secrets sign (webhook-signature: v1,<new> v1,<old>).

Parameters (5)
Response 200 · WebhookWithSecret

Response type: application/json

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/webhooks/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/rotate-secret" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
Example response
{
  "id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
  "url": "https://erp.example.uz/dona/webhooks",
  "event_types": [
    "order.*",
    "attention.opened"
  ],
  "status": "active",
  "failing_since": null,
  "disabled_reason": null,
  "consecutive_failures": 0,
  "last_delivered_at": "2026-09-24T09:12:33+05:00",
  "success_rate_24h": 1,
  "secret_rotating_until": "2026-09-25T14:05:12+05:00",
  "created_at": "2026-09-22T10:00:00+05:00",
  "updated_at": "2026-09-24T09:12:33+05:00",
  "secret": "whsec_2b9Lr8QpXw4Tn6Yc1Hs0Kd5Fa3Gm7Ze9"
}
get/webhooks/{id}/deliveriesBasic

Delivery log (30 d)

BasicScope: webhooks:manageCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

Newest first. status=dead is the dead-letter view.

Parameters (8)
  • idpath · string (uuid)required

    Resource id (UUIDv7). A foreign or missing id is always 404 not_found.

  • statusquery · string

    Exact.

  • event_idquery · string (uuid)

    Exact.

  • cursorquery · string
  • limitquery · integer
  • Accept-Languageheader · string
  • Dona-Sellerheader · string (uuid)
  • X-Dona-Integrationheader · string
Response 200 · DeliveryPage
  • itemsDelivery[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/webhooks/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/deliveries" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01997b33-3d4e-7f5a-8b6c-7d8e9f0a1b2c",
      "webhook_id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
      "event_id": "01997b2e-7f00-7123-8456-789abcdef012",
      "event_type": "order.paid",
      "attempt": 3,
      "status": "failed",
      "next_attempt_at": "2026-09-24T10:12:36+05:00",
      "delivered_at": null,
      "response_status": 502,
      "response_ms": 1840,
      "error": "status_502",
      "occurred_at": "2026-09-24T09:42:31+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
post/webhooks/{id}/deliveries/{delivery_id}/redeliverBasic

Redeliver one delivery

BasicScope: webhooks:manageCost: write-rate · 1

Fresh timestamp + signature, same webhook-id; a new attempt chain. ≤ 100/hour/endpoint. An event older than 30 d ⇒ 404 not_found.

Parameters (6)
Response 200 · Delivery
  • idstring (uuid)required
  • webhook_idstring (uuid)required
  • event_idstring (uuid)required
  • event_typestringrequired

    The glossary event catalogue. New types are additive.

    order.created · order.paid · order.accepted · order.declined · order.ready · order.shipped · order.delivered · order.cancelled · order.line_cancelled · order.accept_due_soon · order.ship_overdue · return.requested · return.status_changed · product.published · product.held · product.rejected · product.demoted · product.violation · product.deleted · stock.low · stock.out · write.held · write.approved · write.rejected · write.expired · account.status_changed · account.documents_decided · account.kyc_reopened · account.suppressed · account.health_changed · account.agreement_owed · live.access_changed · live.strike · attention.opened · attention.resolved · key.expiring · key.suspended · key.unsuspended · webhook.failing · webhook.disabled · ping

  • attemptintegerrequired
  • statusstringrequired

    queued · delivered · failed · dead

  • next_attempt_atstring | nullrequired
  • delivered_atstring | nullrequired
  • response_statusinteger | nullrequired
  • response_msinteger | nullrequired
  • errorstring | nullrequired
  • occurred_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/webhooks/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/deliveries/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/redeliver" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
Example response
{
  "id": "01997b2f-0102-7345-9678-9abcdef01234",
  "webhook_id": "01997b32-2c3d-7e4f-9a5b-6c7d8e9f0a1b",
  "event_id": "01997b2e-7f00-7123-8456-789abcdef012",
  "event_type": "order.paid",
  "attempt": 1,
  "status": "queued",
  "next_attempt_at": "2026-09-24T14:05:12+05:00",
  "delivered_at": null,
  "response_status": null,
  "response_ms": null,
  "error": null,
  "occurred_at": "2026-09-24T09:42:31+05:00"
}

Taxonomy

4 operations

Platform-owned categories, brands, IKPU (T4).

get/categoriesBasic

Category tree (platform-owned)

BasicScope: anyCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

Walk the 5-level tree by parent_id (omit it for the roots).

Parameters (6)
Response 200 · CategoryPage
  • itemsCategory[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/categories" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01972a10-1111-7aaa-8bbb-0123456789ab",
      "parent_id": "01972a10-0000-7aaa-8bbb-0123456789aa",
      "name": {
        "uz": "Futbolkalar",
        "ru": "Футболки",
        "en": "T-shirts"
      },
      "level": 3,
      "is_leaf": true
    }
  ],
  "next_cursor": null,
  "has_more": false
}
get/categories/{id}/requirementsBasic

What a product in this leaf needs

BasicScope: anyCost: key-rate · 1

Per-category requirements + listing policy.

Parameters (4)
Response 200 · CategoryRequirements
  • category_idstring (uuid)required
  • statusstringrequired
  • listing_policystringrequired

    open default; approval_required, licensed, restricted, prohibited.

  • is_leafbooleanrequired
  • can_listbooleanrequired

    status=active AND is_leaf AND listing_policy <> prohibited.

  • min_imagesintegerrequired
  • requires_dimensionsbooleanrequired
  • requires_brandbooleanrequired
  • requires_size_chartbooleanrequired
  • commission_pctnumber | nullrequired
  • attributesCategoryAttribute[]required
Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/categories/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/requirements" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "category_id": "01972a10-1111-7aaa-8bbb-0123456789ab",
  "status": "active",
  "listing_policy": "open",
  "is_leaf": true,
  "can_list": true,
  "min_images": 4,
  "requires_dimensions": false,
  "requires_brand": false,
  "requires_size_chart": true,
  "commission_pct": 12,
  "attributes": [
    {
      "key": "size",
      "label": {
        "uz": "Oʻlcham",
        "ru": "Размер",
        "en": "Size"
      },
      "value_type": "enum",
      "requirement": "required",
      "is_filterable": true,
      "is_variant_axis": true,
      "options": [
        {
          "code": "m",
          "label": {
            "uz": "M",
            "ru": "M",
            "en": "M"
          }
        }
      ]
    }
  ]
}
get/brandsBasic

Brand search

BasicScope: anyCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

Prefix search.

Parameters (6)
Response 200 · BrandPage
  • itemsBrand[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/brands" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01972b20-2222-7ccc-9ddd-0123456789cd",
      "name": "Bahor",
      "protection": "open"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
get/ikpuBasic

IKPU (tax classifier) search

BasicScope: anyCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

From the local tasnif copy.

Parameters (6)
Response 200 · IkpuPage
  • itemsIkpu[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/ikpu?q=<q>" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "code": "06109100001000000",
      "name": "Paxtali trikotaj futbolkalar",
      "active": true,
      "packages": [
        {
          "code": "1520227",
          "name": "dona"
        }
      ]
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Jobs

4 operations

Async exports and product batches.

post/exports/productsBasic

Export products (async)

BasicScope: catalog:readCost: key-rate · 10

CSV or JSONL. ≤ 2 running jobs per key, ≤ 20/day per shop. Not gated by writes_enabled.

Parameters (4)
Request body · ExportRequest
  • formatstring

    csv · jsonl

  • filtersobject

    Same meaning as the list filters; from/to only on orders (≤ 90 d).

    object
    • statusstring
    • updated_sincestring
    • fromstring (date-time)
    • tostring (date-time)
Response 202 · JobAccepted
  • job_idstring (uuid)required
  • status"pending"required
  • linksobjectrequired
Responses
  • 202Accepted
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/exports/products" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"format":"jsonl","filters":{"status":"string","updated_since":"string","from":"2026-09-24T09:00:00+05:00"}}'
Example response
{
  "job_id": "01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d",
  "status": "pending",
  "links": {
    "self": "/seller-api/v1/jobs/01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d"
  }
}
post/exports/ordersBasic

Export orders (async, no PII)

BasicScope: orders:readCost: key-rate · 10

The orders:read projection only — an export never carries recipient.

Parameters (4)
Request body · ExportRequest
  • formatstring

    csv · jsonl

  • filtersobject

    Same meaning as the list filters; from/to only on orders (≤ 90 d).

    object
    • statusstring
    • updated_sincestring
    • fromstring (date-time)
    • tostring (date-time)
Response 202 · JobAccepted
  • job_idstring (uuid)required
  • status"pending"required
  • linksobjectrequired
Responses
  • 202Accepted
Request
curl -sS -X POST "https://api.dona.im/seller-api/v1/exports/orders" \
  -H "Authorization: Bearer $DONA_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"format":"jsonl","filters":{"status":"string","updated_since":"string","from":"2026-09-24T09:00:00+05:00"}}'
Example response
{
  "job_id": "01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d",
  "status": "pending",
  "links": {
    "self": "/seller-api/v1/jobs/01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d"
  }
}
get/jobs/{id}Basic

Poll a job

BasicScope: anyCost: key-rate · 1

Readable by any key of the shop that holds the scope the job's kind needs (catalog:read for export_products, orders:read for export_orders, catalog:write for products_batch); otherwise 404 not_found. Poll ≥ 5 s (Retry-After is set while pending/running). After expires_at (7 d) ⇒ 404 not_found.

Parameters (4)
Response 200 · Job
  • idstring (uuid)required
  • kindstringrequired

    export_products · export_orders · products_batch

  • statusstringrequired

    pending · running · ready · failed

  • progressobjectrequired
  • file_urlstring | nullrequired

    GET /jobs/{id}/download?token=… — the token is valid 15 min from this response and only for a key of this shop (never a public or pre-signed object URL); re-read the job for a fresh one.

  • file_expires_atstring | nullrequired
  • resultsLineResult[] | nullrequired

    products_batch only: the per-line shape.

  • errorstring | nullrequired

    Our code only.

  • created_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • finished_atstring | nullrequired
  • expires_atstring (date-time)required

    Row + file swept after this (7 d); then 404 not_found.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/jobs/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "id": "01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d",
  "kind": "export_orders",
  "status": "ready",
  "progress": {
    "done": 1240,
    "total": 1240
  },
  "file_url": "https://api.dona.im/seller-api/v1/jobs/01997b34-4e5f-7a6b-9c7d-8e9f0a1b2c3d/download?token=v1.1790327512.3q2-7wAAAAB3j5E1c0x9fQ",
  "file_expires_at": "2026-09-24T14:17:10+05:00",
  "results": null,
  "error": null,
  "created_at": "2026-09-24T14:01:00+05:00",
  "finished_at": "2026-09-24T14:02:10+05:00",
  "expires_at": "2026-10-01T14:01:00+05:00"
}
get/jobs/{id}/downloadBasic

Download an export's file

BasicScope: anyCost: key-rate · 1

What a ready export's file_url points at. Needs BOTH a key of the job's shop that holds the scope the job's kind needs (any key of the shop — the file is the shop's; a key of another shop is 404 not_found whatever token it carries) AND the job's own token from file_url, valid 15 min (401 download_token_invalid when absent, forged or another job's; 401 download_token_expired past its time — re-read GET /jobs/{id} for a fresh one). The file is never on a public origin — it is in Dona's private exports bucket (302 to a presigned GET valid 15 min, issued only after both checks above — D24, 2026-09-26) or, where the bucket is not configured, in the database (200 with the bytes). Follow the redirect WITHOUT the Authorization header (the signed URL carries its own authority; most HTTP clients drop the header on a cross-host redirect). Swept with its job after expires_at (7 d) ⇒ 404 not_found. Not gated by writes_enabled.

Parameters (5)
  • idpath · string (uuid)required

    Resource id (UUIDv7). A foreign or missing id is always 404 not_found.

  • tokenquery · stringrequired

    The download token file_url carries (v1.<unix expiry>.<mac>), bound to this job and this shop.

  • Accept-Languageheader · string
  • Dona-Sellerheader · string (uuid)
  • X-Dona-Integrationheader · string
Response 200

Response type: application/x-ndjson, text/csv

Responses
  • 200The file, as an attachment (<code>Content-Disposition</code>, <code>X-Content-Type-Options: nosniff</code>).
  • 302The file is in Dona's private exports bucket — <code>Location</code> is a presigned GET of exactly this job's object, valid 15 minutes, that downloads it as an attachment (<code>dona-&lt;products|orders&gt;-&lt;job id&gt;.&lt;jsonl|csv&gt;</code>). Issued only after the key and token checks; never cacheable.
Request
curl -sS "https://api.dona.im/seller-api/v1/jobs/0199612e-8a0b-7c4d-b1e2-3f4a5b6c7d8e/download?token=<token>" \
  -H "Authorization: Bearer $DONA_KEY"

Finance

2 operations

Balance and settlement lines (ADVANCED).

get/finance/balanceAdvanced

Balance

AdvancedScope: finance:readCost: key-rate · 1

The wallet's answer for the key's shop, asked as the shop's CURRENT owner (the portal's own finance bridge), projected field by field. Never error-copied. No requisites, PAN or statement URLs. The wallet unreachable, erroring or not knowing the shop ⇒ 503 wallet_unavailable (Retry-After: 30), never its body (C57). Served by the marketplace process only: a SERVER_ROLE=seller-api server answers 503 role_unavailable before any read (D3).

Parameters (3)
Response 200 · Balance
  • available_uzsinteger (int64)required

    Integer soʻm (no decimals).

  • held_uzsinteger (int64)required

    Integer soʻm — earned, still inside the admin hold (not yet withdrawable).

  • expected_uzsinteger (int64)required

    Integer soʻm — orders in flight, not yet earned.

  • currency"UZS"required
  • as_ofstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/finance/balance" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "available_uzs": 12450000,
  "held_uzs": 3100000,
  "expected_uzs": 5400000,
  "currency": "UZS",
  "as_of": "2026-09-24T14:05:12+05:00"
}
get/finance/settlementsAdvanced

Settlement lines

AdvancedScope: finance:readCost: key-rate · 1 + ceil(limit/50) when limit > 50, else 1

The wallet statement's lines for the shop, newest first (asked as the shop's current owner). memo is the line's machine kind from the wallet's CLOSED vocabulary (sale_income, escrow_hold, escrow_release, refund, return, adjustment, withdrawal, withdrawal_failed, fee, commission, hold_placed, hold_captured, hold_released, cod_collected, cod_remitted, transfer) or other — never the wallet's free-text title (C57). A line whose id or txn_id is not a UUID, or a next_cursor that is not an opaque ≤ 512-character URL-safe token, refuses the whole page. 503 wallet_unavailable / role_unavailable as /finance/balance.

Parameters (8)
Response 200 · SettlementPage
  • itemsSettlement[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/finance/settlements" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "01997d00-1234-7abc-8def-0123456789a0",
      "txn_id": "01997e00-5678-7abc-9def-0123456789b1",
      "direction": "credit",
      "amount_uzs": 233100,
      "signed_amount_uzs": 233100,
      "order_id": "01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b",
      "memo": "sale_income",
      "created_at": "2026-09-24T14:05:12+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Keys

1 operation

Read-only list of the shop's keys.

get/keysBasic

The shop's keys (read-only; management is portal-only)

BasicScope: anyCost: key-rate · 1

Metadata only — never a secret, never last_used_ip. Mint/rotate/revoke live in the seller portal.

Parameters (3)
Response 200 · KeyPage
  • itemsKey[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/keys" \
  -H "Authorization: Bearer $DONA_KEY"
Example response
{
  "items": [
    {
      "id": "0199760c-2f4b-7a3e-9d21-5c8e4b1f0a37",
      "name": "billz-prod",
      "kind": "sk",
      "env": "live",
      "prefix": "dona_sk_live_3fK",
      "last4": "q9Zx",
      "scopes": [
        "catalog:read",
        "catalog:stock",
        "orders:read"
      ],
      "state": "active",
      "expiring_soon": false,
      "expires_at": "2027-09-24T00:00:00+05:00",
      "last_used_at": "2026-09-24T14:00:03+05:00",
      "ip_allow": [],
      "rotated_from": null,
      "overlap_until": null,
      "suspended_until": null,
      "created_at": "2026-09-24T11:02:00+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Meta

5 operations

Public: ping, status, changelog, this spec, llms.txt.

get/pingPublic

Liveness (key optional)

PublicScope: noneCost: edge · 0

Public. Without Authorization it answers authenticated:false. With a key the key is fully verified — a bad key is 401, never a silent false.

Parameters (2)
Response 200 · Ping
  • ok"pong"required
  • timestring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

  • authenticatedbooleanrequired

    True only when a valid key was presented.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/ping"
Example response
{
  "ok": "pong",
  "time": "2026-09-24T14:05:12+05:00",
  "authenticated": true
}
get/statusPublic

Service status

PublicScope: noneCost: edge · 0

Public; edge-cacheable 60 s; linked from every 503's doc_url.

Parameters (2)
Response 200 · Status
  • statusstringrequired

    operational · degraded · outage

  • componentsobjectrequired
  • incidentsobject[]required
  • updated_atstring (date-time)required

    ISO 8601 with offset (Tashkent +05:00 on output).

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/status"
Example response
{
  "status": "operational",
  "components": {
    "api": "operational",
    "mcp": "disabled",
    "webhooks": "operational",
    "events": "operational"
  },
  "incidents": [],
  "updated_at": "2026-09-24T14:05:12+05:00"
}
get/changelogPublic

Changelog (JSON, or RSS with Accept)

PublicScope: noneCost: edge · 0

Public. Accept: application/rss+xml returns RSS. Backed by seller_api_changelog (migration 0487, S4): published entries only, newest first; the first row is the v1 seed. Written only from Dona Control (admin-contract §7). The seller portal reads the same body at GET /api/v1/sellers/me/api-docs/changelog (portal-contract §6a) — this tree sends no CORS grant. Cached public, max-age=300 with Vary: Accept-Language, Accept on BOTH formats, and a weak ETag over the exact bytes: If-None-Match with it answers 304 and no body.

Parameters (4)
Response 200 · ChangelogEntryPage
  • itemsChangelogEntry[]required
  • next_cursorstring | nullrequired

    Pass back as ?cursor=. null when has_more is false.

  • has_morebooleanrequired
  • metaPageMeta

    Endpoint-specific facts; clients must tolerate unknown keys.

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/changelog"
Example response
{
  "items": [
    {
      "id": "01997e00-0000-7000-8000-000000000001",
      "version": "v1",
      "kind": "added",
      "title": {
        "uz": "Dona API v1",
        "ru": "Dona API v1",
        "en": "Dona API v1"
      },
      "body": {
        "uz": "Birinchi versiya: kalitlar, mahsulotlar, qoldiq va narxlar, buyurtmalar, qaytarishlar, hodisalar, eʼtibor talab qiladigan holatlar, webhooklar va MCP.",
        "ru": "Первая версия: ключи, товары, остатки и цены, заказы, возвраты, события, требующие внимания состояния, вебхуки и MCP.",
        "en": "The first version: keys, products, stock and prices, orders, returns, events, attention items, webhooks and MCP."
      },
      "affects_scopes": [],
      "sunset_at": null,
      "published_at": "2026-09-24T00:00:00+05:00"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
get/openapi.jsonPublic

This document

PublicScope: noneCost: edge · 0

OpenAPI 3.1, public, Cache-Control: public, max-age=300.

Parameters (2)
Response 200 · object

Response type: application/json

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/openapi.json"
get/llms.txtPublic

llms.txt index for AI agents

PublicScope: noneCost: edge · 0

Plain-text index of the docs + spec links (llms-full.txt beside it).

Parameters (2)
Response 200

Response type: text/plain

Responses
  • 200OK
Request
curl -sS "https://api.dona.im/seller-api/v1/llms.txt"

0308

Limits

Limits are counted per proven key and shop, never per IP. These are the defaults; the numbers that apply to your key are in GET /me → limits.

Limits
LimitBasic skAdvanced skAgent ak
Requests / min (key)300 · burst 60600 · burst 12060 · burst 20
Requests / day (key)100 000250 00020 000
Writes / min6012010
Writes / day20 00050 0002 000
Requests in flight484
Requests / min (shop, all keys)9001 800—
  • One product’s stock or price — at most 1 write every 10 seconds (object_cooldown).
  • Bulk writes ≤ 1 000 lines; body ≤ 256 KiB (bulk 4 MiB). 100–300 lines recommended.
  • Page limit ≤ 100 (≤ 200 on /events). If-None-Match → 304 at half cost.
  • Our own 429/503 and 5xx cost nothing and never suspend a key.

Dona-Rate-Limited-Reason values:

  • key-rate
  • write-rate
  • seller-rate
  • concurrency
  • object-cooldown
  • app-rate
  • global
  • suspended

0408

Error codes

59 codes

Every error is one envelope: error is the stable code, message is localised by Accept-Language.

Branch on the code, never on message.

Error envelope
{
  "error": "insufficient_scope",
  "message": "Bu amal uchun ruxsat yetarli emas.",
  "request_id": "req_01997b2e-9a8b-7c6d-8e5f-4a3b2c1d0e9f",
  "details": [],
  "doc_url": "https://dona.uz/uz/developers/errors#insufficient_scope",
  "required_scope": "orders:write"
}

202Accepted

400Bad Request

  • invalid_body

    malformed JSON, a missing Idempotency-Key, validation (details[] carries field codes such as ikpu_required, cursor_expired), a dry_run / Dona-Dry-Run that is not the literal true/false (details[{field, code:"invalid"}], C51), a vendor-app install key without Dona-Seller (details[{field:"Dona-Seller", code:"required"}], S6) or with a repeated / non-canonical one (code:"invalid"), or an op-specific 400 (invalid_decline_reason)

  • invalid_decline_reason

    see POST /orders/{id}/decline

  • request_state_invalid

    the MCP door — see MCP

401Unauthorized

Also a seller/app/staff JWT on this tree

403Forbidden

  • insufficient_scope

    +required_scope; also a scope in disabled_scopes

  • tier_required

    +scope; derived on every request; also a key minted for a third-party orders:pii recipient — details[pii_recipient: pii_third_party_pending_counsel], D7, C59

  • key_suspended

    +suspended_until, Retry-After, Dona-Rate-Limited-Reason: suspended

  • api_blocked
  • ip_not_allowed

    judged on the trusted client IP only

  • seller_not_approved

    shop closed/blocked/suspended/restricted

  • not_a_seller

    never on this tree; the portal's owner doors

  • app_frozen

    S6: every key of a frozen vendor app, from the next request

  • agreement_required

    +accept_url: the shop owner has not accepted the CURRENT Annex 2 «API and automated access» — the version in force or, while none is, the earliest announced; accepted once in Settings › API, then every door admits the key again. Checked after the sanctions, before the scopes

  • request_state_foreign

    the MCP door — see MCP

404Not Found

  • not_found

    every missing OR foreign id (never 403: no existence oracle)

409Conflict

Also a replay of a rejected held write

410Gone

413Content Too Large

422Unprocessable Content

429Too Many Requests

Also Always a JSON body + Retry-After from the origin (an edge 429 from Cloudflare has NO body — treat it as Retry-After: 60). Costs 0 and never counts toward the breaker. rate_limited is also the pre-auth ADDRESS guard, answered before the key is read: an address over 1 200 requests a minute that were not admitted (no key, a public route, a malformed, unknown or refused key — admitted requests never count), or one tarpitted at 60/min for 15 min after ≥ 20 unknown keys in a minute. No key is ever suspended by traffic that did not prove it

Also Cloudflare edge, per-IP 60/min on public routes. No JSON body (the edge eats it) — treat as Retry-After: 60

500Internal Server Error

  • internal_error

    details are in the redacted error copy, never here. Costs 0

503Service Unavailable

  • auth_unavailable

    key lookup failed — fail closed

  • limits_unavailable

    limiter blind on a write/MCP call, or a kill switch is off: enabled, writes_enabled, webhooks_enabled; Retry-After: 30

  • attention_busy

    an acknowledgement that resolves an item could not take the attention writer lock within 2 s — nothing was written; retry after Retry-After: 2

  • wallet_unavailable

    /finance/*: the wallet could not answer for the shop; Retry-After: 30; its body is never relayed or error-copied — S4, C57

  • role_unavailable

    + served_by: "marketplace": a SERVER_ROLE=seller-api server does not serve decline / cancel or /finance/*; answered before any read or write, nothing changed; no Retry-After — D3

  • intent_unavailable

    the MCP door — see MCP

Also Costs 0

0508

Events and webhooks

41 event types

Every change lands in one feed. For systems that can only initiate a request (1C), polling it is the primary channel.

Polling

get/events
GET /events
curl -sS "https://api.dona.im/seller-api/v1/events?cursor=$LAST_EVENT_ID&limit=200" \
  -H "Authorization: Bearer $DONA_KEY"

Webhooks — Standard Webhooks signatures

Delivery rules

One event per POST (≤ 20 KB), at-least-once, unordered. Reply 2xx within 10 s (ack first, process after); 3xx is a failure; 410 Gone disables the endpoint. Retry ladder: immediate · 5 s · 5 m · 30 m · 2 h · 5 h · 10 h · 14 h · 20 h · 24 h (≈ 75 h, ±10 % jitter).

Headers

webhook-id
= the event id. Your idempotency key — dedupe for 24 h.
webhook-timestamp
Unix seconds. Reject if |now − ts| > 300 s. A redelivery carries a FRESH timestamp.
webhook-signature
Standard Webhooks: space-delimited v1,<base64 HMAC-SHA256(base64decode(secret after whsec_), id + "." + ts + "." + rawBody)>; two entries during a 24 h rotation.
Dona-Request-Id
Correlates with the delivery log.

Event types

  • order.createdorder.paidorder.acceptedorder.declinedorder.readyorder.shippedorder.deliveredorder.cancelledorder.line_cancelledorder.accept_due_soonorder.ship_overdue
  • return.requestedreturn.status_changed
  • product.publishedproduct.heldproduct.rejectedproduct.demotedproduct.violationproduct.deleted
  • stock.lowstock.out
  • write.heldwrite.approvedwrite.rejectedwrite.expired
  • account.status_changedaccount.documents_decidedaccount.kyc_reopenedaccount.suppressedaccount.health_changedaccount.agreement_owed
  • live.access_changedlive.strike
  • attention.openedattention.resolved
  • key.expiringkey.suspendedkey.unsuspended
  • webhook.failingwebhook.disabled
  • ping

The signature below is real: it was computed when the site was built from the demo secret and body shown, and the snippet verifies it byte for byte.

POST https://example.uz/dona-webhook
POST https://example.uz/dona-webhook

webhook-id: 01997b2e-7f00-7123-8456-789abcdef012
webhook-timestamp: 1790223151
webhook-signature: v1,CFjQ7u8l+tPV5Wzhawj/Hx9iWYx9sM6mcFJu4wsQxpQ=

# demo secret — NOT a real one
# whsec_ZG9uYS1kZW1vLXNlY3JldC1ub3QtYS1yZWFsLW9uZSE=

{"id":"01997b2e-7f00-7123-8456-789abcdef012","type":"order.created","occurred_at":"2026-09-24T09:12:31+05:00","seller_id":"0198f3a2-6c1d-7b40-8e55-2a9c3d4e5f60","api_version":"v1","data":{"order_id":"01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b","status":"paid","payment_status":"paid","payment_method":"card","scheme":"own_fleet","total_uzs":259000,"accept_deadline":"2026-09-25T09:12:00+05:00","ship_by_deadline":null,"updated_at":"2026-09-24T09:12:31+05:00"},"links":{"self":"/seller-api/v1/orders/01997b2e-41c0-7e6f-8a9b-0c1d2e3f4a5b"}}
Verifying a signature · Node.js
import { createHmac, timingSafeEqual } from 'node:crypto'

// secret = "whsec_…" from POST /webhooks; body = the RAW request body (a string, not parsed JSON)
export function verify(secret, headers, body) {
  const id = headers['webhook-id'], ts = headers['webhook-timestamp']
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false
  const key = Buffer.from(secret.slice('whsec_'.length), 'base64')
  const want = createHmac('sha256', key).update(`${id}.${ts}.${body}`).digest()
  return headers['webhook-signature'].split(' ').some((entry) => {
    const [v, sig] = entry.split(',')
    const got = Buffer.from(sig ?? '', 'base64')
    return v === 'v1' && got.length === want.length && timingSafeEqual(got, want)
  })
}
Verifying a signature · Python
import base64, hashlib, hmac, time

def verify(secret: str, headers: dict, body: bytes) -> bool:
    wid, ts = headers["webhook-id"], headers["webhook-timestamp"]
    if abs(time.time() - int(ts)) > 300:
        return False
    key = base64.b64decode(secret.removeprefix("whsec_"))
    want = hmac.new(key, f"{wid}.{ts}.".encode() + body, hashlib.sha256).digest()
    for entry in headers["webhook-signature"].split(" "):
        v, _, sig = entry.partition(",")
        if v == "v1" and hmac.compare_digest(base64.b64decode(sig), want):
            return True
    return False

0608

MCP — AI agents

Claude Code, Cursor, Codex and other AI agents can work with your shop over MCP. Every call is logged.

With an agent key the agent reads the shop: products, orders, events, attention items, account health and these docs. Write tools — stock, prices, shipping and cancelling an order — appear only for an Advanced shop while writes are on. Nothing is written on the first call: the change is confirmed first, in the agent’s own confirmation form or by the shop owner in Settings › API › Held writes.

Endpoint
https://api.dona.im/seller-api/mcp
Protected resource metadata (RFC 9728)
/.well-known/oauth-protected-resource/seller-api/mcp

The agent key (dona_ak_live_…, scope mcp) is created in Settings › API.

One line for Claude Code
claude mcp add --transport http dona https://api.dona.im/seller-api/mcp --header "Authorization: Bearer ${DONA_AGENT_KEY}"

OAuth 2.1 — claude.ai and ChatGPT connectors

Connectors that cannot hold a pasted key sign in with OAuth 2.1: the shop owner approves the connection in the seller portal (Settings › API › Connected apps) and can disconnect it at any time. Over OAuth the agent can only read.

Authorization server
https://auth.dona.im

0708

SDKs

SDKs for TypeScript, Python, Go, PHP and C# are generated from this same contract.

  • TypeScript
  • Python
  • Go
  • PHP
  • C#
  • Pre-release 0.1.0

The SDKs live in a public GitHub repository. Registry packages (npm, PyPI, NuGet, Packagist) are not published yet — install straight from the repository for now.

  • TypeScript

    Requires Node ≥ 20

    Needs a local clone

    Install
    git clone https://github.com/ozbuz/dona-api.git && cd dona-api/sdks/typescript && npm install
    Install
    npm install ../dona-api/sdks/typescript
  • Python

    Requires ≥ 3.11
    Install
    pip install "dona-api @ git+https://github.com/ozbuz/dona-api.git@main#subdirectory=sdks/python"
  • Go

    Requires ≥ 1.26
    Install
    go get github.com/ozbuz/dona-api/sdks/go@main
  • PHP

    Requires ≥ 8.1, Guzzle 7

    Needs a local clone

    Install
    git clone https://github.com/ozbuz/dona-api.git && cd your-project && composer config repositories.dona-api path ../dona-api/sdks/php && composer require ozbuz/dona-api:*@dev
  • C#

    Requires .NET 10

    Needs a local clone

    Install
    git clone https://github.com/ozbuz/dona-api.git && dotnet add reference path/to/dona-api/sdks/csharp/src/Dona.Api/Dona.Api.csproj

0808

Changelog and status

Every change is published with its date, kind and the scopes it affects.

Need a key?

The shop owner creates a key in the seller portal: Settings › API. The key is shown once.

A question as a buyer or seller? Visit the help centre.

Scan to download DonaDownload Dona

Show the QR code
QR code to download the Dona app

Scan with your phone camera