Skip to main content
A write can succeed on our side while the response never reaches you: a timeout, a dropped connection, a restart of your own process. Without help, you cannot tell whether to send it again. An idempotency key settles it. Send the same key with the retry, and the API returns the first attempt’s response instead of doing the work a second time.

The Idempotency-Key header

Every write accepts an optional Idempotency-Key header: every POST, PUT, PATCH and DELETE, including the webhook management endpoints.
string
A string of 1 to 255 characters that you choose. Use one key per logical operation. A version 4 UUID is a good choice.
A key outside those bounds answers 400 validation_error. The entry in details has path set to Idempotency-Key and code set to invalid_header. Nothing is remembered for that request. Without the header, every request runs.

Replay: the same key and the same request

When a request arrives with a key the API has already seen for the same request, nothing runs again. The API returns the stored status and body of the first attempt, and adds one header:
Other headers are left out of this example. Idempotent-Replayed: true appears only on a replay. The X-Request-Id is new on every response. When the stored response is an error, the request_id in its body is replaced with the new one, so the header and the body always agree. What is stored, and what is not: A scan request is the one place where a 402 or a 403 comes from processing the request: 402 no_manual_scans, and 403 tier_not_entitled for a plan without on-demand scans. Both are stored and replay.
A stored 4xx replays for as long as the key is remembered. Once you have fixed the cause, whether in the request or on the account, send the request under a new key. That includes the two conflicts that clear by themselves, 409 prompt_writes_busy and 409 delivery_in_flight: wait a moment, then retry under a new key, because the same key replays the 409. The one conflict to retry with the same key is 409 idempotency_key_in_flight.

Reuse: the same key and a different request

A key belongs to one request. The API compares the method, the path including its ids, the organisation and the JSON body. The order of keys in the JSON does not matter. If any of those differ, the API answers 409 idempotency_key_reuse and runs nothing. Deleting prompt A and then prompt B under one key is a reuse, even though both requests have an empty body.

In flight: two requests at once

If two requests with the same key arrive together, exactly one runs. The other answers 409 idempotency_key_in_flight. Retry it shortly with the same key. Once the first attempt has finished, the retry replays its response, or runs the request if that attempt ended in a 5xx. The response is stored before it is sent. If you hold a 2xx, a retry can never be told in_flight. Disconnecting does not cancel a write. If your client times out and drops the connection, the request still runs to completion and its response is stored as usual, so a retry with the same key replays it.
If idempotency_key_in_flight persists far beyond the time the request normally takes, the first attempt was interrupted before its response could be stored, and that key will keep answering in_flight. Send the request again under a new key. That is safe for the prompt sync, which is declarative. For a create that did complete before the interruption, the new request answers 409 with the id of the existing resource.

Scope and lifetime

Keys are scoped to your API key. Two API keys can use the same string without colliding. It also means a retry must be sent with the same API key as the first attempt, or it runs as a new request. Keys are remembered for a limited time. Treat a key as single-use for one logical operation: generate it when you decide to make the write, reuse it for every retry of that write, and never use it again afterwards.
POST /orgs/{orgId}/brands/{brandId}/scan-requests returns a field named idempotency_key. That is the scan queue’s own key for the brand and the day. It is not the Idempotency-Key header, and the two do not interact.

Rotating a webhook secret

POST /webhook-endpoints/{endpointId}/rotate-secret shows the new secret once. If you retry it with the same key, the API replays the first response, including that secret, and rotates nothing. If you retry it without a key, every attempt rotates the secret again, and the secret from a response you never received is gone. Always send an Idempotency-Key with this call.

Create a prompt, safely

Each sample creates a prompt for the own brand Hotel Aurora. It generates one key and sends it with every attempt.
If the prompt already exists, the API answers 409 conflict with details.existing_id. That response is stored too, so a retry replays it. See Errors for every code a write can return, and Rate limits and quotas for Retry-After.