Token
Exchange an authorization code for tokens, or refresh an access token.
POST
Handles two grants, selected by the
grant_type body field: authorization_code (with mandatory PKCE) and refresh_token (with rotation and reuse detection). Accepts both application/json and application/x-www-form-urlencoded. Prefer form encoding, since that’s what most OAuth libraries send by default.
Access tokens are JWTs signed with ES256, valid for 900 seconds. Refresh tokens are opaque strings, valid for 60 days from whenever they were last issued, and rotate on every use.
Authorization code grant
Body Parameters
Must be
"authorization_code".The code from the
/oauth/authorize callback. Single-use: redeeming it twice
fails with invalid_grant. Expires 10 minutes after it was issued.Must exactly match the
redirect_uri used in the authorization request.The original value that
code_challenge was
derived from.
Body Parameters
Must be
"refresh_token".Optionally narrow the scope of the new access token. Must be a subset of what
the grant already has. You can’t use this to escalate scope.
Errors
| Status | error | When |
|---|---|---|
400 | invalid_request | A required field is missing or malformed. |
400 | invalid_scope | Refresh requests a scope outside what the grant already has. |
400 | invalid_grant | The code/refresh token is invalid, expired, already used, or reused after rotation (which also revokes the grant). Also returned when PKCE verification fails or redirect_uri doesn’t match. |
401 | invalid_client | Unknown or disabled client_id. |
400 | unauthorized_client | The client isn’t registered for the grant type it’s using. |