External Console API
The External Console API is the surface you call from your own systems to run the account work you would otherwise do by hand in the console: list the models an account may call, read usage history, check an account's credit, and transfer credit between accounts.
It is authenticated with a console token (csk-...), which is not the same
credential as the sk-... key used for model inference. The Asset Management
API is served from the same base URL but takes the sk-... API key instead.
Base URL
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| List Accounts | GET /accounts |
Accounts this token can reach |
| List Models | GET /models |
Models an account may call, with list pricing |
| Get Usage | GET /usage |
Usage history, paginated |
| Get One Usage Record | GET /usage/{request_id} |
A single usage record |
| Get Credit | GET /accounts/{accountId}/credit |
Balance or remaining quota of one account |
| Transfer Credit | POST /accounts/transfers |
Move credit to a sub-account |
Authentication
Send your console token as a Bearer token on every request:
A console token carries no permissions of its own. It inherits the authority of the user who created it, resolved against IAM on every call. Two consequences worth designing around:
- Take a permission away from that user and the token loses it immediately — no re-issue needed.
- A token issued by a member who lacks a permission cannot be used to work around that. Issuing a token grants nothing the session did not already have.
See Console Tokens for how to create, pin and revoke them.
Choosing the account a request acts in
Most organisations have one root account with sub-accounts beneath it. A token that is not pinned can reach several of them, so every request settles on one effective account.
The request names it — the parameter differs per endpoint and is documented with each one. Whatever you pass is intersected with the token's pin and your live memberships, so you cannot name an account you have no claim to.
Omit it and the first account in your list is used. That default is positional: it moves as your account list changes.
Name the account explicitly in automated jobs
Call GET /accounts first and pass the id you want. A
job that relies on the positional default will silently start acting on a
different account the day your account list changes.
Status codes
Branch on the HTTP status and on error.code — never on error.message, which
is prose and will change.
Treat the payload as open: ignore fields you do not recognise. New ones are added without a version bump.
| Status | Meaning |
|---|---|
| 400 | The request was malformed or failed validation |
| 401 | Token missing, malformed, expired or revoked |
| 403 | The issuing user lacks the permission, or their membership is inactive |
| 404 | The thing you named is not reachable from your effective account |
| 429 | Rate limited. Honour Retry-After |
| 5xx | A server-side failure |
Rate limiting
Limits are applied per user, not per token and not per IP — a second token held by the same user draws from the same budget. The default is 120 requests per 60 seconds.
Every response carries the current budget:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests allowed per window |
X-RateLimit-Remaining |
Requests left in this window (floors at 0) |
X-RateLimit-Reset |
Unix seconds when the window rolls over |
Retry-After |
Seconds to wait — sent on 429 only |
Going over budget returns 429 with error.code RATE_LIMIT_EXCEEDED.