Skip to content

Working with tokens ​

Every flow ends at the same place: an access_token you send with each API call, and a refresh_token you use to get a new pair when it expires.

Exchanging the authorization code ​

Identical for Flow A and Flow B.

Request

http
POST /auth/oauth/token
Content-Type: application/json

{
    "grant_type": "authorization_code",
    "code": "abc123def456",
    "client_id": "69c50a2d50f7b939923a469b",
    "client_secret": "4f3c1a9e8b27d605f1ac93e7d284b6510ff7a3c9e15d8b2470a6cd91e83f4b27"
}
FieldTypeRequiredDescription
grant_typestringYesMust be "authorization_code"
codestringYesThe authorization code Bito gave you
client_idstringYesYour OAuth client id
client_secretstringYesYour client secret

Response

json
{
    "code": 0,
    "message": "Success",
    "data": {
        "access_token": "eyJhbGciOiJIUzI1NiIs...",
        "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
        "expires_in": 86400,
        "token_type": "Bearer",
        "scope": "read write"
    }
}

expires_in is in seconds, counted from the moment of issue.

WARNING

An authorization code is single-use and dies after 20 minutes. Exchanging it twice returns Invalid credentials, not a second token pair.

Refreshing tokens ​

Request

http
POST /auth/oauth/token
Content-Type: application/json

{
    "grant_type": "refresh_token",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "client_id": "69c50a2d50f7b939923a469b",
    "client_secret": "4f3c1a9e8b27d605f1ac93e7d284b6510ff7a3c9e15d8b2470a6cd91e83f4b27"
}

The response shape is identical to the code exchange.

WARNING

Both tokens rotate on every refresh. The refresh token you just used stops working — persist the new pair atomically, or the next refresh will fail and the merchant will have to reinstall.

Refreshing does not re-install an integration the merchant removed. If a merchant uninstalls you, refresh keeps working against a not-installed record; treat your uninstall hook as the authoritative signal to stop calling.

Client credentials grant ​

For server-to-server work with no user present. It goes to a different endpoint — /auth/oauth/auth, not /auth/oauth/token — and returns tokens immediately, with no authorization code in between.

WARNING

Prerequisite: the merchant must already have installed your integration. This grant authenticates against the per-install integration record, so there is nothing to match before the first install.

Request

http
POST /auth/oauth/auth
Content-Type: application/json

{
    "response_type": "client_credentials",
    "client_id": "69c50a2d50f7b939923a469b",
    "client_secret": "4f3c1a9e8b27d605f1ac93e7d284b6510ff7a3c9e15d8b2470a6cd91e83f4b27",
    "redirect_uri": "https://example-app.com/callback",
    "scope": ["read", "write"],
    "state": "optional_state"
}
FieldTypeRequiredDescription
response_typestringYesMust be "client_credentials"
client_idstringYesYour OAuth client id
client_secretstringYesThe secret of the integration install
redirect_uristringYesMust be a valid URL. Validated even though this grant never redirects — send your registered callback.
scopestring[]YesRequested scopes, as an array
statestringNoOpaque value, echoed back

Response

json
{
    "code": 0,
    "message": "Success",
    "data": {
        "access_token": "eyJhbGciOiJIUzI1NiIs...",
        "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
        "expires_in": 86400,
        "token_type": "Bearer",
        "scope": "read write"
    }
}

Using access tokens ​

Send the token in the Authorization header on every call.

http
POST /integration/api/v2/product/get-paging
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
Accept-Language: en

{
    "limit": 20,
    "page": 1
}
HeaderRequiredDescription
AuthorizationYesBearer <access_token>
Accept-LanguageNoResponse language: uz, uz_cyrillic, ru, en, kk, tg, ky. Default uz.
organization_idNoTarget organization inside the account. Used only when the integration's settings (fields) include the organization_id field — otherwise the header is ignored. When the header is absent, that field's value is used.
warehouse_idNoTarget warehouse. Used only when the integration's settings (fields) include the warehouse_id field — otherwise the header is ignored. When the header is absent, that field's value is used.
price_idNoPrice list to read prices from. Used only when the integration's settings (fields) include the price_id field — otherwise the header is ignored. When the header is absent, that field's value is used.
role_idNoThe role the request runs with. Used only when the integration's settings (fields) include the role_id field — otherwise the header is ignored. When the header is absent, that field's value is used.
user_idNoRun the request as this user. The user must belong to this account and not be blocked, otherwise the request is refused. When absent, the request runs as the account owner.

You never send an account id — the token already carries it.

Token lifetimes ​

TokenLifetimeNotes
Authorization code20 minutesSingle use, must be exchanged inside the window
Access token24 hoursSent in the Authorization header
Refresh token90 daysExchanged for a new pair; rotates on every use
One-time token24 hoursSingle use, signed with your client secret — see One-time tokens

WARNING

Rotating your client secret kills every access and refresh token issued under the old one, in every merchant account at once.

Rate limits ​

/auth/oauth/auth and /auth/oauth/token allow 20 requests per minute per IP. Refresh on expiry, not on a timer, and never per request.

Bito 2.0 Integration API