Skip to main content

Idempotency

The API supports idempotency so you can repeat mutations safely without executing the same operation twice. The classic case: a POST /api/v1/charges drops due to a network error and you don't know whether the charge was created — resend with the same key and the original response comes back, without creating a second charge.

How to use it

Send the header on any POST, PUT, or PATCH:

X-Idempotency-Key: <key>

The key is generated on your side — we recommend UUID v4. Maximum of 255 characters.

curl -i \
-H "Authorization: Bearer $DUNNING_TOKEN" \
-H 'Content-Type: application/json' \
-H 'X-Idempotency-Key: 8c0f5d6e-3f8b-4cb5-9a47-d8f5b15e9b21' \
-d '{ "personId": "cmd8k1x2y0009cd4e", "amount": 1500.00, "dueDate": "2026-08-10" }' \
-X POST 'https://api.dunning.kobana.com.br/api/v1/charges'

How it works

  1. The key is reserved atomically, scoped to your organization (different organizations can use the same key without conflict).
  2. The server computes the request's fingerprint: a hash of method + path + raw body.
  3. The handler runs and the response (status, body, headers) is recorded against the key — including when it's an error.
  4. A new request with the same key within 24 hours receives the original response verbatim, with the extra header:
X-Idempotent-Replay: true

After 24 hours the key expires and the same key produces a fresh execution.

Conflicts

ScenarioStatuscode
Same key with a different method/path/body409IDEMPOTENCY_KEY_REUSED
Another request with the same key in flight409IDEMPOTENCY_REQUEST_IN_PROGRESS
Empty key or more than 255 characters400VALIDATION_ERROR

Example conflict response:

{
"message": "X-Idempotency-Key já foi usado com parâmetros diferentes. Reenvie os parâmetros originais ou gere uma nova chave",
"code": "IDEMPOTENCY_KEY_REUSED"
}

On IDEMPOTENCY_REQUEST_IN_PROGRESS, wait a moment and resend the same request: either the first execution finishes and you get the replay, or the reservation was released and your attempt runs.

When the result is not recorded

It's safe to resend when the handler never got to run:

  • The request was rejected before the handler (invalid JSON, missing authentication).
  • The handler threw an exception — the reservation is released so the next retry runs clean.

Accepted methods

MethodAccepts X-Idempotency-Key?
POST / PUT / PATCH✅ Yes
GET / DELETE❌ No — already idempotent by definition; the header is ignored

Best practices

  • One key per business operation; repeat it only on retries of the same operation.
  • Persist the key alongside the operation's state on your side — a post-crash retry uses the same key.
  • Combine with exponential backoff on 5xx/timeout: the key guarantees the retry duplicates nothing.
  • Since errors are recorded too, an error replay is a signal to fix the request and generate a new key — not to insist on the same one.
  • Discard keys older than 24 hours on your side; the server has already expired them.