---
title: "Users and verification"
description: "A user is one person on your platform. They verify their email with a 6-digit code, and Flume returns an access token you send on calls made for them."
url: https://docs.tryflume.ai/concepts/users
updated: 2026-11-11
---

# Users and verification

> A user is one person on your platform. They verify their email with a 6-digit code, and Flume returns an access token you send on calls made for them.

A **user** is one person on your platform. They **verify** their email once with a 6-digit code. Flume then links them to a Flume account and gives you a **user access token**.

## How do you identify a user?

You send `external_ref`: your own permanent id for the person.

- Keep it stable. Flume uses it to find the user's earlier searches and quotes.
- Never reuse it for another person.
- 1 to 128 characters.

## How does verification work from your own screens?

1. Call `POST /users/trigger-verification` with `external_ref` and `email`. Flume emails the code.
2. Show a code box. `masked_email` tells the user which inbox to check.
3. Call `POST /users/confirm-verification` with `email` and `code`.
4. Store `access_token` from the response. It is returned once.

Step by step with code: [Verify a user](https://docs.tryflume.ai/guides/verify-a-user.md).

## How does verification work in the iframe?

**Diagram: The first open: the user verifies, you get a token.** Happens once per user. You build no screen for it: the iframe asks for the code.

```mermaid
sequenceDiagram
  participant user as User
  participant iframe as Flume iframe
  participant page as Your page
  participant backend as Your backend
  participant flume as Flume servers
  user->>iframe: clicks Connect
  iframe->>flume: send a code
  flume->>user: email, 6 digits
  user->>iframe: types the code
  iframe->>flume: check the code
  iframe-->>page: flume:user_connected
  page->>backend: connect_code
  backend->>flume: POST /users/exchange-connect-code
  flume->>backend: access_token
  Note over backend: store the token
```

1. The iframe opened without a user access token, so it shows the user their name and email. The user clicks Connect.
2. The iframe asks Flume to email a code.
3. Flume emails a 6-digit code to the user. It is valid for 10 minutes.
4. The user types the code in the iframe.
5. The iframe checks the code with Flume. Any token you held for this user stops working now.
6. The iframe posts flume:user_connected with a connect code.
7. Your page sends the connect code to your backend, at once.
8. Your backend calls POST /users/exchange-connect-code. The code works once, within 120 seconds.
9. Flume returns the user access token, once.
10. Your backend checks external_ref, then stores the token over any older one. It sends it on the next POST /start-iframe-session.

1. You call `POST /start-iframe-session` without a user access token.
2. The iframe asks the user to verify, and checks the code itself.
3. Your page receives `flume:user_connected` with a `connect_code`.
4. Your backend calls `POST /users/exchange-connect-code` within 120 seconds.
5. Your backend checks `external_ref` in the response, then stores `access_token`.
6. Next time, send the token on `POST /start-iframe-session`. The iframe opens with the user already verified.

**Diagram: A later open, for a user whose token you hold.** No email step and no flume:user_connected. Lighter steps happen only when the stored token has stopped working.

```mermaid
sequenceDiagram
  participant user as User
  participant iframe as Flume iframe
  participant page as Your page
  participant backend as Your backend
  participant flume as Flume servers
  user->>page: clicks your button
  page->>backend: your route
  backend->>flume: POST /start-iframe-session
  flume->>backend: 401 USER_TOKEN_INVALID (not every time)
  Note over backend: delete the token (not every time)
  backend->>flume: POST /start-iframe-session (not every time)
  flume->>backend: iframe_url, session_uuid
  backend->>page: iframe_url, session_uuid
  page->>iframe: iframe.src
  iframe->>flume: launch code, once
  iframe-->>page: flume:iframe_ready
```

1. The user clicks your button for one item.
2. Your page asks your backend to start a session.
3. Your backend calls POST /start-iframe-session and sends the user access token it stored for this user.
4. Only when the token no longer works, because the user verified again or you revoked it: Flume answers 401 USER_TOKEN_INVALID.
5. Then your backend deletes the stored token.
6. Then your backend calls POST /start-iframe-session again without a token. The user verifies in the iframe, as on a first open.
7. Flume returns iframe_url and session_uuid.
8. Your backend returns those two values to your page.
9. Your page starts the ready timer, adds the message listener and sets the iframe src.
10. The iframe spends the launch code with Flume.
11. The iframe posts flume:iframe_ready and opens with the user already verified.

## What happens when a user verifies in the iframe?

A user has one working access token at a time. Each verification issues a new one and stops the one before.

| Moment | Effect on tokens |
|---|---|
| The user confirms the code in the iframe | Any token you hold for this user stops working. The iframe's own session keeps working |
| Your backend exchanges the connect code | Flume issues the user's new token and returns it to you |
| You never exchange the connect code | You hold no working token for this user |

What this means for your backend:

- **Always exchange the connect code**, at once, and overwrite the stored token.
- **Always send the token you hold** on `POST /start-iframe-session`. The iframe then opens verified, and no new token is issued.
- If you use both the API and the iframe, expect API calls to return `401` [`USER_TOKEN_INVALID`](https://docs.tryflume.ai/api/errors.md#user_token_invalid) between the user's verification in the iframe and your exchange. Retry them with the new token.
- Another iframe that was open for the same user ends at its next call. It posts `flume:close_iframe` with reason `expired`.

## What are the rules for a code?

| Rule | Value |
|---|---|
| Length | 6 digits |
| Valid for | 10 minutes |
| Wrong tries before it is burned | 3 |
| Wait before a resend | 30 seconds |
| Codes per mailbox | 5 in 24 hours |
| Codes per user IP address, when you send `X-Flume-Client-IP` | 30 in 1 hour |
| Codes per partner | 500 per UTC day |

- A resend replaces the earlier code.
- `X-Flume-Client-IP` is optional. When you send the user's IP address, Flume also limits codes per IP address. Without it, only the limits per mailbox and per partner apply.

## What happens to the user's Flume account?

`account` in the response tells you.

| Value | Meaning |
|---|---|
| `CREATED` | Flume opened a new Flume account with the details you sent |
| `LINKED` | The email already had a Flume account. Flume linked it and wrote nothing to it |

Searches, quotes and projects you create for the user appear in that account.

## How long does a user access token last?

It has no expiry date. It stops working when one of these happens.

| Event | Result |
|---|---|
| You call `DELETE /users/{external_ref}/token` | The token stops at once. Iframe sessions opened with it end at their next call |
| The user confirms a code again, on your screens | The response holds a new token. The old one stops |
| The user verifies in the iframe | The old token stops. The new one comes from the connect code exchange |
| You call trigger-verification with a new email for a known `external_ref` | The user verifies the new address. The old token stops |

A stopped token returns `401` [`USER_TOKEN_INVALID`](https://docs.tryflume.ai/api/errors.md#user_token_invalid). Verify the user again to get a new one.

## How do you update a user's details?

Call `POST /users/trigger-verification` again with the same `external_ref`.

- A field you leave out is kept.
- A field you send as `null` is cleared.
- The call also sends a fresh code.

## What can go wrong?

| Situation | Status | Code |
|---|---|---|
| Wrong code | 422 | `VERIFICATION_CODE_INVALID`. The message says how many tries are left |
| Third wrong code | 422 | `VERIFICATION_TRIES_EXHAUSTED`. Send a new code |
| Code expired, used or never sent | 422 | `VERIFICATION_CODE_EXPIRED`. Send a new code |
| Resend too soon, or a mailbox or IP limit | 429 | `VERIFICATION_RATE_LIMITED`. Wait `Retry-After` seconds |
| The email could not be sent | 502 | `VERIFICATION_EMAIL_FAILED`. Retry |
| The email belongs to a closed Flume account | 422 | `USER_ACCOUNT_CLOSED`. On every other call this code is a `403` |
| No user of yours with that email awaits a code | 404 | `NOT_FOUND` |

Every code is in the [error catalog](https://docs.tryflume.ai/api/errors.md).
