These endpoints support partner apps that merchants install, the Flint CLI, and workload identities. Partner apps and the CLI use the shared token and revocation endpoints. Workload identities exchange an external issuer's JWT for a short-lived access token at the token endpoint. See Partner OAuth for the hosted install flow and CLI OAuth for device login.
Client kinds#
The grant and client authentication select the flow:
- Partner apps are confidential clients. They authenticate with HTTP Basic or with
client_idandclient_secretform fields, never both in one request. They use the authorization code grant with S256 PKCE, and the refresh token grant. flint-cliis a public client. It sendsclient_id=flint-cliwith noclient_secretand noAuthorizationheader. It uses the device code grant and the refresh token grant.- Workload identities use
grant_type=urn:ietf:params:oauth:grant-type:token-exchange. They send a JWT from their configured external issuer and aworkload_identity_id, without client authentication orclient_id.
A request that sends client_id=flint-cli together with a secret is treated as a partner app and fails client authentication.
Requests#
Token and revocation requests use application/x-www-form-urlencoded bodies only. JSON bodies and URL query strings return 400 invalid_request. A repeated field returns invalid_request. Token exchange rejects unrecognized fields; partner and CLI requests ignore them.
Responses#
OAuth responses use flat protocol JSON from RFC 6749, RFC 7009, and RFC 8693, without the Flint envelope. Access tokens are Bearer tokens for every client. Each client reads scope in the vocabulary it requested: permission IDs for partner apps, and API scope names for flint-cli and workload identities.
Errors carry error and a fixed error_description. The description is text for people, not for parsing; branch on error. A failed client authentication returns 401 invalid_client, with WWW-Authenticate: Basic realm="flint" when the client used HTTP Basic.
Workload identity token exchange#
First create a workload identity that trusts your issuer and workload. Then call POST /v1/oauth/token with a JWT from that external issuer. Flint verifies the JWT's signature and claims, then matches it against that identity's trust rules. You don't send a Flint API key.
| Field | Required | Value |
|---|---|---|
grant_type | Yes | urn:ietf:params:oauth:grant-type:token-exchange |
subject_token | Yes | Your external issuer's JWT, up to 8 KiB. |
subject_token_type | Yes | urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:id_token. |
workload_identity_id | Yes | The wid_ identity whose trust rules and scopes apply. |
scope | No | Space-separated subset of the identity's API scopes. Omit to request all its current scopes. |
requested_token_type | No | urn:ietf:params:oauth:token-type:access_token. No other value is supported. |
Don't send an Authorization header, client_id, client secrets or assertions, actor_token, resource, or audience. These and any other unrecognized fields return invalid_request. The identity already fixes the accepted issuer and audience, merchant, mode, and sandbox.
curl https://api.withflintpay.com/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data-urlencode "subject_token=$OIDC_TOKEN" \
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
--data-urlencode "workload_identity_id=wid_1kmn0aExample" \
--data-urlencode "scope=commerce.products.read"
{
"access_token": "fwat_example",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "commerce.products.read",
"workload_identity_id": "wid_1kmn0aExample",
"merchant_id": "mer_1kmn0aExample",
"mode": "test",
"sandbox_id": "test_1kmn0aExample"
}
The opaque fwat_ access token expires after the shorter of 3,600 seconds and the JWT's remaining lifetime. The response never contains a refresh token. Live-mode responses omit sandbox_id. Exchange another valid issuer token when you need a new access token; the same unexpired issuer token can be exchanged more than once.
Send the returned token as Authorization: Bearer fwat_... on API requests. Don't combine it with another credential. The token stays bound to the identity's merchant, mode, and sandbox. If you send Flint-Merchant-Id, Flint-Mode, or Flint-Sandbox-Id, each must match that binding; a mismatch returns 403 FORBIDDEN with reason oauth_context_mismatch.
Deactivating or archiving an identity invalidates its access tokens on the next request. Narrowing its scopes also takes effect on the next request, including for tokens issued earlier. A token can never regain scopes excluded by its original exchange. GET /v1/developer/auth-context identifies the caller with auth_type: workload_identity, workload_identity_id, name, and expires_at.
Exchange errors and limits#
| Status | OAuth error | Action |
|---|---|---|
400 | invalid_request | Check required fields, token types, content type, and forbidden or repeated fields. |
400 | invalid_grant | Check that the identity is active and the issuer token is valid and matches its trust rules. |
400 | invalid_scope | Request only scopes currently granted to the identity. |
400 | unsupported_grant_type | Use a grant advertised in discovery. |
429 | temporarily_unavailable | Wait at least Retry-After seconds; Flint-Rate-Limited-Reason identifies the exhausted budget. |
503 | temporarily_unavailable | Issuer verification keys are unavailable. Retry later. |
500 | server_error | Retry later and retain X-Request-Id when contacting Flint Help. |
Token exchange allows 300 requests per minute per IP and 600 per minute per workload identity. The identity budget counts requests only after signature verification. Each invalid_grant failure has a fixed description; don't parse descriptions as error codes.
Discovery#
Flint publishes authorization server metadata (RFC 8414) at https://api.withflintpay.com/.well-known/oauth-authorization-server. It lists the endpoints, the supported grant types, and S256 as the only PKCE method. Both discovery paths and the public key set are documented in OAuth discovery and signing keys. These paths are outside the /v1 API.
The Partner app installs guide walks through PKCE, token exchange, refresh, and revocation for partner apps.
