---
title: "API error codes"
description: "Every Flume Partner API error code with its HTTP status, what it means and how to fix it. All errors share one JSON envelope with a stable code."
url: https://docs.tryflume.ai/api/errors
updated: 2026-11-11
---

# API error codes

> Every Flume Partner API error code with its HTTP status, what it means and how to fix it. All errors share one JSON envelope with a stable code.

Every error from the Flume Partner API is JSON in one envelope, with a stable `code`. This page lists every code, its HTTP status, what it means and how to fix it.

## The error envelope

```json
{
  "error": {
    "code": "SKU_NOT_IN_SEARCH",
    "message": "This product was not a result of that search. Use the search_session_uuid of the search (or the page) that returned it.",
    "field": "sku_uuid",
    "request_id": "e19b8889-a63b-4000-9dbe-ffd5b9157bd5"
  }
}
```

| Field | Type | Notes |
|---|---|---|
| `code` | string | Stable. Branch on it |
| `message` | string | For a person. It can change |
| `field` | string or null | The request field at fault, as a dotted path |
| `request_id` | string | Quote it to Flume support. Also in the `X-Request-Id` header |
| `details` | object | Sometimes present, with more about a refusal |

## All codes

| Code | Status |
|---|---|
| [`PARTNER_KEY_INVALID`](#partner_key_invalid) | 401 |
| [`EMBED_TOKEN_INVALID`](#embed_token_invalid) | 401 |
| [`USER_TOKEN_INVALID`](#user_token_invalid) | 401 |
| [`USER_NOT_CONNECTED`](#user_not_connected) | 403 |
| [`USER_ACCOUNT_CLOSED`](#user_account_closed) | 403 |
| [`CONNECT_CODE_INVALID`](#connect_code_invalid) | 401 |
| [`VERIFICATION_CODE_INVALID`](#verification_code_invalid) | 422 |
| [`VERIFICATION_TRIES_EXHAUSTED`](#verification_tries_exhausted) | 422 |
| [`VERIFICATION_CODE_EXPIRED`](#verification_code_expired) | 422 |
| [`VERIFICATION_RATE_LIMITED`](#verification_rate_limited) | 429 |
| [`VERIFICATION_EMAIL_FAILED`](#verification_email_failed) | 502 |
| [`VALIDATION_FAILED`](#validation_failed) | 422 |
| [`NOT_FOUND`](#not_found) | 404 |
| [`SESSION_NOT_FOUND`](#session_not_found) | 404 |
| [`SKU_NOT_IN_SEARCH`](#sku_not_in_search) | 422 |
| [`IMAGE_REQUIRED`](#image_required) | 400 |
| [`IMAGE_TOO_LARGE`](#image_too_large) | 413 |
| [`IMAGE_UNSUPPORTED`](#image_unsupported) | 422 |
| [`SEARCH_UNAVAILABLE`](#search_unavailable) | 503 |
| [`BELOW_MINIMUM_ORDER`](#below_minimum_order) | 400 |
| [`NOT_INSTANTLY_QUOTABLE`](#not_instantly_quotable) | 400 |
| [`QUOTE_NOT_READY`](#quote_not_ready) | 422 |
| [`IDEMPOTENCY_KEY_REQUIRED`](#idempotency_key_required) | 400 |
| [`IDEMPOTENCY_KEY_REUSED`](#idempotency_key_reused) | 422 |
| [`IDEMPOTENCY_IN_PROGRESS`](#idempotency_in_progress) | 409 |
| [`KEY_LIMIT_REACHED`](#key_limit_reached) | 409 |
| [`KEY_ROTATION_RATE_LIMITED`](#key_rotation_rate_limited) | 429 |
| [`LAST_ACTIVE_KEY`](#last_active_key) | 409 |
| [`RATE_LIMITED`](#rate_limited) | 429 |
| [`DAILY_LIMIT_REACHED`](#daily_limit_reached) | 429 |
| [`INTERNAL`](#internal) | 500 |

## Responses outside the envelope

| Response | When |
|---|---|
| `403` with no JSON body | On live, a request from an unsupported country. See [Environments](https://docs.tryflume.ai/concepts/environments.md) |
| `429` with no JSON body and no `Retry-After` | On live, a network-level limit per IP address. Back off and retry |
| No response | A network timeout. Retry a write with the same `Idempotency-Key` |

## Credentials

### `PARTNER_KEY_INVALID`

- **Status:** 401.
- **Returned by:** Every call.
- **Meaning:** The partner key is missing, unknown, revoked, expired, or from the other environment. On live, also a platform Flume has not enabled yet.
- **Fix:** Check the header name `X-Flume-Partner-Key` and the environment. Rotate the key if it was revoked.

### `EMBED_TOKEN_INVALID`

- **Status:** 401.
- **Returned by:** Every call.
- **Meaning:** You sent an `Authorization` header and no `X-Flume-Partner-Key`. Flume read the call as one from its own iframe.
- **Fix:** Send the partner key in `X-Flume-Partner-Key`. Never send it in `Authorization`.

### `USER_TOKEN_INVALID`

- **Status:** 401.
- **Returned by:** Calls that read `X-Flume-User-Token`.
- **Meaning:** The user access token is unknown, revoked, another partner's, or replaced by a later verification. A verification in the iframe replaces it too.
- **Fix:** Delete the stored token. Exchange the connect code if you have one. Otherwise verify the user again.

### `USER_NOT_CONNECTED`

- **Status:** 403.
- **Returned by:** Calls made for a user.
- **Meaning:** No `X-Flume-User-Token` header was sent.
- **Fix:** Send the user's access token. If you hold none, verify the user first.

### `USER_ACCOUNT_CLOSED`

- **Status:** 403 on calls made for a user. 422 with `field: email` from the verification calls.
- **Returned by:** Calls made for a user, and the two verification calls.
- **Meaning:** The user's email belongs to a Flume account that Flume has closed.
- **Fix:** Do not retry. The user contacts Flume.

### `CONNECT_CODE_INVALID`

- **Status:** 401.
- **Returned by:** `POST /users/exchange-connect-code`.
- **Meaning:** The connect code is used, older than 120 seconds, unknown, or another partner's.
- **Fix:** Exchange each code once, at once. The user verifies again on the next open.

## Verification

### `VERIFICATION_CODE_INVALID`

- **Status:** 422.
- **Returned by:** `POST /users/confirm-verification`.
- **Meaning:** The code is wrong. The message says how many tries are left.
- **Fix:** Let the user type it again.

### `VERIFICATION_TRIES_EXHAUSTED`

- **Status:** 422.
- **Returned by:** `POST /users/confirm-verification`.
- **Meaning:** The code was wrong 3 times. It is burned.
- **Fix:** Send a new code.

### `VERIFICATION_CODE_EXPIRED`

- **Status:** 422.
- **Returned by:** `POST /users/confirm-verification`.
- **Meaning:** No code is valid: it is older than 10 minutes, already used, or was never sent.
- **Fix:** Send a new code.

### `VERIFICATION_RATE_LIMITED`

- **Status:** 429, with `Retry-After`.
- **Returned by:** `POST /users/trigger-verification`.
- **Meaning:** A resend came too soon, or the mailbox reached its limit. When you send `X-Flume-Client-IP`, the limit per IP address applies too.
- **Fix:** Wait `Retry-After` seconds.

### `VERIFICATION_EMAIL_FAILED`

- **Status:** 502.
- **Returned by:** `POST /users/trigger-verification`.
- **Meaning:** The email could not be sent.
- **Fix:** Retry with backoff.

## Requests

### `VALIDATION_FAILED`

- **Status:** 422. 400 for an `Idempotency-Key` over 255 characters. 405 for a wrong HTTP method.
- **Returned by:** Every call.
- **Meaning:** 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.
- **Fix:** Fix the field. Request bodies are strict: remove fields the API does not list.

### `NOT_FOUND`

- **Status:** 404.
- **Returned by:** Calls that name a product, quote, key, save or user. Also any unknown path.
- **Meaning:** The thing does not exist, or is not this user's. A search id that is not a UUID also returns this code.
- **Fix:** Check the id and that it belongs to this user.

### `SESSION_NOT_FOUND`

- **Status:** 404.
- **Returned by:** Calls that take `search_session_uuid`.
- **Meaning:** The search does not exist, or is another user's.
- **Fix:** Use a `search_session_uuid` from one of this user's searches.

### `SKU_NOT_IN_SEARCH`

- **Status:** 422.
- **Returned by:** `POST /request-instant-quote`, `POST /save-item`, `POST /deep-links`, `POST /sales-requests`.
- **Meaning:** The product was not a result of the search you named.
- **Fix:** Use the `search_session_uuid` of the search, or the page, that returned the product.

## Search

### `IMAGE_REQUIRED`

- **Status:** 400.
- **Returned by:** `POST /search`.
- **Meaning:** The request has no `image` part, or the part is empty.
- **Fix:** Send the photo as the `image` part of `multipart/form-data`. On a retry, read the file again.

### `IMAGE_TOO_LARGE`

- **Status:** 413.
- **Returned by:** `POST /search`.
- **Meaning:** The photo is over 6,000,000 bytes.
- **Fix:** Resize or compress the photo.

### `IMAGE_UNSUPPORTED`

- **Status:** 422.
- **Returned by:** `POST /search`.
- **Meaning:** The file is not a JPEG, PNG or WebP, or it is over 50 megapixels. Flume reads the bytes, not the file name.
- **Fix:** Convert or resize the photo.

### `SEARCH_UNAVAILABLE`

- **Status:** 503.
- **Returned by:** `POST /search`.
- **Meaning:** Search is briefly unavailable. The call is not counted against your limit.
- **Fix:** Retry with backoff.

## Quotes and saves

### `BELOW_MINIMUM_ORDER`

- **Status:** 400.
- **Returned by:** `POST /request-instant-quote`.
- **Meaning:** The quantity is under the product's minimum order. The message states the minimum.
- **Fix:** Raise `quantity_sqft`. Read `minimum_order_sqft` on the product's shipping options.

### `NOT_INSTANTLY_QUOTABLE`

- **Status:** 400.
- **Returned by:** `POST /request-instant-quote`.
- **Meaning:** Flume's sales team prices this product. There is no instant quote.
- **Fix:** Check `price_on_request` first. File a sales request.

### `QUOTE_NOT_READY`

- **Status:** 422.
- **Returned by:** `POST /save-item`, `POST /deep-links`.
- **Meaning:** The quote is not `READY`. On `POST /save-item` it also covers a quote that expired, is not this user's, lacks the option you named, or is under that option's minimum order.
- **Fix:** Request a new quote, then try again.

## Idempotency

### `IDEMPOTENCY_KEY_REQUIRED`

- **Status:** 400.
- **Returned by:** `POST /request-instant-quote`, `POST /keys`.
- **Meaning:** The `Idempotency-Key` header is missing.
- **Fix:** Send one key per user action.

### `IDEMPOTENCY_KEY_REUSED`

- **Status:** 422.
- **Returned by:** Calls that take `Idempotency-Key`.
- **Meaning:** The key was already used with a different body.
- **Fix:** Use a new key for a new request.

### `IDEMPOTENCY_IN_PROGRESS`

- **Status:** 409, usually with `Retry-After: 2`.
- **Returned by:** Calls that take `Idempotency-Key`.
- **Meaning:** The first call with this key is still running.
- **Fix:** Wait 2 seconds. Retry with the same key.

## Keys

### `KEY_LIMIT_REACHED`

- **Status:** 409.
- **Returned by:** `POST /keys`.
- **Meaning:** You already hold the maximum number of active keys.
- **Fix:** Revoke a key you no longer use.

### `KEY_ROTATION_RATE_LIMITED`

- **Status:** 429, with `Retry-After: 86400`.
- **Returned by:** `POST /keys`.
- **Meaning:** You reached the number of keys you can issue in 24 hours. Your current key keeps working.
- **Fix:** Wait, then rotate. For a leaked key on live, contact Flume.

### `LAST_ACTIVE_KEY`

- **Status:** 409.
- **Returned by:** `DELETE /keys/{key_id}`.
- **Meaning:** This is your only working key.
- **Fix:** Issue a replacement first.

## Limits and server

### `RATE_LIMITED`

- **Status:** 429, with `Retry-After`.
- **Returned by:** Calls with a daily limit.
- **Meaning:** Today's limit for this call is used up. `Retry-After` is the number of seconds until 00:00 UTC.
- **Fix:** Stop that call until then. See [Limits](https://docs.tryflume.ai/concepts/limits.md).

### `DAILY_LIMIT_REACHED`

- **Status:** 429, with `Retry-After`.
- **Returned by:** `POST /search`, `POST /request-instant-quote`.
- **Meaning:** This user's own daily limit of searches or instant quotes is used up. Flume sets this limit per partner account, and it is off by default. `details` holds `kind`, `limit`, `reset_at` and `retry_after_s`.
- **Fix:** Tell the user when they can try again, from `details.reset_at`. Other users are not affected. See [Limits](https://docs.tryflume.ai/concepts/limits.md#is-there-a-limit-per-user).

### `INTERNAL`

- **Status:** 500.
- **Returned by:** Every call.
- **Meaning:** Something failed on Flume's side.
- **Fix:** Retry with backoff. Keep the same `Idempotency-Key`. Send `request_id` to Flume if it persists.

## What is not an error

- `POST /request-instant-quote` returns `200` with `status: FAILED` when it could not price. Read `failure_reason`. See [Get an instant quote](https://docs.tryflume.ai/guides/get-an-instant-quote.md).
- `POST /detect-category` returns `200` for every verdict, including `error`.

## See also

- [Handle errors](https://docs.tryflume.ai/guides/handle-errors.md): what to retry
- [Limits](https://docs.tryflume.ai/concepts/limits.md)
