Idempotency
An idempotency key makes a request safe to retry: however many times you send it, the object is created once and charged at most once.
Send a unique key in the Idempotency-Key header. A version 4 UUID works well.
Idempotency-Key: 7c9e2a44-1d0b-4f8e-b3a6-52e9d0c4f7a8Required on the requests that can use credits:
POST /v1/papersPOST /v1/submissions
Accepted on every other POST, and recommended there too, except four that ignore it:
POST /v1/papers/{id}/review_linksmakes a new link every time, because a link is a secret we never keep a copy of.POST /v1/api_key/revokeis safe to repeat as it is.POST /v1/webhook_endpointsandPOST /v1/webhook_endpoints/{id}/rotate_secretreturn the endpoint’s secret, which we never save with a response. If a create may have failed, list your endpoints before creating one again.
How it works:
- One key, one object. Generate a key when you decide to create an object, store it with your own record, and reuse it only to retry that same request.
- First success: we create the object and save the
2xxresponse against the key, in the same step. Retries get the saved response without the work being done again, with the headerIdempotent-Replayed: true. - Errors are not saved. After a
4xxor5xx, nothing was created, so you can fix the cause and retry with the same key. - Same key, different body:
409 idempotency_key_reused. Bodies are compared as parsed JSON, so key order and spacing don’t matter. - Retry while the first request is still running:
409 idempotency_request_in_progress. Wait a second and retry. - Replacements need a new key. If a paper or submission later fails and you create a replacement, use a new key. The old key would only return the failed object again.
- Keys are kept for 24 hours, separately per account and per mode. For protection beyond 24 hours, give papers an
external_id; a second paper with the same one gets409 external_id_taken. Submissions are protected by the one-per-student rule.
Actions that change state are safe to repeat without a key. Approving a paper that is already ready, releasing a submission that is already released and cancelling one that is already canceled all return 200 with the object unchanged.