Skip to main content

Idempotency

POST /orders moves money, so it is the one endpoint where a retry must never mean a second payment. That is what X-Idempotency-Key is for, and it is mandatory — not a nice-to-have.

X-Idempotency-Key: 0190f9b2-3c4d-4e5f-8a6b-7c8d9e0f1a2b

Omit it and the request never reaches the order logic:

{
"message": "\"Missing required request parameters: [X-Idempotency-Key]\"",
"requestId": "92885917-5fb9-41d1-8272-70cfdefe6c23"
}

That body comes from the API gateway, not from the service: the header is declared required at the edge, so the request is turned away before any order logic runs. It is the one error shape with a requestId and no statusCode — quote that id if you open a ticket about it.

It is also your reference​

The key is an opaque, client-generated string — format is up to you, and we forward it untouched. It comes back in the order as idempotency_key, which makes it the handle you use to find that order later:

GET /orders?idempotency_key=0190f9b2-3c4d-4e5f-8a6b-7c8d9e0f1a2b

So pick something meaningful and unique in your system — an invoice id, a payment run id, a UUID you store next to the payable. If you generate a random key and throw it away, you have thrown away your own reference.

What happens on a repeat​

You sendResult
Same key, same request201 with the original order. Nothing new is created.
Same key, different request409 B2B_IDEMPOTENCY_CONFLICT
New keyA new order

That first row is the behaviour you want after a timeout, a dropped connection, or any response you could not read. Replay the identical request with the identical key and you will either create the order (if the first attempt never landed) or receive the one you already have.

The second row is a bug signal, not something to work around: the same key arrived with different data. Do not retry it with a new key until you know which of the two payloads was the one you meant.

The other single-use thing​

Idempotency keys are not the only uniqueness rule on POST /orders, and the other one behaves differently:

{
"code": "B2B_QUOTATION_ALREADY_USED",
"category": "PERMANENT",
"message": "La cotización referenciada ya respalda otra operación: es de uso único. NO reintente con la misma cotización. Verifique el estado de la operación existente antes de crear una nueva; si corresponde, genere una cotización nueva."
}

A quotation is single-use: it backs exactly one order, forever, regardless of how that order ended. So:

  • Never retry with the same quotation_id. It fails identically every time.
  • Before creating a replacement order, look up the existing one. There is already an operation in flight or completed against that quote, and creating a second one is how you double-pay a beneficiary.

A new attempt means a new quotation — which also means a fresh rate, since quotes expire.

No other endpoint takes it​

X-Idempotency-Key only applies to POST /orders. Reads are naturally idempotent, and POST /quotations is safe to repeat: an extra quotation costs nothing and commits nothing until an order references it.