---
title: "Confirm a verification code"
description: "POST /users/confirm-verification: Confirms the code a user typed and returns their user access token."
url: https://docs.tryflume.ai/api/confirm-verification
updated: 2026-11-11
---

# Confirm a verification code

> POST /users/confirm-verification: Confirms the code a user typed and returns their user access token.

Confirms the code a user typed and returns their user access token.

```http
POST https://sandbox-api.tryflume.ai/partner/v1/users/confirm-verification
```

- Flume opens a Flume account for the user, or links the one their email already has.
- `access_token` is returned once and has no expiry. Store it as a secret, against the user.
- Three wrong tries burn the code. After that, or after 10 minutes, send a new one.
- Confirming again issues a new token and stops the old one.

## Request

### Headers

| Header | Required | Description |
|---|---|---|
| `X-Flume-Partner-Key` | Yes | Your partner key. |

### Body (`application/json`)

| Field | Type | Required | Description |
|---|---|---|---|
| `method` | enum | No | The method used to send the code. `email_code` is the only method today. Default `"email_code"`. |
| `email` | string | Yes | The address the code was sent to. Max 320 characters. Pattern `^[^@\s]+@[^@\s]+\.[^@\s]+$`. |
| `code` | string | Yes | The 6 digits from the email. 6 digits. |
| `external_ref` | string or null | No | Your id for the user. Needed only when two of your users share one email. Max 128 characters. |

### Sample

**curl**

```sh
curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/users/confirm-verification" \
  -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "sarah.chen@example.com",
  "code": "482913"
}'
```

**Python**

```python
import os

import requests

headers = {
    "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"],
}

response = requests.post(
    "https://sandbox-api.tryflume.ai/partner/v1/users/confirm-verification",
    headers=headers,
    json={
        "email": "sarah.chen@example.com",
        "code": "482913",
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

**Node.js**

```js
const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/users/confirm-verification", {
  method: "POST",
  headers: {
    "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "email": "sarah.chen@example.com",
    "code": "482913"
  }),
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
console.log(await response.json());
```

## Returns (200)

Every field is always present. A field with no value is `null`.

| Field | Type | Description |
|---|---|---|
| `external_ref` | string | Your id for the user. Check that it is the user you expect before you store the token. |
| `email` | string | The verified address. |
| `access_token` | string | The user access token. Returned once. Flume keeps only a hash of it. |
| `token_type` | string | Always `user_token`. Always `"user_token"`. Default `"user_token"`. |
| `verified_at` | string | When the user verified their email, in UTC. |
| `account` | enum | `CREATED`: Flume opened a new Flume account with the details you sent. `LINKED`: the email already had a Flume account, and Flume linked it unchanged. |

**Response (200)**

```json
{
  "external_ref": "u_8812",
  "email": "sarah.chen@example.com",
  "access_token": "ut_dev_acme_example_token",
  "token_type": "user_token",
  "verified_at": "2026-10-16T09:02:11Z",
  "account": "CREATED"
}
```

## Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `EMBED_TOKEN_INVALID` | You sent an `Authorization` header and no `X-Flume-Partner-Key`. Flume read the call as one from its own iframe. |
| 401 | `PARTNER_KEY_INVALID` | The partner key is missing, unknown, revoked, expired, or from the other environment. On live, also a platform Flume has not enabled yet. |
| 401 | `USER_TOKEN_INVALID` | The user access token is unknown, revoked, another partner's, or replaced by a later verification. A verification in the iframe replaces it too. |
| 404 | `NOT_FOUND` | The thing does not exist, or is not this user's. A search id that is not a UUID also returns this code. |
| 422 | `USER_ACCOUNT_CLOSED` | The user's email belongs to a Flume account that Flume has closed. |
| 422 | `VALIDATION_FAILED` | A field is missing, unknown or has an unusable value. `field` names it as a dotted path. Some refusals carry the detail in `details` and leave `field` empty. |
| 422 | `VERIFICATION_CODE_EXPIRED` | No code is valid: it is older than 10 minutes, already used, or was never sent. |
| 422 | `VERIFICATION_CODE_INVALID` | The code is wrong. The message says how many tries are left. |
| 422 | `VERIFICATION_TRIES_EXHAUSTED` | The code was wrong 3 times. It is burned. |
| 500 | `INTERNAL` | Something failed on Flume's side. |

Every code, with its fix: [API error codes](https://docs.tryflume.ai/api/errors.md).

Guide: [Verify a user](https://docs.tryflume.ai/guides/verify-a-user.md).
