Idempotency-Key lets you resend a write without doing it twice.
- Use a new key, such as a UUID, for each new action.
- Use the same key, with the same body, when you resend that action.
- Keys are up to 64 characters. A longer key answers
400 validation_error. - Keys belong to the business. A key used through REST and the same key used through MCP are different requests and conflict.
Which writes take a key
Without a key, a retried
POST /v1/payables, POST /v1/contacts or POST /v1/bank-accounts creates a second record. The contact duplicate guard usually catches a retried contact, but it does not replace a key.
What a retry gets
Cleo stores a fingerprint of the first request: its method, path and body. Key order and fields left out do not change the fingerprint.
Which first answers are stored and replayed:
- Successes are stored.
4xxanswers are stored, except409,429and the state refusals below. A retry of a stored400gets the same400.- State refusals are not stored:
payment_in_progress,insufficient_balance,setup_required,approval_required,payments_locked,setup_incomplete,feature_unavailableandcontact_not_payable. A retry with the same key checks again, so it can succeed once the state changes. 409and429answers are not stored. A retry runs the action again.5xxanswers are not stored, except for money writes.
Money writes never run twice
Payments, cancels, retries, deposits and withdrawals are money writes. Once one has started:- A failure that may have moved money (any
5xx, includingpayment_unconfirmed) is final for that key. Every retry with the key answers409 payment_unconfirmed. - A first request that died without an answer, and is more than 5 minutes old, also answers
409 payment_unconfirmed. - Refusals raised before anything is sent (the state refusals above) still let a same-key retry try again.
payment_unconfirmed, the money may still move. Look for the payment with GET /v1/payments, or the transfer with GET /v1/balance/transactions. Send it again with a new key only once you know the first did not go out.
MCP
MCP write tools take the same key as anidempotencyKey argument. It is bound to the tool and its arguments. See MCP server.