---
title: "Idempotency"
description: "Send an Idempotency-Key header so a retried write returns the original answer instead of running twice. Instant quotes and key rotation require it."
url: https://docs.tryflume.ai/concepts/idempotency
updated: 2026-11-11
---

# Idempotency

> Send an Idempotency-Key header so a retried write returns the original answer instead of running twice. Instant quotes and key rotation require it.

An **idempotency key** makes a retry safe. Send the same `Idempotency-Key` header again and Flume returns the first answer instead of doing the work twice.

## Which calls use it?

| Call | `Idempotency-Key` | Without it |
|---|---|---|
| `POST /request-instant-quote` | Required | `400` [`IDEMPOTENCY_KEY_REQUIRED`](https://docs.tryflume.ai/api/errors.md#idempotency_key_required) |
| `POST /keys` | Required | `422` [`VALIDATION_FAILED`](https://docs.tryflume.ai/api/errors.md#validation_failed) |
| `POST /sales-requests` | Optional | The call runs every time |
| `POST /deep-links` | Optional | The call runs every time |
| `POST /save-item` | Not used. `save_uuid` in the body does the same job | |
| `POST /search` | Not used. Every call is a new search | |

## What is a valid key?

- Any string of 1 to 255 characters. A UUID works well.
- Use a new key for each user action. Reuse it only to retry that same action.
- A key longer than 255 characters is a `400` `VALIDATION_FAILED`.

```sh
curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/deep-links" \
  -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \
  -H "X-Flume-User-Token: $FLUME_USER_TOKEN" \
  -H "Idempotency-Key: 0d3c1a52-6f0e-4d0b-9a57-1c2e8b7f4a10" \
  -H "Content-Type: application/json" \
  -d '{"search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f"}'
```

## What happens on a retry?

| Retry | Result |
|---|---|
| Same key, same body, first call finished | The stored answer, with the header `Idempotent-Replay: true` |
| Same key, same body, first call still running | `409` [`IDEMPOTENCY_IN_PROGRESS`](https://docs.tryflume.ai/api/errors.md#idempotency_in_progress), usually with `Retry-After: 2` |
| Same key, different body | `422` [`IDEMPOTENCY_KEY_REUSED`](https://docs.tryflume.ai/api/errors.md#idempotency_key_reused) |

## What is stored?

- Flume keeps a key for 24 hours.
- A key is scoped to your platform and to one endpoint.

| First answer | Stored | A retry with the same key |
|---|---|---|
| A success | Yes | Replays it |
| A `4xx` refusal | Yes | Replays it. Fix the request and use a new key |
| A `5xx` error | No | Runs the work again |
| A `429` with `Retry-After` | No | Runs the work again |
| A quote that is still pricing | No | Runs again, and counts against the daily quote limit again |

> **Poll, do not retry:** When a quote is still pricing, poll `GET /quotes/{quote_uuid}`. Polling is free. A retry of `POST /request-instant-quote` uses another unit of your daily limit.

## What is never replayed?

A replay never repeats a secret.

| Call | Field on a replay |
|---|---|
| `POST /keys` | `secret` is `null` |
| `POST /deep-links` | `url` is `null` |

## How does save_uuid work?

`POST /save-item` uses `save_uuid` from the body instead of the header.

- You choose `save_uuid`. Use a new UUID per save.
- The same `save_uuid` returns the stored payload, unchanged.
- Another user's `save_uuid` is a `409` `SAVE_CONFLICT`.

## Why does search have no idempotency?

Every `POST /search` call is a new search. Page 2 included.

- Each call spends one unit of the daily search limit.
- Each call returns a new `search_session_uuid`.
- To read a search again without spending a unit, call `GET /searches/{search_session_uuid}`.
