Rightshold™ Sign up

Answers · agent payments

How do I stop an AI agent from paying twice when it retries a request?

Last updated

TL;DR: Give every purchase one idempotency key, created once before the first attempt and sent unchanged on every retry, and use APIs that honour it. POST is not idempotent in HTTP, so a timeout followed by a retry can create a second charge unless the server deduplicates. Stripe accepts an Idempotency-Key header of up to 255 characters on POST requests and returns the saved result for a retry with the same key; keys can be pruned after 24 hours. The IETF Idempotency-Key header draft says a key MUST NOT be reused for a different request payload. For x402, a signed EIP-3009 authorization can settle only once because its nonce is used up on chain, but an agent that signs a fresh authorization on retry can pay again; x402's payment-identifier extension adds an id the client reuses on retries so servers and facilitators can return the cached response. Before retrying a payment that timed out, check whether the first one settled.

What should I check, step by step?

1. Create the idempotency key when the agent decides to buy, store it with the order (a random UUID v4 is fine), and reuse it on every retry of that purchase. Never generate a new key inside the retry loop.

  1. Send it the way the API expects: Idempotency-Key header for Stripe and APIs following the IETF draft; extensions["payment-identifier"].info.id for x402 servers that advertise payment-identifier.
  2. Keep the request body identical on retries. Stripe errors if the parameters differ from the first request with that key; the IETF draft says to answer 422 for a reused key with a different payload.
  3. Treat 409 Conflict as 'the first attempt is still running': wait and retry with the same key, don't create a new one.
  4. For x402, keep the signed payment and resend the same one rather than signing a new authorization. EIP-3009 nonces can be used only once, so the same authorization can't be settled twice.
  5. After a timeout, check before paying again: read PAYMENT-RESPONSE if you got one, look up the transaction hash on chain, or list recent charges. An x402 settlement_pending error means the transaction may still confirm.
  6. Make resumable agent runs safe too: in LangGraph code before interrupt() runs again on resume, so put payments after it or make them idempotent.
  7. Cap total spend per order and per day, so a bug that does pay twice is bounded.

Why retries double-charge

HTTP defines GET, HEAD, PUT and DELETE as idempotent and POST and PATCH as not. When a POST payment times out, the client can't tell whether the server never got it or charged and the reply was lost. Retrying without a key risks a second charge; not retrying risks no charge.

How the main mechanisms behave

Stripe (API v1): send Idempotency-Key on POST requests, up to 255 characters. Stripe saves the status code and body of the first request once execution begins and returns them for retries with the same key, including 500 errors. Keys can be removed once they're at least 24 hours old, after which the same key makes a new request. Stripe suggests V4 UUIDs and says not to use personal data as keys.

What can go wrong?

A new key per attempt: the most common bug. If the key is generated in the function that sends the request, every retry looks like a new purchase.

How do I ask Rightshold about this (free, no key)?

Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Before you pay, check this purchase against my spending limits and get a signed allow or deny, with the order's idempotency key as the reference.”

One-click setup

{
  "tool": "authorize_spend",
  "arguments": {
    "credential_id": "<your agent credential id>",
    "amount_minor": 2000,
    "currency": "USD",
    "merchant_domain": "api.example.com",
    "reference": "order-7f3c2a90-idem-key"
  }
}

Rightshold reads x402 payment requirements and records spending decisions against limits you set. It doesn't hold funds, send or retry payments, or deduplicate charges for you; the payment provider or the server has to honour the idempotency key.

What are the sources?

Related

Not legal advice. JSON · Markdown · All answers

Get a free API key in one call

No card and no email needed. 100 free credits; the license and policy checks stay free without a key.

curl -s -X POST https://www.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_...   # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
  -H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'