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.