Skip to content

API Integration Overview ​

This page explains how Bione's HTTP APIs are structured, how authentication works, and what to expect from responses. The API Reference documents every endpoint; this page gives you the landscape so you know where to look and what conventions to follow.

What the API is for ​

Bione is a merchant platform: companies run their shops, customers earn cashback, and integration partners connect to exchange data. Four separate API surfaces serve four audiences, each with its own authentication strategy. You are reading about the Integration surface — the one external partners use — but understanding all four helps you reason about data ownership and access boundaries.

The four surfaces ​

SurfaceURL prefixAuthWho uses it
Customer/v1/customer/*JWT cookies or body tokensMobile app, website, Telegram Mini App
Company/v1/company/*JWT + OrganizationMemberCompany admin web
Platform/v1/platform/*JWT + platformRole + 2FAPlatform admin web
Integration/integration/v1/*API key in api-key headerExternal partner systems

The Integration surface lives at /integration/v1/* — outside the global /v1 prefix — so it can be versioned and deprecated independently from Bione's internal releases.

Authentication ​

Browser surfaces (Customer, Company, Platform) ​

All three use httpOnly cookie sessions (bione_at / bione_rt). Tokens are never in response bodies for browser clients; they are set via Set-Cookie and sent automatically. Refresh happens through POST /<surface>/auth/refresh.

Native mobile (Customer) ​

The Kotlin Multiplatform mobile app is the only client permitted to receive tokens in the response body. It sends X-Client-Type: native on auth requests, stores tokens in platform-secured storage, and sends them as Authorization: Bearer <accessToken> on subsequent calls.

Integration (external partners) ​

Send your API key in the api-key header:

api-key: bik_your_key_here

There is no OAuth flow, no token exchange, and no refresh. The key is the credential until the merchant revokes it. Bione stores only an argon2 hash of the key — a lost key cannot be recovered and must be reissued.

See Authentication for key scopes, rotation, and expiry.

Response envelope ​

Every response — success or error — is wrapped in the same envelope. This is a Bito-compatible shape, designed so that clients which also integrate with the Bito ERP API can share dispatch logic.

Dispatch on code, not on the HTTP status. 0 is success; anything else is an error. The numeric code is stable across releases; the prose message may be reworded.

json
{
  "code": 0,
  "message": "Success",
  "data": {
    "id": "prod_1",
    "name": "Kofe",
    "price": "9900.00"
  },
  "messages": {
    "uz": "Muvaffaqiyatli",
    "ru": "Успешно",
    "en": "Success"
  },
  "status_code": 200,
  "time": "2026-08-25T08:32:00.000Z"
}

Key properties:

  • code — 0 means success. Non-zero is an error; see Error Codes.
  • data — the payload. On error this is null or carries validation context. A controller returning nothing yields data: "OK" instead of null.
  • messages — the same explanation in all three locales Bione ships (uz / ru / en), so you can localise without re-fetching.
  • status_code — the real HTTP status, echoed in the envelope.
  • Every success is HTTP 200, regardless of verb. A POST that creates a resource answers 200, not 201.

Paginated responses ​

Lists nest under data with pagination metadata:

json
{
  "code": 0,
  "message": "Success",
  "data": {
    "items": [...],
    "total": 42,
    "page": 1,
    "pageSize": 20,
    "totalPages": 3
  },
  "messages": { "uz": "...", "ru": "...", "en": "..." },
  "status_code": 200,
  "time": "2026-08-25T08:32:00.000Z"
}

Money on the wire ​

All monetary values are Decimal(14,2) serialised as strings, not numbers. You will see "5000.00", not 5000.

  • Never parse money strings with Number() or parseFloat().
  • Format at display time with Intl.NumberFormat or your locale's equivalent.

This avoids floating-point rounding and keeps precision consistent between your system and Bione's.

Standard REST verbs ​

Bione uses standard HTTP verbs — it is not a POST-only RPC:

VerbWhenExample
GETRead (list, get-by-id)GET /integration/v1/products
POSTCreate or perform an actionPOST /integration/v1/orders
PATCHPartial updatePATCH /integration/v1/orders/:id
DELETESoft-deleteDELETE /integration/v1/products/:id

Use the verb that matches your operation. The backend validates and rejects incorrect usage.

Idempotency ​

Safe retries are built in. Send an Idempotency-Key header on any non-GET request to opt in.

SituationResult
First request with this keyHandler runs; response is stored on 2xx
Retry — same key, same body, original doneStored response replayed verbatim
Retry — same key, different body409 idempotency mismatch
Retry — first request still in flight425 request in progress

Idempotency records live in Redis for 24 hours. A non-2xx response deletes the record, so a failed call may be retried with the same key.

Tip: The body hash is compared after parsing, not raw bytes. Two requests with the same JSON but different key ordering are considered the same body.

Pagination ​

List endpoints accept standard query parameters and return a Paginated<T> shape:

GET /integration/v1/products?page=1&pageSize=20&q=kofe
ParameterDefaultDescription
page11-indexed page number
pageSize20Items per page
q—Free-text search filter

The response data object contains items, total, page, pageSize, and totalPages.

Error handling ​

Errors use the same envelope as successes. The code field carries a numeric error code from Bione's namespaced enum; 0 is always success.

json
{
  "code": 10012,
  "message": "X-Audit-Reason header is required for this action",
  "data": null,
  "messages": {
    "uz": "Audit sababini ko'rsating",
    "ru": "Укажите причину для аудита",
    "en": "X-Audit-Reason header is required for this action"
  },
  "status_code": 400,
  "time": "2026-08-25T08:32:00.000Z",
  "path": "/v1/platform/organizations/abc/suspend"
}
  • code is the stable, machine-readable identifier. Map it in your client rather than matching on message text.
  • messages gives you all three locales in one response, so you can localise without a second call.
  • status_code is the real HTTP status (400, 403, 404, …).

See Error Codes for the full list.

Your first call ​

Assuming you already have an API key from the merchant:

1. Health check ​

This route needs no key. It separates "wrong network path" from "wrong key":

bash
curl https://api.bione.uz/integration/v1/health
json
{
  "code": 0,
  "message": "Success",
  "data": { "status": "ok", "version": "1" },
  "messages": { "uz": "Muvaffaqiyatli", "ru": "Успешно", "en": "Success" },
  "status_code": 200,
  "time": "2026-08-25T08:32:00.000Z"
}

2. List products ​

bash
curl -H "api-key: bik_your_key_here" \
     https://api.bione.uz/integration/v1/products

You will get back the envelope with data.items containing the merchant's product catalogue.

3. Next steps ​

From here you can browse the API Reference for the full list of endpoints, request schemas, and response shapes.

What's NOT in the API ​

The Integration surface is intentionally scoped. Things it does not expose:

  • No webhooks. Bione v1 is pull-only. Poll for changes or use the health and status endpoints to stay current.
  • No OAuth. Authentication is API key only. There is no token exchange, no authorization URL, and no refresh cycle.
  • No order cancellation, refunds, or cashback adjustments. These are merchant-staff operations performed through the Company admin surface.
  • No cross-merchant access. One API key belongs to one merchant. There is no way to query across merchants with a single key.

These constraints are by design. If you need webhook delivery or cross-merchant aggregation, reach out — these are candidates for future versions.

Stability and versioning ​

The Integration surface lives at /integration/v1/*. Within a major version:

  • Fields may be added to responses without notice.
  • Fields will not be removed or renamed within v1.
  • Endpoints will not be removed or renamed within v1.

Breaking changes — removals, renames, or semantic shifts — bump the prefix to /integration/v2/*. The old version continues to work during a deprecation window.

See Versioning for the full policy.