Guides Integration

Exchange Code for Tokens

The authorization code is single-use and short-lived. Exchange it from your server for an ID token carrying the age result.

POSThttps://app.agewallet.io/user/token

Parameters are sent in the request body as form fields, with Content-Type: application/x-www-form-urlencoded. Sending JSON returns invalid_request.

ParameterRequiredPurpose
grant_typeYesAlways authorization_code.
codeYesThe one-time code from the callback.
redirect_uriYesYour callback URL. Must match the /authorize request exactly.
client_idYesYour registered client ID.
client_secretConfidential clientsYour client secret. Omit for public clients, which rely on PKCE instead.
code_verifierYesThe original, unhashed verifier you generated before /authorize.

Success Response

FieldWhat is it for
id_tokenProves the user authenticated. Validate it, but don’t read the age status from it.
access_tokenBearer token for /userinfo. This is the call that returns the age result.
token_typeAlways Bearer.
expires_inLifetime of the access token in seconds. 900 is 15 minutes.

No refresh token is issued. A verification is a one-time event: if you need a fresh result, start a new authorization request.

When the exchange fails

Failures return HTTP 400 with an error field. invalid_grant covers most of them and is worth checking against all four causes before opening a ticket.

ErrorUsual cause
invalid_grantThe code expired, was already used, the redirect_uri does not match the /authorize request, or the code_verifier does not match the challenge you sent.
invalid_clientWrong client_id, or a missing or incorrect client_secret.
invalid_requestA required parameter is missing, or the body was sent as JSON rather than form-encoded.
unsupported_grant_typegrant_type is not authorization_code.

Next: validate the ID token

Receiving an id_token is not the same as trusting it. Before you act on the age result, verify its signature against our JWKS, and check that iss, aud, exp, and the nonce you sent all match.

Last updated July 28, 2026

Was this page helpful?