Skip to main content

Create a Token

Exchanges your client credentials for an access token and a refresh token. Every other partner endpoint takes the access token as Authorization: Bearer <access_token>. expires_in is a lifetime in seconds (RFC 6749 §5.1), not a timestamp.

The endpoint is public at the gateway (no bearer token). The integration service still checks the client credentials, the client's usage window and, when the client requires one, its client certificate.

One live access token per client

Each client has exactly one valid access token at a time. Issuing a new token, with any grant, immediately invalidates the previous access token of that client: the next call made with the old token is refused with 401. If several processes share a client, let one of them own the token and hand it to the others, or give each process its own client.

Request​

POST /v2/oauth/token

Headers​

NameDescription
Content-TypeRequired. application/json

Body​

FieldTypeRequiredDescription
client_idstringYesYour client id
client_secretstringYesYour client secret
grant_typestringYesclient_credentials, password or refresh_token. The grant must be enabled on your client.
usernamestringpassword grantEmail of a supervisor on your client's node
passwordstringpassword grantThat supervisor's password
refresh_tokenstringrefresh_token grantThe most recent refresh token issued to your client

Grant types​

GrantActs asPermissions in the tokenAccess token lifetimeRefresh token lifetime
client_credentialsThe client itself (machine to machine)The scopes configured on the clientThe client's token_expiry.duration; no expiry when token_expiry.enabled is false (expires_in is then 0)Same as the access token
passwordA supervisor of the client's nodeThe supervisor's role permissions15 minutes (expires_in: 900)30 days
refresh_tokenWhoever the refresh token was issued toSame as the original grantSame as the original grantSame as the original grant

Regardless of the client's policy, a refresh token is never redeemable more than 365 days after it was issued.

Client credentials​

curl --request POST \
--url '{{baseUrl}}/v2/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"client_id": "c1a9beda-b14c-4eb5-b880-113023e6fc2d",
"client_secret": "<client_secret>",
"grant_type": "client_credentials"
}'

Password​

curl --request POST \
--url '{{baseUrl}}/v2/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"client_id": "c1a9beda-b14c-4eb5-b880-113023e6fc2d",
"client_secret": "<client_secret>",
"grant_type": "password",
"username": "supervisor@example.com",
"password": "<password>"
}'

The supervisor must belong to the same node as the client; a supervisor of another node is refused exactly like a wrong password. Repeated failed passwords for the same username or from the same address are throttled with 429.

Refresh token​

curl --request POST \
--url '{{baseUrl}}/v2/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"client_id": "c1a9beda-b14c-4eb5-b880-113023e6fc2d",
"client_secret": "<client_secret>",
"grant_type": "refresh_token",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'

A refresh token works once. The call spends the presented refresh token and answers a new pair; store both. Presenting a spent refresh token, or one that a newer issuance superseded, is refused with 400 and code 10006. Only the newest refresh token issued to a client is live, so a fresh client_credentials or password token also supersedes the outstanding refresh token.

Response​

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "token:view token:create default",
"created_at": 1762687200
}

Response Fields​

FieldTypeDescription
access_tokenstringJWT to send as Authorization: Bearer <access_token>
token_typestringAlways Bearer
expires_innumberAccess token lifetime in seconds. 0 when the client's policy issues tokens without expiry
refresh_tokenstringSingle-use token for the refresh_token grant
scopestringSpace-separated permissions the token carries (role permissions for password, client scopes for client_credentials)
created_atnumberUnix time the token was issued

Errors​

This endpoint predates the shared error format and answers most refusals with HTTP 409 and a JSON string body that embeds the code:

"Code: 10003, Domain: 20, Message: invalid request"
StatusBodyCause
400stringThe body does not parse, or client_id, client_secret or grant_type is missing / not a known grant
400{"code": 10006, "domain": 20, "message": "refresh token is no longer valid"}The refresh token was already redeemed or a newer issuance superseded it. Sign in again with your client's own grant
401{"code": 10005, "domain": 20, "message": "client certificate required"}The client requires a certificate and the TLS handshake did not present the one issued to it. See mTLS Client Certificates
409Code: 10001 … client not foundUnknown client_id / client_secret pair
409Code: 10002 … client is inactiveThe operator deactivated the client
409Code: 10004 … client has expiredThe client's usage window has ended. No grant issues a token, not even refresh_token
409Code: 10003 … invalid requestThe grant is not enabled on the client; a grant parameter is missing; wrong username or password; the supervisor is on another node; the refresh token is malformed, expired, older than 365 days or belongs to another client
429{"code": 10001, "domain": 86, "message": "too many failed password attempts; try again later"}Too many failed password grant attempts

Calls to every other endpoint use the shared error format.

Using the token​

curl --request GET \
--url '{{baseUrl}}/v2/admin/tokens/68aeb272d9336c619ce249f6' \
--header 'Authorization: Bearer <access_token>'

A protected call answers 401 when the token is missing, malformed, expired, is a refresh token, or is no longer the client's live access token. Request a new token (or redeem the refresh token) and retry.