Why it matters
A create is the one request here that costs money. If your connection drops while the upload is finishing, you cannot tell from your side whether we accepted the recording — and a blind retry transcribes it twice, billing the minutes twice. That is what this header is for.
Sending a key
Put a unique string in Idempotency-Key on every POST /transcriptions. A UUID is ideal. It is optional, and strongly recommended — treat it as required in production code.
curl https://heartotext.com/api/public/v1/transcriptions \
-H "Authorization: Bearer $H2T_API_KEY" \
-H "Idempotency-Key: 9f1c2b7a-1f2e-4a0c-9f0b-2c1d3e4f5a6b" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/episode-42.mp3"}'Replays
Send the same key with the same body again and you get the stored answer — the same status, the same recording id — with one extra header. No second recording is created and nothing is billed twice.
HTTP/1.1 201 Created
Idempotency-Replayed: trueThis makes a retry safe to perform unconditionally after a timeout, a 5xx or a dropped connection. Your worker does not have to know whether the first attempt arrived.
Conflicts
- Same key, different body →
409 idempotency_key_reused. This almost always means a key is being reused where a new one was meant; it is a bug in the caller, and we refuse rather than guess which recording you wanted. - Same key while the first request is still running →
409 idempotency_request_in_progress. Wait a few seconds and ask again — this is the shape a slow upload takes when a second worker picks up the same job.
Scope and lifetime
- Keys are remembered for 24 hours. After that the same string is a fresh request and will create a second recording.
- A key is scoped to one API key. Two of your keys using the same string do not collide — and neither do two different accounts.
- Only
POST /transcriptionsuses it. The reads are already idempotent, and a delete answers the same way whether it ran once or twice.
Choosing a key
- Generate it where the job is created, store it with the job, and reuse it on every attempt. A key generated inside the retry loop protects nothing.
- A natural key works too —
podcast-42-episode-7— as long as it is unique per recording and you never change the body underneath it. - Up to 255 characters. Anything longer is a 422.
