Idempotency keys and safe retries
What to do when a submit response is lost: find the existing job, retry safely, and avoid creating a second one.
A lost response is an unknown outcome
Suppose your agent's submit call times out after the server receives the request. The timeout does not prove that admission failed. Keep the original request body and key, read the existing job, and continue from the identity you saved. Do not create a second job just to discover what happened.
- Keep the original request body, key, account, quote and quote-bound job ID together in your own request record.
- Read
GET /v1/jobs/{id}using that job ID. In the TypeScript client, the method isgetJob(jobId). - If the job is present, follow its current state. Do not submit again merely to check progress.
- If you cannot establish the outcome, preserve the uncertainty. A bounded retry uses the original body and key while the quote remains valid.
A failed read is not proof that the job does not exist. If read-back remains unavailable or the quote has expired, pause and reconcile through support. Share safe job and request references, not credentials or input content.
The Compute paper's recovery section explains why recovery keeps the original job identity and limits the state that changes.
Keep one identity for one request
Save your request body and idempotency key before your agent submits work. Save the prepared task brief with that request: frozen instructions and rubric, input snapshot, operation and output schema, stable IDs, required evidence and acceptance rule. An exact recovery keeps those identities unchanged. Changing any of them is revised work, even when the same source file or item IDs are reused.
Every Compute REST POST requires an Idempotency-Key header. Use 8–128 ASCII characters: a letter
or digit first, then letters, digits, ., _, :, or -. For a job, the header must match the
body's idempotency_key and the signed quote's key.
Keep retry kinds separate
Distinguish these four repeat or change cases:
| Repeat | Identity and budget | What it means |
|---|---|---|
| Transport or upload retry | Same body, artifact, attempt identity and idempotency key; outside compute retry budget | Lithi reconciles or resends the same request after a network fault, interrupted upload or lost acknowledgement. It is not a new attempt or a second charge. |
| Bounded compute retry | Same logical job, with a sequential new attempt and fence bounded by retry policy, exposure, capacity, budget and deadline | A lease, executor or transient native failure may receive a replacement attempt without another customer approval. The original scope and maximum stay fixed, owner exposure still counts, and concurrent hedges are rejected. |
| Accepted-result recovery | Same accepted result, artifact and request identity; outside compute retry budget | A finalizer, outbox or delivery repair replays the accepted result or durable state. It does not recompute the work. |
| Deliberately revised work | New body, quote, approval and idempotency key | You changed the input, policy, destination, deadline or desired operation. This is new customer-authorized work. |
You approve the admitted job or a covering bounded policy once. human_approve_each_run
means one customer run, not every physical attempt. No human approves a retry, redrive or repair
while it remains within that admitted scope. The retry policy controls compute attempts; it does not
make an uncertain transport response safe to replace with another job. Reconcile the original identity
first.
Choose the action that matches the situation
| Situation | Action |
|---|---|
| Same intent, retryable error, unchanged valid request | Repeat the identical body with the same key. |
| Admission is already known | Read the existing job and its results. |
| Submission outcome is unknown | Reconcile the original job; never rotate its key to escape uncertainty. |
| Body changed | Resolve the original intent first. Quote and approve the revised work separately. |
| Quote expired | Check whether the original job was admitted. Obtain a fresh quote for work that still needs submission. |
| Deliberately new work | Record the new intent, obtain its quote and approval, and use a new key. |
| Only delivery failed | Recover delivery of the existing result, not a new compute run. |
Preserve the body as well as the key
Replaying a job means keeping its artifact, operation, verification, deadline, destination, and correlation fields unchanged. Keep the brief's instructions, rubric and output schema fixed as well. Rebuilding the body with a new timestamp is not an exact replay.
The TypeScript client's bounded retries preserve the serialized body and key within one call. Across a process restart, your application must restore its saved request. Do not assume a newly generated key identifies the previous call.
A changed-body refusal is not permission to keep trying different keys. Correct the original body for an exact replay, or deliberately authorize new work after reconciliation.
Keep quote expiry separate from retry identity
An expired quote cannot authorize a new admission. A fresh quote may change fields bound to the request, even when your business goal is unchanged. Review those changes and obtain the required approval before submitting again. See Compute availability and access approval when the operation or account fit has changed.
Do not assume an idempotency record lives forever. Keep your own request-to-job mapping and reconcile old attempts before creating replacements. A fresh quote after expiry may change the forecast, capacity or deadline terms, so it is a new approved intent even when the business goal is similar.
Confirm recovery without duplicating work
Successful recovery identifies the same quote-bound job, not a second job that happens to use the same input. Compare the returned job ID and follow its state and result references.
Physical execution attempts are not automatically accepted customer billing units. Retries are not new billing units: the quote and its usage policy define which accepted unit contributes to customer usage; use the usage and receipt records for that decision. A replacement attempt can remain unbilled as a duplicate while its owner exposure still counts against the approved policy.
If the result is retained and authorised, zero account credit does not by itself block its recovery or delivery-link renewal; retention and access permission still control what you may read.
When only a destination write failed, use delivery recovery without rerunning compute. Retrieve or redeliver the same accepted result, keep its identity intact, and do not submit the input again. For file and digest checks, continue with Read and verify Compute result files.