Idempotency
A request can fail after we acted but before you got the response - a dropped connection, a proxy timeout, a phone losing signal. Retrying is the right instinct, but without protection a retry creates a second debate or pays for a second video ingest.
Send a unique Idempotency-Key on anything that creates or spends:
curl -sX POST $RTFC/debates \
-H "X-Auth-Token: $TOKEN" -H 'Content-Type: application/json' \
-H "Idempotency-Key: 8f14e45f-ea6b-4b1f-9f2a-6de9a3f1b0c2" \
-d '{"title": "…"}'Retry with the same key and you get the original response replayed, with Idempotency-Replayed: true. Nothing happens twice.
Where it applies
POST /debates, POST /claim, POST /provision/user, and the upload-media / upload-youtube / upload-transcript endpoints.
Reads do not need it. Neither do PATCH/PUT/DELETE on a specific id, or voting - those are already idempotent by construction.
Multipart file uploads (upload-audio, upload-media/blob) are not covered: protecting them would mean buffering an entire audio file in memory to fingerprint it. The expensive media path that matters - upload-media, which triggers a download and transcription - takes a small JSON body and is covered.
Rules
| Situation | Result |
|---|---|
| Same key, same body, first call finished | Original response replayed |
| Same key, same body, first call still running | 409 idempotency_conflict - wait and retry |
| Same key, different body | 422 idempotency_key_reused |
| Key on a request that failed | Key released - a real retry works |
That last row matters: a request that 500s does not poison its key. Retrying after a failure is exactly when you need this to work.
Keys are scoped to your account and kept for 24 hours.
Use a fresh UUID per logical operation - not per HTTP attempt. All retries of the same operation must share one key, which is the whole point.