Authentication and App Token
The server REST API (OpenAPI below) is only for your business server. Before calling it, exchange the app's Client ID and Client Secret for an App Token, then send it in the Authorization header of every request:
Authorization: Bearer <App Token>An App Token carries management rights over the whole app: creating and deleting users, sending messages as any user, exporting messages. Keep and use it on your server only; never ship it in apps, web pages or mini programs.
Don't call OpenAPI from a browser
OpenAPI doesn't return CORS headers, so browser scripts can't call it directly. Clients (apps, web pages) use the user's own User Token with the client APIs, see Sign-in and devices.
Get the Client ID and Client Secret
When you create an app in the console (including the demo app created at sign-up), the first set of credentials is generated; store them in your server's secret manager. You can view the Client ID and Client Secret under "Access credentials" on the console Overview; viewing the Secret requires re-verifying your identity. You can later rotate or revoke credentials on the "Server keys" page of the app's details, see Server credentials and IP allowlist.
Each app can have up to two valid Secrets at once (primary and secondary slots), for rotation without downtime:
- Rotate the key in the console to generate a new Secret; the old Secret enters a 24-hour grace period during which both work;
- Update your server's configuration to the new Secret and use it to get new App Tokens;
- The old Secret expires automatically when the grace period ends, or revoke it manually once you've confirmed everything works.
Once a Secret is revoked or expires, App Tokens obtained with it stop working immediately.
Get an App Token
/{org_name}/{app_name}/tokenExchanges the app's credentials for an App Token. This API doesn't need the Authorization header.
Path parameters
| Parameter | Type | Description |
|---|---|---|
org_name | String | The tenant's unique identifier, the part of the AppKey before # |
app_name | String | The app name, the part of the AppKey after # |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
grant_type | String | Yes | Always client_credentials |
client_id | String | Yes | The app's Client ID |
client_secret | String | Yes | The app's Client Secret |
Example request
curl -X POST "$IM_API/$ORG/$APP/token" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "YXA6MVMEOd4cqtAvTUnGUUCnTw",
"client_secret": "YXA6sMvoBP0HiVoT-69b1BWXzip5_-Ii2ea8u0ph9ijVgck"
}'Response
Returns 200 OK:
| Field | Type | Description |
|---|---|---|
access_token | String | The App Token |
expires_in | Number | Validity in seconds. 604800 (7 days) by default, set by the app's runtime policy app_token_ttl_seconds, from 300 seconds to 1 year |
application | String | App ID |
{
"access_token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImsxIiwidHlwIjoiSldUIn0.eyJpc3Mi...",
"expires_in": 604800,
"application": "99579846509723648"
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | grant_type isn't client_credentials |
| 401 | invalid_client | Wrong Client ID or Client Secret, or org_name or app_name doesn't exist. These cases aren't distinguished, to prevent probing |
| 403 | ip_not_allowed | The source IP isn't in the app's IP allowlist |
| 403 | tenant_unavailable | The tenant is suspended or in the account-deletion cooling-off period |
| 403 | app_unavailable | The app is disabled or suspended |
| 429 | rate_limited | Too many requests; wait for the Retry-After header and retry. Each source IP can get at most 120 tokens a minute; once credentials pass validation, the request also counts toward the app's OpenAPI call quota |
Using and caching App Tokens
With the Go, Python or Node.js server SDKs, the SDK gets, caches and renews App Tokens automatically, so the rules below are already handled for you.
- Cache and reuse: an App Token can be used repeatedly while valid; don't get a new one before every OpenAPI call. Cache it on the server and get a new one shortly before expiry (for example with less than 10% left). Multiple service instances can each get their own; several valid App Tokens at once don't affect each other.
- Get a new one when it stops working:
401 unauthenticatedfrom OpenAPI means the App Token no longer works; get a new one and retry once. These make issued App Tokens stop working early:- the Secret that issued it is revoked or expires;
- the app is disabled or suspended by the platform, or the tenant is suspended;
- the app is deleted or the tenant closed, in which case
details.reasonisapp_deletedortenant_closed: don't retry, and clear the stored credentials.
{
"error": {
"code": "unauthenticated",
"message": "App Token 无效或已过期",
"request_id": "HQ566J6O5VSPRLYPUX5UXOEJIX"
}
}Authentication checks on every request
Every OpenAPI request goes through these checks in order, and fails immediately at the first one that doesn't pass:
| Order | Check | On failure |
|---|---|---|
| 1 | The App Token's signature and validity; the Secret that issued it is still valid; no tenant or app change has invalidated it | 401 unauthenticated |
| 2 | The App Token's app matches org_name and app_name in the path | 403 permission_denied |
| 3 | The source IP is in the app's IP allowlist (no restriction if no allowlist is set) | 403 ip_not_allowed |
| 4 | The tenant and app can provide service. If the app was disabled or suspended, or the tenant suspended, after the token was issued, the token stops working and step 1 already returns 401 | 403 tenant_unavailable or 403 app_unavailable |
| 5 | The app's OpenAPI call quota (requests per second) isn't used up | 429 rate_limited |
Only after these checks does the request reach the API's business logic. For each API's own errors see its documentation; for common errors see Error codes.
Security recommendations
- Keep credentials on the server only: the Client Secret and App Token are equivalent to the app's admin password; never put them in client code, commit them to repositories, or print them in logs.
- Turn on the IP allowlist: add your business servers' egress IPs to the IP allowlist, so leaked credentials can't be used from elsewhere.
- Rotate Secrets regularly: rotating as described above doesn't interrupt service. If you suspect a leak, revoke it in the console immediately, and App Tokens obtained with it stop working at once.
- Separate test and production: create different apps for test and production, each with its own credentials.
RTC channel identity
Standalone RTC channel APIs use different identities:
| Identity | Used for |
|---|---|
| App Token | Channel management, issuing tickets, removing and banning members |
| User Token | The user's own channel session: joining, heartbeats, renewing credentials, leaving |
Your server only gives each user their own channel ticket, never the App Token. Channel tickets and media credentials can't replace IM sign-in tokens, and clients can't specify another user's username or role.
