Exchange or refresh a token
const url = 'https://qinto-api.blotout.io/oauth/token';const options = { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ grant_type: 'authorization_code', code: 'qac_9f2c…', redirect_uri: 'https://example.com/callback', client_id: 'qc_4b1d…', client_secret: 'qcs_71aa…', code_verifier: 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk', refresh_token: 'qor_5e0b…', scope: 'team:read', resource: 'https://qinto-api.blotout.io', subject_token: 'qoa_83fe…', subject_token_type: 'urn:ietf:params:oauth:token-type:access_token', requested_token_type: 'urn:ietf:params:oauth:token-type:access_token' })};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://qinto-api.blotout.io/oauth/token \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data grant_type=authorization_code \ --data 'code=qac_9f2c…' \ --data redirect_uri=https://example.com/callback \ --data 'client_id=qc_4b1d…' \ --data 'client_secret=qcs_71aa…' \ --data code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk \ --data 'refresh_token=qor_5e0b…' \ --data scope=team:read \ --data resource=https://qinto-api.blotout.io \ --data 'subject_token=qoa_83fe…' \ --data subject_token_type=urn:ietf:params:oauth:token-type:access_token \ --data requested_token_type=urn:ietf:params:oauth:token-type:access_tokenIssues qoa_ access tokens: exchanges a one-time authorization code from the consent flow, rotates a qor_ refresh token into its successor plus a fresh access token, or re-audiences a token for another Qinto resource server (RFC 8693).
How it works
Requests are form-encoded per RFC 6749. Confidential clients authenticate with HTTP Basic or client_secret_post; public clients send their client_id alone. resource is single-valued — every access token is bound to exactly one audience — and a replayed code or reused refresh token revokes the whole grant. A token exchange takes subject_token and subject_token_type, carries the subject’s teams and scope across unchanged, never outlives it, and answers 429 when the session’s exchange budget is exhausted. Responses carry Cache-Control: no-store.
Request Bodyrequired
Section titled “Request Bodyrequired”RFC 6749 form-encoded token request.
object
The grant type: authorization_code to redeem a consent code, refresh_token to rotate a refresh token, or urn:ietf:params:oauth:grant-type:token-exchange for the RFC 8693 exchange.
Example
authorization_codeThe one-time qac_ authorization code from the consent redirect. Required for authorization_code.
Example
qac_9f2c…The exact redirect URI used in the authorization request. Required for authorization_code.
Example
https://example.com/callbackThe app’s client id. Required unless the client authenticates with HTTP Basic.
Example
qc_4b1d…The app’s client secret for client_secret_post authentication. Public clients omit it.
Example
qcs_71aa…The PKCE code verifier whose S256 digest was sent as the authorization request’s code challenge. Required for authorization_code.
Example
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkThe current qor_ refresh token to rotate. Required for refresh_token.
Example
qor_5e0b…Optional narrowing scope for refresh_token: a space-delimited subset of the grant’s scope.
Example
team:readOptional RFC 8707 resource the token is minted for. Single-valued: every access token is bound to exactly one audience.
Example
https://qinto-api.blotout.ioThe qoa_ access token to re-audience. Required for urn:ietf:params:oauth:grant-type:token-exchange.
Example
qoa_83fe…The type of subject_token, required by RFC 8693 §2.1 for the exchange. Only urn:ietf:params:oauth:token-type:access_token is accepted.
Example
urn:ietf:params:oauth:token-type:access_tokenThe type of token to issue. Optional; only urn:ietf:params:oauth:token-type:access_token is accepted.
Example
urn:ietf:params:oauth:token-type:access_tokenResponses
Section titled “Responses”The issued tokens.
object
The issued qoa_ bearer token, valid for expires_in seconds at exactly one resource server.
Always bearer.
Access-token lifetime in seconds.
The new qor_ refresh token. Present when the grant carries offline_access; each use rotates it, so store this one and discard its predecessor.
The scope actually granted (RFC 6749 §5.1), which may be narrower than requested.
The type of the issued token. Present on a token exchange, where RFC 8693 §2.2.1 requires it.
Example
{ "access_token": "qoa_83fe…", "token_type": "bearer", "expires_in": 3600, "refresh_token": "qor_5e0b…", "scope": "team:read offline_access", "issued_token_type": "urn:ietf:params:oauth:token-type:access_token"}The request failed: invalid_request, invalid_grant, unsupported_grant_type, invalid_scope, or invalid_target.
object
The RFC 6749 / RFC 8707 error code.
A human-readable explanation of the failure, safe to log.
Example
{ "error": "invalid_request", "error_description": "The authorization code has expired."}invalid_client — client authentication failed.
object
The RFC 6749 / RFC 8707 error code.
A human-readable explanation of the failure, safe to log.
Example
{ "error": "invalid_request", "error_description": "The authorization code has expired."}The token-exchange budget for this session is exhausted; retry shortly.
object
The RFC 6749 / RFC 8707 error code.
A human-readable explanation of the failure, safe to log.
Example
{ "error": "invalid_request", "error_description": "The authorization code has expired."}