Skip to main content
The ledger API uses the OAuth 2.0 client_credentials grant. A client has a client_id, a client_secret shown once, a mode (test or live), and a set of granted scopes.
Integrating spend control instead? It uses agent keys and dashboard tokens, documented on Kordio Control: agent keys and dashboard tokens. Nothing on this page applies there.

Mint an access token

Send your credentials as HTTP Basic auth, or as form fields.
200 OK
Mint on demand and cache until shortly before expiry. A token stored once and never refreshed becomes a dead integration within the hour. The token carries your tenant, mode, and scopes. There is no ?test= parameter: the client’s mode decides which dataset you touch.

Scopes

Rate limits are per client and per class: 100 reads a second and 20 writes a second, answered with X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a Retry-After on a 429.
Scopes do not imply each other. ledger:write does not grant ledger:read. A client that both lists and creates needs both scopes named explicitly in the token request. This is the most common cause of an unexpected 403 on a first integration.
Reading period closes needs only ledger:read; only the mutations need ledger:period_close. A token missing the scope an endpoint requires returns 403 with code insufficient_scope, plus a WWW-Authenticate header naming the scope it wanted.

Managing clients

Clients are self-service, with their own scopes:
Secrets are shown in plaintext exactly once, at creation or rotation; only a SHA-256 hash is stored. Rotating with POST /v1/oauth_clients/{id}/rotate_secret keeps the previous secret valid for 24 hours, so you can roll a deployment without downtime. Rotating again inside that window returns 409 rotation_in_progress; pass ?force=true to destroy the old secret immediately.

Discovery

The authorization server publishes RFC 8414 metadata at GET /.well-known/oauth-authorization-server, so tooling can discover the token endpoint, grants, scopes and signing algorithms without hard-coding them. Token-endpoint failures follow RFC 6749: invalid_client, invalid_grant, invalid_scope, unsupported_grant_type.

Test and live

Every client is bound to one mode and the two never mix. Live and test data are segregated at the storage level, so a test client cannot read or write live rows. Responses echo livemode so you can confirm which dataset you touched:
Mode is a property of the ledger, not the organization. One organization can hold several ledgers, and each carries its own mode. See GET /v1/ledgers.

Step-up authentication

One operation requires more than a valid token. POST /v1/tenants/me/anonymize needs ledger:write and a token issued within the last five minutes, or it returns 401 fresh_authentication_required. Mint a fresh token immediately before calling it.

Next steps

Post your first transaction

Use the token you just minted.

Errors

Every code the ledger returns, with hints.