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.
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: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 answers409 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 answers409 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.
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
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.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.