Idempotency-Key header and the retry is safe: we replay the original response instead of doing the work again.
Which calls accept it
Every call that creates something:
Updates (
PATCH, PUT) do not need a key. They set fields to the values you send, so sending the same update twice leaves the same result - retry them directly.
Choosing a key
Any string of 1 to 255 characters, unique to one logical operation. An identifier you already have is usually the best choice - the resource’s ownexternal_id, a batch reference, a job id.
Reuse the same key for every retry of that one operation. Do not reuse it for a different operation, and do not generate a fresh key per retry - a new key is a new operation, which is exactly what you are trying to avoid.
What happens on a retry
Same key, same request body. You get the original response back, withIdempotent-Replayed: true. The work is not repeated.
409 idempotency.key_conflict. The key is already bound to a different request, so we will not guess which one you meant. Use a new key.
Same key, original still in flight. 409 idempotency.in_progress. Wait a moment and send it again.
The original failed. Nothing is stored for a failed call, so the same key retries through and can succeed. You do not need a fresh key after an error.
Scope and lifetime
Keys are scoped to your API key and to the specific call, so the same key value used on two different endpoints, or by two different API keys, never collides. For calls authenticated with a tap session token, the scope is the tap. A stored response is replayable for 24 hours. Session-scoped entries expire with the session token instead, since the credential is gone by then.Replays are snapshots
A replayed response is the body we sent the first time, byte for byte. If the resource has changed since - issuance completed, a redirect URL was updated - the replay will not show it. That is what makes a replay safe, but it means a replay is not a way to poll. Read the resource directly for current state:Creating the same resource twice on purpose
The key identifies a request, not an intent. Two calls that send byte-identical bodies look like one operation to us. This comes up when pairing test chips:{"unit_id": "...", "is_test": true} carries nothing that distinguishes the first chip from the second, so reusing a key returns the first chip rather than creating another. When you mean to create two, send two different keys.
