Idempotency
The failure mode worth designing for is not a request that fails. It is a request that succeeds and never tells you — a dropped connection, a proxy timeout, a client killed a millisecond too early. You retry, because retrying is the only sensible thing to do, and you end up with two topics where you meant one.
Every write endpoint on this API requires an Idempotency-Key header for exactly that reason.
curl --request POST \
--url 'https://api.askylabs.com/v1/brands/{brand_id}/topics' \
--header 'Authorization: Bearer <api-key>' \
--header 'Idempotency-Key: 8f14e45f-ceea-467a-9c1f-4a2b0f2c9a71' \
--header 'Content-Type: application/json' \
--data '{"name":"Answer engine optimisation"}'How it behaves
Send a unique value per attempt, and reuse it when retrying that attempt. A UUID per logical operation is the usual choice. The key is yours to pick; we only ever compare it.
The first request with a given key runs normally and its outcome is stored. Any retry with the same
key replays that stored response verbatim — same status, same body, same request_id — and
carries Idempotency-Replayed: true. Nothing executes a second time.
| Situation | What you get |
|---|---|
| First use of the key | The request runs |
| Retry after the response was lost | 200 with the original body and Idempotency-Replayed: true |
| Retry while the first attempt is still running | 409 conflict. Wait and retry |
| Key reused for a different request | 400 invalid_request |
The first attempt returned 5xx | The key is released. Retrying gets a real attempt |
A key belongs to one request
A key is bound to the exact request it was first used with: same method, same path, same query string, same body, byte for byte. Reuse it for anything else and the API refuses rather than replaying an answer to a question you never asked.
This is stricter than it strictly needs to be — reordering the fields in your JSON counts as a different request — and that is deliberate. Being too strict costs you one clear error message. Being too loose returns the wrong answer and looks like it worked.
Retention
Keys are remembered for 24 hours. After that the record is pruned and the same key would start a fresh request, so do not rely on replay as a long-term record of what you did. Use Usage and monitoring for that.
Where it matters most
On Create topic the payoff is obvious: without a key, a retry creates a duplicate.
On Delete topic it is less obvious and just as valuable. A retry
after a lost response would otherwise return 404 for a topic that was in fact deleted
successfully, which reads as a failure and invites you to investigate something that already worked.
With a key, the retry replays the original counts.
What it does not do
Idempotency is not a lock on the resource. It stops the same request running twice; it does not
stop two different requests racing each other, and it does not make a delete reversible. If you are
about to remove a topic and its prompts, dry_run=true is the safeguard, not this.