Repeat a request without doing the work twice

The Idempotency-Key header — which requests require it, what a repeated request is answered with, and how to choose a key.

4 min read

A request that runs a flow or moves credits carries a key of your own in the Idempotency-Key header. The key names one piece of work. A request repeated under its key is answered instead of being done again, so a retry after a timeout or a lost connection runs nothing twice and charges nothing twice.

Where the key is required

The header is required by the requests that spend or move something:

  • a request that runs a flow — POST /v1/operations/execute;

  • a movement of a customer's credits — POST /v1/customers/{externalUserId}/topup, POST /v1/customers/{externalUserId}/charge and POST /v1/customers/{externalUserId}/adjust.

The page of each resource marks these operations. The other operations do not read the header.

  • A request without the header, or with an empty one, is refused with 400 and the code idempotency_key_required. Nothing runs.

  • A key is at most 190 characters. A longer one is refused with 422 and the code invalid_request, before anything is held, charged or credited.

What a repeat is answered with

A repeat is the same request under the same key. What it is answered with depends on what the first request did.

RequestA repeat under its key
A flow runThe execution the first request opened, as it is at that moment, with replayed set to true: 202 while the execution is not finished, 200 with its outcome once it is. Nothing runs again
A credit movementThe first answer, for 24 hours. After that, the account as it is now. The credits move once

An answer with an error is kept in some cases and not in others.

  • A flow run keeps its key once the execution exists. An error that came before that — flow_not_found, a body that fails validation — leaves the key free. An error that came after it, such as invalid_flow_input, closes the execution, and a repeat reads it: 200, the status failed and the code.

  • A credit movement keeps only an answer that succeeded. A refused one can be sent again under the same key.

Another request under a used key

A key that already names a piece of work refuses another one. The answer is 409 with the code idempotency_conflict, and nothing runs.

409 Conflict
{
  "error": {
    "code": "idempotency_conflict",
    "message": "The Idempotency-Key was already used with a different request body",
    "request_id": "6b2e9c4a-1d7f-4e3b-8a5c-0f9d2e7b4c61"
  }
}

What counts as another request depends on what the key names.

  • For a flow run, the body is compared byte for byte. Another input, another context, another execution_mode — even the same fields in another order — make another request.

  • For a credit movement, any other body is another request while the first answer is kept. After that a credit movement is compared by its customer, credit type, kind and amount.

Choose a key

  • Make the key from the piece of work: an order and its step (order-42:email), or the id of a job of your queue. Every repeat of that work then carries the same key, and the next piece of work gets a new one.

  • A key made at random for each attempt protects only that attempt. A repeat your application sends under a new key is new work, and it is paid for again.

  • A key belongs to the project. Two projects may use the same key, and inside a project each piece of work has a key of its own.

  • To run the work again after a request failed — with another input or another model — send the corrected request under a new key.

Where to go next

  • Idempotency keys — the keys the SDKs send, and a key made from the job of a queue.

  • Operations — the request that runs a flow, and the model calls it made.

  • Error codes — what the codes of a refused key mean, and what to do about each.

  • Customers and credits — the keys of top-ups, charges and adjustments.

  • Quickstart — a first request with a key of its own.

  • Overview — the other conventions of a request.

  • Executions — read the execution a key belongs to, and its trace.