---
title: "Exchange OAuth code for JWT"
method: POST
path: "/api/oauth/exchange"
tags: ["oauth"]
---

# Exchange OAuth code for JWT

`POST /api/oauth/exchange`

Exchange OAuth authorization code for application JWT after validating state, nonce, and ID token signature. User is retrieved from or created in the database and details added to the JWT.

## Request body

- TokenExchangeRequest — Request to exchange OAuth code for JWT
  - `code` string, nullable — Authorization code returned by the OAuth provider after user consent
  - `state` string, nullable — State token from the initial auth request, used for CSRF protection
  - `error` string, nullable — Error code if OAuth provider returned an error instead of authorization code
  - `code_verifier` string, nullable — PKCE code verifier that corresponds to the code_challenge sent in the authorization request

## Response `200`

Successful Response

- TokenExchangeResponse — Response containing JWT and user info
  - `access_token` string, required — JWT to send as a Bearer token in the `Authorization` header on subsequent requests
  - `user` UserInfo, required — User information in token exchange response
    - `id` string, required — Unique Gumnut user identifier with `intuser_` prefix
    - `email` string, nullable, required — Email address reported by the OAuth provider; null if not shared
    - `first_name` string, nullable, required — Given name reported by the OAuth provider; null if not shared
    - `last_name` string, nullable, required — Family name reported by the OAuth provider; null if not shared
    - `clerk_user_id` string, nullable, required — Identifier of the linked identity-provider account
    - `is_active` boolean, required — Whether the account is active. A token exchange can still succeed for an inactive account, but subsequent authenticated API requests are rejected with 401
    - `is_verified` boolean, required — Whether the account is marked verified. An internal account flag, not proof of email verification — it can be true even when `email` is null

## Other responses

- `400` — Invalid authorization code, state, or provider response
- `422` — Validation Error
- `429` — Rate limit exceeded. Retry after the interval in the `Retry-After` header.

---

[API](https://skmtc.net/gumnut-ai/apis/gumnut-api.md) · [All operations](https://skmtc.net/gumnut-ai/apis/gumnut-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/gumnut-ai/gumnut-api/versions/e71db45f5d4a/schema)
