Appearance
Idempotency
Send an Idempotency-Key with a POST, PATCH or DELETE, and a quick retry of the same request returns the original response instead of doing the work twice.
How to use it when retrying: Handle errors and retries.
The header
http
Idempotency-Key: "d7c59ab2-3f12-4ad8-9b03-2a7b4f9c7e10"- Use a fresh UUID per logical operation, and keep it for that operation's retries.
- Per RFC 9651 the value is quoted. An empty value returns
400. GETrequests ignore the header — they are safe to repeat anyway.
Behavior
| Situation | What happens |
|---|---|
| Same key, same request, original finished less than 30 seconds ago | The original response is replayed; nothing is executed again |
| Same key, same request, original still in progress | 429 with retry-after: 2 — retry after two seconds |
| Same key, same request, more than 30 seconds later | Executed again, as a new request |
| Same key, different body, path or method | Treated as a different request and executed |
"Same request" means the same method, URL and body, from the same merchant. The signing headers are not part of it, so a retry can be re-signed with a fresh zay-timestamp.
Successful responses and 4xx errors are replayed. 429 and 5xx responses are not, so a retry after a server error runs the request again.
The replay window is 30 seconds. It protects you from a retry right after a timeout — not from reporting the same payout again minutes later. Before retrying a payout after a longer gap, check whether it was created; see Report a payout.
Responses
The two idempotency errors use their own body, not the problem-details shape:
json
{
"ok": false,
"error": {
"message": "Another request with this idempotency key is still in progress, please try again later"
}
}json
{
"ok": false,
"error": {
"message": "The Idempotency-Key header provided was empty or malformed"
}
}Submit events also deduplicates each event by its own event_id, for 24 hours.