Skip to content

Exchange a code or refresh token for an access token

POST /oauth/token

Accepts `application/x-www-form-urlencoded` (RFC 6749) or `application/json`. Public clients authenticate with PKCE alone; confidential clients MUST present their secret, by HTTP Basic or `client_secret` in the body. `grant_type=authorization_code` requires `code`, `redirect_uri` and `code_verifier`. The redirect URI and the client must match those the code was issued to. `grant_type=refresh_token` requires `refresh_token`, and rotates it — the presented token is consumed and a new one returned. An optional `scope` may narrow the grant, never widen it; the grant row is the authority, not the copy on the presented token. Replay is treated as compromise, not as a plain error: presenting an authorization code or a refresh token twice REVOKES THE WHOLE GRANT (OAuth 2.1 §4.1.3 / §6.1), invalidating every token derived from it. The user must re-authorize from `GET /oauth/authorize`. Responses are `Cache-Control: no-store`. Rate limited per `client_id`.

Response Body

access_tokenstring

Bearer token for POST /mcp. Short-lived; re-check expires_in rather than assuming.

expires_ininteger

Access token lifetime in seconds.

refresh_tokenstring

Single-use — rotated on every refresh.

scopestring

Space-delimited granted scopes.

token_typestring
Possible values:
Bearer
curl -X POST 'https://api.elaichi.ai/oauth/token' \
  -H 'Content-Type: application/json'
const response = await fetch('https://api.elaichi.ai/oauth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
});

const data = await response.json();
console.log(data);
import os
import requests

url = "https://api.elaichi.ai/oauth/token"
headers = {
    "Content-Type": "application/json",
}

response = requests.post(url, headers=headers)
print(response.json())