Authentication
Authentication and Tokens
Issue, refresh, and revoke tokens, and send an Access Token with business requests.
Credential lifecycle
| Credential | Prefix | Default lifetime | Purpose |
|---|---|---|---|
| App Key | ak_ | Long-lived | Identifies the application and remains visible in the admin console |
| App Secret | sk_ | Until reset | Proves the application identity and is shown only after creation or reset |
| Access Token | oat_ | 24 hours | Authorizes protected API requests |
| Refresh Token | ort_ | 30 days | Rotates both the Access Token and Refresh Token |
Resetting the App Secret or disabling the application immediately revokes all active tokens for that application.
Issue tokens
| Item | Value |
|---|---|
| Method | POST |
| Request URL | https://backend.biotech-mkt.com/openapi/auth/token |
| Authentication | App Key + App Secret |
| Permission | No endpoint grant required |
| Content-Type | application/json |
This endpoint does not require an Access Token. Send a JSON body:
Request body
| Field | Type | Required | Constraint |
|---|---|---|---|
app_key | string | Yes | Maximum 64 characters |
app_secret | string | Yes | Maximum 128 characters |
Request example
curl --request POST "https://backend.biotech-mkt.com/openapi/auth/token" \ --header "Content-Type: application/json" \ --data '{"app_key":"ak_xxx","app_secret":"sk_xxx"}'Response example
{ "code": 200, "message": "Success", "data": { "token_type": "Bearer", "app_id": 1, "app_key": "ak_xxx", "access_token": "oat_xxx", "expires_in": 86400, "access_expires_at": "2026-08-27 12:00:00", "refresh_token": "ort_xxx", "refresh_expires_in": 2592000, "refresh_expires_at": "2026-09-25 12:00:00" }}Response fields
| Field | Return type | Description |
|---|---|---|
token_type | string | Always Bearer |
app_id | integer | Internal OpenAPI application ID |
app_key | string | App Key of the current application |
access_token | string | Full Access Token, returned only in this response |
expires_in | integer | Access Token lifetime in seconds; default 86400 |
access_expires_at | string | Absolute Access Token expiration time |
refresh_token | string | Full Refresh Token, returned only in this response |
refresh_expires_in | integer | Refresh Token lifetime in seconds; default 2592000 |
refresh_expires_at | string | Absolute Refresh Token expiration time |
On success, data contains the token type, application details, both tokens, their lifetimes in seconds, and absolute expiration timestamps. Authentication failures return 401 without revealing whether the App Key or App Secret was incorrect.
Refresh tokens
| Item | Value |
|---|---|
| Method | POST |
| Request URL | https://backend.biotech-mkt.com/openapi/auth/refresh |
| Authentication | Refresh Token |
| Permission | No endpoint grant required |
| Content-Type | application/json |
Request body
| Field | Type | Required | Constraint |
|---|---|---|---|
refresh_token | string | Yes | Maximum 128 characters |
Request example
curl --request POST "https://backend.biotech-mkt.com/openapi/auth/refresh" \ --header "Content-Type: application/json" \ --data '{"refresh_token":"ort_xxx"}'Response example
The response has the same fields as the issue-token endpoint, but both tokens and expiration timestamps are new:
{ "code": 200, "message": "Success", "data": { "token_type": "Bearer", "app_id": 1, "app_key": "ak_xxx", "access_token": "oat_new_xxx", "expires_in": 86400, "access_expires_at": "2026-08-27 13:00:00", "refresh_token": "ort_new_xxx", "refresh_expires_in": 2592000, "refresh_expires_at": "2026-09-25 13:00:00" }}Refresh uses token rotation. A successful response returns a new Access Token and a new Refresh Token, and both old tokens stop working.
Atomic replacement
Serialize refresh attempts for the same Refresh Token and replace both stored tokens in one transaction or atomic write. Do not let multiple processes refresh the same token concurrently.
Revoke tokens
| Item | Value |
|---|---|
| Method | POST |
| Request URL | https://backend.biotech-mkt.com/openapi/auth/revoke |
| Authentication | Refresh Token |
| Permission | No endpoint grant required |
| Content-Type | application/json |
Request body
| Field | Type | Required | Constraint |
|---|---|---|---|
refresh_token | string | Yes | Maximum 128 characters |
Request example
curl --request POST "https://backend.biotech-mkt.com/openapi/auth/revoke" \ --header "Content-Type: application/json" \ --data '{"refresh_token":"ort_xxx"}'Response example
{ "code": 200, "message": "Success", "data": []}Revocation targets the token record identified by the Refresh Token. Repeating the request, or submitting a Refresh Token that no longer exists, is treated as an idempotent success.
Send an Access Token
Use the standard Bearer header:
Authorization: Bearer oat_xxxCompatible alternative:
X-Openapi-Token: oat_xxxWhen both headers are present and the Bearer header is validly formatted, the server uses Authorization. Access Tokens are not accepted in URL query parameters.
Authentication response headers
Token endpoints return:
Cache-Control: no-storePragma: no-cacheX-Request-Id: <request ID>
A 401 response also includes WWW-Authenticate: Bearer.
Errors and rate limits
Authentication endpoints can return 401, 422, 429, 500, or 503. Deployment settings can override the defaults. See Errors and Rate Limits for limits, Retry-After, and retry guidance.
Security recommendations
- Store the App Secret and Refresh Token only in a server-side secret manager or encrypted storage.
- Log only token prefixes or irreversible fingerprints, never full values.
- Use HTTPS and never transmit credentials over plaintext HTTP.
- Reset the Secret immediately after a leak; doing so invalidates all existing tokens.
Last updated Aug 27, 2026