How to make calls safely to avoid unintentional retries
Idempotency
As some of the operations with payouts involve not-reversible money flows, this API supports idempotency keys to prevent the same operation from being accidentally executed twice. This may happen when a request sent by the API client fails due to connection errors or other unexpected conditions. Using the same idempotency key, the API client can safely retry the operation knowing that Devengo will guarantee the operation associated with the key is executed only once.
How it works
To perform an idempotent request, provide an additional X-Devengo-Idempotency-Key header to the request. When an idempotency key is provided Devengo will save the status code and body of the first request made for that key. Subsequent requests with the same key will return the same status code and body. This approach will be applied regardless of whether the first request succeeded or failed.
What to use as the key
An idempotency key is a unique value generated by the client that the server uses to recognize subsequent retries of the same request. Defining the nature and implementing the uniqueness of the keys is up to the client. Still, Devengo suggests using V4 UUIDs or another random string with enough entropy to avoid collisions, like some client business identifier.
Duration and use of the keys
Devengo will store idempotency keys only for 7 days. After that period, the key will be removed from the system, and a new request will be generated if the old key is reused. The idempotency layer compares incoming parameters to those of the original request and errors unless they're the same to prevent accidental misuse.
All POST requests accept idempotency keys. Sending idempotency keys in GET and DELETE requests has no effect and should be avoided, as these requests are idempotent by definition.
How to know when a request was replayed
The header Idempotent-Replayed is attached to every request that is processed as idempotent (POST and PATCH with an X-Devengo-Idempotency-Key). It is not returned for GET or DELETE, where the key is ignored.
Idempotent-Replayed: false— this request was executed.Idempotent-Replayed: true— the status and body are a replay of a previous result for this key, or a conflict because the original attempt is still in progress or has no stored response yet.
This is the reliable way to distinguish a first execution from a replay.
Server errors and unknown outcomes
The first completed response is stored including 5xx. A later request with the same key and the same payload, within the 7-day window, returns that stored response and does not run the operation again. The key is therefore consumed by a 5xx.
That does not mean the side effect did not happen. Idempotency records the HTTP response; it does not wrap the business operation in a single transaction. A payment can be created and the client can still receive a 5xx, or the connection can drop before the response is stored. In the latter case a retry may receive 409 rather than the original status, and still will not re-execute.
So:
- Replaying the same key cannot create a second payment.
- A stored 5xx or a
409on that key is not proof that no payment exists. - After a 5xx, a timeout, or a
409on the key, reconcile before sending the same payment with a new key.
Recovering from an unknown outcome on payment creation
When POST /v1/payments does not return a clear 2xx or 4xx, do not retry with a different idempotency key until you know whether the payment exists.
- If you received a
201, persist the paymentidand useGET /v1/payments/{id}. - If the outcome is unknown, call
GET /v1/payments?company_reference=…using thecompany_referenceyou sent on create. This filter is supported on the payments listing.company_referenceis not unique: send a value that is unique per payment attempt so that zero results means the payment was not created, and one result is that payment. - You can also rely on
outgoing_payment.*webhooks if you subscribe to them.
Only if the listing (and webhooks, if used) show no payment should you create it again, with a new idempotency key.