Changelog
Fixed

Retrying a failed job needs a new idempotency key

Submit a retry with a new idempotency key when a job fails retryably. Reusing the spent key now returns 409 CONFLICT instead of the failed job.

API SDK MCP

An idempotencyKey on POST /jobs guarantees that a request sent twice creates one job. That guarantee held. What it did not do was say anything useful once the job it created had already failed.

Reusing the key returned the stored job, whatever state it was in. So a client that read error.retryable: true on a failed job, did exactly what that field asks, and resubmitted the identical request under the identical key got this back, forever:

{ "mode": "idempotent", "jobId": "job_abc123", "status": "failed" }

A 200. No new job, no error, nothing saying why the retry did nothing. The correct behaviour and the silent no-op looked the same from the outside, and the widening of the retryable set in dispatch failures set error.retryable to true made it easier to hit.

A key can only ever be attached to one job, so the retry cannot be granted under the old key. It is now refused out loud instead:

{
"error": {
"code": "CONFLICT",
"message": "Idempotency key \"order-4417\" is already bound to job job_abc123, which failed with a retryable error. Reusing the key cannot create a new job. Retry with a new idempotency key.",
"details": { "jobId": "job_abc123" }
}
}

Everything else replays exactly as before. A key whose job is still waiting, dispatched, or running returns that job. A key whose job completed returns the completed job. A key whose job failed terminally, an invalid input or an ffmpeg error, returns the failed job too, because resubmitting it reproduces the same failure and error.retryable already says so. Only the one case the API told you to retry now answers with a 409.

Every error code and what it means is in the error reference, and the job lifecycle these states belong to is in concepts: job.

Share