Staying in control
How to stop an AI agent making duplicate payments
Agents retry when unsure, and retries can pay twice. Idempotency keys, status checks and tool wording that keep one intended payment to one payment.
Require an idempotency key on every payment request, so a retry with the same key returns the original payment instead of creating a new one, and give the agent a way to read a payment's status so it checks before it tries again.
Why is this harder than it sounds?
Models retry. A timeout, an unfamiliar status or a long wait for approval all look like failure to an agent that wants to finish its task, and the natural next step is to call the tool again. Without protection, every retry is a new payment. A related failure is concurrency: two tool calls in flight at once can each see the same 'spent so far' and both squeeze under a limit only one of them fits, which a payment service has to handle in one atomic operation.
How do you do it?
- Make the idempotency key required. The payment tool's schema should require a key the agent generates per intended payment. Stripe's API uses the same pattern for the same reason.
- Reuse the key on retries. Tell the agent explicitly: a retry of the same payment reuses its key, and a new key means a new payment that will spend again.
- Give the agent a status check. Expose get_payment or list_payments, and instruct the agent to call it before concluding a payment failed.
- Name waiting states clearly. Statuses like awaiting_approval and submitted should be distinct from failed, and the tool result should say whether to wait.
- Enforce limits atomically. The check that a payment fits the daily cap and the update to today's total must be one operation, or concurrent calls can overspend.
- Test it. On a simulated source, interrupt a payment and tell the agent it failed. It should check status or reuse the key, not pay again.
What it does not mean
- An idempotency key generated fresh on every call does nothing. It has to identify the intended payment, not the attempt.
- Deduplicating by amount and recipient is not a substitute. Two identical payments can both be intended.
- A daily cap limits the damage from duplicates; it doesn't prevent them.
Common questions
What if the agent sends the same key with a different amount?
Treat the key as naming one payment. Most services, Stripe included, refuse or return the original rather than letting a key's payment change.
How long should a key be remembered?
At least as long as the agent might retry. Stripe's docs say its API may remove keys once they are at least 24 hours old, so a day is a common window.
Where does Veyra fit?
Veyra's create_payment requires an idempotency_key, and its tool description tells the agent to reuse it for a retry and never to create a second payment because it is unsure the first worked; get_payment and list_payments give the canonical status. Statuses are pending, awaiting_approval, submitted, confirmed, confirmed_simulated, failed, cancelled and expired. The daily cap is reserved in a single database statement, so two concurrent calls cannot both fit under a limit only one fits.
Sources
Terms on this page
- Idempotency keyAn idempotency key is a unique value a client attaches to a request so that, if the same request is sent again with the same key, the server returns the original result instead of performing the action a second time.
- Payment intentA payment intent is an object representing a payment that has been requested but not necessarily completed, which moves through defined states such as awaiting approval, submitted, confirmed or cancelled.
This page explains how agent payments and the related technology work. It is not financial, investment, legal or tax advice. Protocols and provider features change; check the linked official sources before relying on a detail.