Idempotency
Every write tool — every tool that is not a read — accepts an optional idempotency_key argument. It is sent to the endpoint as the X-Idempotency-Key header the REST API already honours.
{ "name": "dunning_charges_create",
"arguments": {
"person_id": "…",
"original_amount": 1500.00,
"issue_date": "2026-07-10",
"due_date": "2026-08-10",
"idempotency_key": "7c4a8d09-ca37-4f21-b1f0-3d2f2b8c9a11"
} }
| Format | Opaque string, 1 to 255 characters. A UUID is the usual choice. |
| Replay, same body | Returns the recorded response, without running the operation again. |
| Replay, different body | Conflict — 409 IDEMPOTENCY_KEY_REUSED, returned as a tool error. |
The key is an argument of the tool, not part of the body: it never reaches the endpoint as a field. How keys are stored, for how long and what happens on a conflict is the API's behavior, unchanged — see Idempotency in the API documentation.
Why an agent needs this more than your backend does
An MCP client that times out and tries again, or a model that decides "I am not sure that worked, let me redo it", is exactly the pattern that creates the second charge, the second agreement and the second debtor. Your backend has a retry loop you wrote; the agent has a judgement call.
Send a key whenever the operation creates something or reaches the outside world. Reads take no key.
:::warning The ones that reach the debtor
dunning_charges_notify, dunning_notifications_create and dunning_notifications_resend send a message every time they run, and dunning_webhook_endpoints_test and dunning_webhook_deliveries_retry deliver again — they are marked idempotentHint: false. Reporting to a credit bureau (dunning_negativations_create) and protesting (dunning_protests_create) produce an effect outside Régua de Cobrança that no _delete undoes. Send an idempotency_key on those even when you think you will not repeat.
:::
Idempotency and confirmation
Destructive tools are confirmed by the user before they run — see Confirming destructive tools. The two protections are different: confirmation makes sure a person wanted the action; the key makes sure a retry does not run it twice. A confirmation is single-use, so a re-sent confirmed call is refused (confirmation_replayed) rather than repeated; a new call, confirmed again, with the same idempotency_key and the same body returns the recorded response instead of sending the message a second time.