Skip to main content
POST
/
oauth
/
token
curl -X POST 'https://api.resend.com/oauth/token' \
     -H 'Content-Type: application/x-www-form-urlencoded' \
     -d 'grant_type=authorization_code&client_id=550e8400-e29b-41d4-a716-446655440000&code=AUTHORIZATION_CODE&redirect_uri=http%3A%2F%2F127.0.0.1%3A49152%2Foauth%2Fcallback&code_verifier=CODE_VERIFIER_VALUE'
{
  "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6Im9hdXRoX2tleSIsInR5cCI6ImF0K2p3dCJ9...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "JcL7aYfE7S9h3L4qv0o2e1w8m6n5b3x9RkP2tD4uV6Q",
  "scope": "emails:send"
}
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

grant_type
string
required
Must be "authorization_code".
client_id
string
required
code
string
required
The code from the /oauth/authorize callback. Single-use: redeeming it twice fails with invalid_grant. Expires 10 minutes after it was issued.
redirect_uri
string
required
Must exactly match the redirect_uri used in the authorization request.
code_verifier
string
required
The original value that code_challenge was derived from.
curl -X POST 'https://api.resend.com/oauth/token' \
     -H 'Content-Type: application/x-www-form-urlencoded' \
     -d 'grant_type=authorization_code&client_id=550e8400-e29b-41d4-a716-446655440000&code=AUTHORIZATION_CODE&redirect_uri=http%3A%2F%2F127.0.0.1%3A49152%2Foauth%2Fcallback&code_verifier=CODE_VERIFIER_VALUE'
{
  "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6Im9hdXRoX2tleSIsInR5cCI6ImF0K2p3dCJ9...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "JcL7aYfE7S9h3L4qv0o2e1w8m6n5b3x9RkP2tD4uV6Q",
  "scope": "emails:send"
}

Body Parameters

grant_type
string
required
Must be "refresh_token".
client_id
string
required
refresh_token
string
required
scope
string
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.
curl -X POST 'https://api.resend.com/oauth/token' \
     -H 'Content-Type: application/x-www-form-urlencoded' \
     -d 'grant_type=refresh_token&client_id=550e8400-e29b-41d4-a716-446655440000&refresh_token=JcL7aYfE7S9h3L4qv0o2e1w8m6n5b3x9RkP2tD4uV6Q'
{
  "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6Im9hdXRoX2tleSIsInR5cCI6ImF0K2p3dCJ9...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "N3wRefreshTokenValue...",
  "scope": "emails:send"
}

Errors

StatuserrorWhen
400invalid_requestA required field is missing or malformed.
400invalid_scopeRefresh requests a scope outside what the grant already has.
400invalid_grantThe 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.
401invalid_clientUnknown or disabled client_id.
400unauthorized_clientThe client isn’t registered for the grant type it’s using.