Docs
Idempotency
One field — attempt_key — stops a retried request from buying the same thing twice. Every confirmation-tier operation accepts it, and none of them require it.
Why it exists
Networks time out on the way back. A client that renewed a service, never saw the response and retried has now renewed it twice — and an agent retries on timeout far more eagerly than a person does.
attempt_key
Every confirmation-tier operation accepts an attempt_key: a string you choose that
identifies this intent, not this HTTP request. It is optional on all of them — omit it
and you simply get no replay protection, which for a money operation is the wrong trade. Send it.
A repeat inside the window carrying the same key returns the existing pending action rather than creating a second one. Nothing is charged twice, and the user is not asked to approve the same thing twice.
In the body, not a header
Choosing a key
-
Derive it from the intent, not from a random value:
renew-{serviceId}-{date}is a good key. A fresh UUID per attempt is a useless one — every retry gets a new key and the protection does nothing. - Keep it stable across retries of the same logical operation.
- Change it when the intent changes. Renewing for two years instead of one is a different intent and needs a different key.
- Keys are scoped to your token. Another account using the same string cannot collide with yours.
The 30-minute window
A key replays for as long as its pending action is still pending and unexpired — in practice 30 minutes, the lifetime of the hold. After that the same string starts a fresh action, which is correct: a renewal attempted yesterday and attempted again today is two intentions, not one.
Approval ends the window too. Once the user approves, the action leaves
pending and the key stops replaying — so sending the same attempt_key
again creates a second action and a second charge. The key protects you against a
timeout, not against calling the operation twice on purpose. Check
the pending action's status before retrying.