---
title: "Redeem a sign-in code"
method: POST
path: "/v1/auth/code/verify"
tags: ["Auth"]
---

# Redeem a sign-in code

`POST /v1/auth/code/verify`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/auth/code/verify`
- Auth: none required

Exchanges an emailed code for a session, creating the account if the address is new. The code is single-use and short-lived. A code is destroyed after too many wrong guesses, since six digits is small enough to guess given unlimited attempts — the client must then request a new one rather than retry.

## Request body

- SignInCodeVerify
  - `email` string, email, required
  - `code` string, required: The code from the email, as sent — digits only.

## Response `200`

Authenticated; a session is returned.

- AuthSession: A short-lived access JWT plus a rotating refresh token. The refresh token is also set as an httpOnly, SameSite=Lax cookie scoped to `/v1/auth` (Secure whenever the connection is https), which is what carries a browser session across a reload; the body copy is for clients with no cookie jar, such as the CLI.
  - `token_type` 'Bearer', required
  - `access_token` string, required: Short-lived access JWT. Send as `Authorization: Bearer <token>`.
  - `expires_in` integer, required: Access-token lifetime in seconds.
  - `refresh_token` string: Single-use refresh token, rotated on every refresh.
  - `user` User, required
    - `id` string, required: Public user ID (user_ prefix).
    - `email` string, email, required
    - `name` string, nullable
    - `email_verified` boolean, required
    - `roles` string[], required: Roles on naturali itself, not on a project. Empty for almost every account; `admin` marks a naturali operator. Changed only through `PUT /v1/admin/users/{user_id}/roles`, by a caller holding the role that grants each one.
    - `usage_alerts` boolean, required: Whether the account is emailed when its model credit, runs or indexed storage reaches 75%, 90% and 100%. On for a new account.
    - `created_at` string, date-time, required

## Other responses

- `400`: The request was malformed or failed validation.
- `401`: The code is wrong, expired, or already used (`invalid_token` / `expired_token`).
- `429`: Too many incorrect attempts (`too_many_attempts`); the code has been destroyed and a new one must be requested.

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
