Skip to main content

Agent Payment Idempotency + Webhooks

Engineering deep-dive on idempotency keys, webhook signature verification, and replay defense for agent payments. Stripe, Coinbase, x402, AP2, Visa TAP patterns.

Written by Eco

Agent payment idempotency and webhook patterns are the two engineering disciplines that keep autonomous AI payments from billing the same card twice or losing the receipt that proves a charge succeeded. Idempotency uses a client-supplied key to make any payment request safe to retry: the same key returns the same response, so a network hiccup never produces a second charge. Webhooks carry the asynchronous event a few seconds later, signed with HMAC-SHA256 or an equivalent message-signature scheme so the receiver can verify the event came from the payment provider and not a forged source. This piece walks the production patterns: Stripe idempotency keys with a 24-hour window, EIP-3009 nonces on x402 stablecoin rails, AP2 mandate signatures, Visa TAP's RFC 9421 HTTP Message Signatures, and the dead-letter queue patterns that catch the events that would otherwise be silently dropped. Real engineering content for teams shipping agent payment infrastructure in 2026.

What Are Agent Payment Idempotency and Webhooks?

Idempotency is the property that retrying a payment request produces the same outcome as making it once. Webhooks are signed asynchronous event callbacks that notify the merchant or agent platform when an off-thread payment state change happens. Together they form the request and response halves of every reliable agent payment flow.

Both patterns predate agentic commerce by a decade — Stripe shipped idempotency keys in 2015 and signed webhooks shortly after — but the agent context raises the stakes. Human checkout has a person watching a spinner; if the page hangs, the user reloads and sees a confirmation. An autonomous agent has no spinner. It has a retry loop with exponential backoff, and that retry loop is the failure mode. Without idempotency, the second retry attempts a second charge. Without webhook signature verification, an attacker can forge a "payment_intent.succeeded" event and convince a merchant to release goods. The agentic payments overview covers the broader payment-flow context; this article zooms into the two reliability primitives that the rest of the stack assumes are already in place.

Scope across providers is now consistent. Stripe, Coinbase, PayPal, Adyen, and Checkout.com all expose idempotency keys on POST endpoints and HMAC-SHA256-signed webhooks. The crypto-native stack (x402, AP2, MPP) implements the same guarantees through different primitives — EIP-3009 nonces and detached JWS signatures rather than headers — but the contract is the same: a request is safe to retry, and an event is safe to trust only after signature verification. The x402 protocol explainer covers the stablecoin settlement side; the Stripe MPP article covers the LLM-payment-rail side. The patterns in both rest on the primitives below.

How Does Idempotency Protect Against Duplicate Charges?

An idempotency key is a unique client-generated string sent with a payment request. The provider stores the key and the resulting response for a fixed window — 24 hours on Stripe. Any subsequent request with the same key, regardless of how many times it arrives, returns the cached response instead of executing a new charge. The retry path becomes safe by construction.

Stripe's implementation is the dominant reference. Per the Stripe API docs, the client passes an `Idempotency-Key` header on POST requests (Stripe's docs note GET and DELETE are idempotent by definition and do not accept the header). UUID v4 is the recommended format because 122 bits of randomness make collisions infeasible. Stripe stores the response for 24 hours; identical requests with the same key return that cached response with the original status code. A request with the same key but a different request body returns HTTP 400 — the provider refuses to overwrite the original. That refusal is load-bearing: it catches the bug where an agent retries with the same key but mutates the amount field, which would otherwise corrupt the original charge silently.

The window length matters more than it sounds. Twenty-four hours covers the wide majority of legitimate retry storms — exponential backoff with jitter rarely exceeds a few minutes, and operational incidents that take longer than a day usually require human intervention anyway. After the window, the same key becomes a new request. Engineering teams that need longer guarantees layer their own state on top: a payments table keyed by idempotency key, a unique index, and a check-before-call pattern that returns early if the key already resolved.

Crypto-native rails express the same property through nonces. EIP-3009, the standard backing USDC's `transferWithAuthorization` and powering x402 settlement, includes a 32-byte `nonce` field that the buyer signs alongside the payment authorization. The token contract tracks spent nonces; once a transfer with a given nonce is consumed, that signed message can never settle again. The `validAfter` and `validBefore` timestamps bound when the signature can be presented, which limits the replay window without requiring a stateful idempotency store on the client side. Uniswap's Permit2 uses a similar pattern with a 256-bit unordered nonce (split as word and bitmap) so a single signature can be invalidated independently of others — useful when an agent issues many parallel payment authorizations and needs to cancel one without invalidating the rest. AP2 carries idempotency in the mandate itself: each Intent or Cart Mandate signed by the user has a unique mandate ID and timestamp, and the Credential Provider's directory rejects duplicate IDs. The AP2 protocol article covers the mandate flow.

Webhook Architecture for Agent Payments

A webhook is an HTTP POST from the payment provider to the merchant, carrying an event payload (charge succeeded, refund initiated, dispute opened) and a signature header. The receiver verifies the signature against a shared secret using HMAC-SHA256 or, for crypto-native protocols, an asymmetric signature scheme like ECDSA P-256. The verification step is what makes the event trustworthy.

Stripe's signature scheme is the de facto standard. Each webhook ships with a `Stripe-Signature` header containing a timestamp and one or more signatures. The signed payload is the concatenation of the timestamp, a literal period, and the raw request body. The receiver computes HMAC-SHA256 of that string with the endpoint's signing secret, then compares it to the `v1=` value using a constant-time comparison. Stripe rejects events older than 5 minutes by default — the timestamp is the replay defense. Coinbase Commerce uses `X-CC-Webhook-Signature` with HMAC-SHA256 over the body; CDP webhooks use HMAC-SHA256 against a metadata-secret stored in the subscription response. Adyen, PayPal, and Checkout.com follow the same shape with provider-specific header names.

Verification has three failure modes that production code has to catch. First, a missing or malformed signature header — the receiver must reject the request, not log and continue. Second, a stale timestamp — older than the configured window, even if the signature is valid, because an attacker could replay a captured event. Third, a body that has been parsed before signature verification — most HTTP frameworks parse JSON before middleware runs, which mutates whitespace and breaks the HMAC. The fix is to capture the raw body bytes during request parsing and verify the signature against those bytes specifically. Stripe's libraries do this automatically; teams writing their own verifiers usually discover the bug only in production.

Visa TAP, the agent-identity protocol Visa launched on October 14, 2025, uses RFC 9421 HTTP Message Signatures rather than HMAC. The header signs request method, path, body digest, a `created` timestamp, and a `nonce`. The merchant verifies against Visa's public-key directory. Replay defense uses both the nonce (rejecting duplicates) and the timestamp (recipients reject duplicates within the lifetime window; the open spec recommends discarding nonces seen within the last 5–8 minutes). The Visa TAP explainer covers the protocol mechanics. AP2 and ACP carry the same dual replay defense through their mandate signatures and Shared Payment Token expiries — the cryptography differs, but the pattern of "signed envelope plus dedup-by-nonce" recurs across every agent payment standard published in 2025-2026.

Five Patterns That Solve Hard Cases

Idempotency keys and webhook signatures are necessary but not sufficient. A short list of compound patterns — exactly-once charge, ordered event delivery, partition-tolerant retries, fan-out, and idempotent refunds — handle the cases where a naive implementation breaks. These five recur across every production agent payment system the engineering teams at Stripe, Coinbase, and Adyen have published about.

Each pattern below combines a primitive (idempotency key, nonce, signature) with a layer of receiver-side state (a payments table, a processed-events table, an outbox) to produce a guarantee stronger than the underlying transport offers. The spend-controls article covers the policy layer that sits above these patterns; this section covers the rail-level mechanics.

Exactly-once charge

The naive flow — POST to /charges, wait for response — fails if the connection drops after the request reaches the server but before the response returns. Without an idempotency key, the agent retries and double-charges. With a key, the second attempt returns the original response. The full pattern adds a third element: a charges table on the agent side, keyed by idempotency key, written before the API call. The agent generates the key, persists "pending" with the key, calls the provider, and on success persists the provider's charge ID against that row. On failure or timeout, the agent reads the row first; if status is pending, it retries with the same key; if status is succeeded, it skips. The combination produces exactly-once charging without distributed transactions.

Webhook ordering

Webhook delivery is at-least-once, not in-order. A `payment_intent.created` event can arrive after `payment_intent.succeeded` if the network reorders them or the receiver retries the first one. The pattern is to use the event's `created` timestamp and a per-resource version field (Stripe's `livemode` plus `id` plus `created`, or AP2's mandate version) as the source of truth, not arrival order. Receivers persist the last-seen version per resource and silently drop events with older versions. The benefit is independence from delivery order; the cost is that the receiver has to be tolerant of the empty-state case where a "succeeded" arrives before the resource exists locally — which means falling back to a GET against the provider's API to fetch the canonical state.

Network partition

Partitions are the case where the agent doesn't know whether the request reached the server. Idempotency keys make the retry safe, but the agent still has to decide when to retry. The pattern is exponential backoff with jitter (250ms, 500ms, 1s, 2s, 4s, capped at 30s) plus a circuit breaker that trips after a configurable threshold and an alerting hook that pages a human after a longer threshold. Stripe's official client libraries implement this by default; teams writing their own clients often skip jitter, which produces thundering-herd retries when an outage recovers. The agent payment errors article (sibling piece in this cluster) covers debugging cases where the retry loop itself is the bug.

Event fan-out

One webhook event often needs to drive multiple downstream actions: update the order status, send a receipt email, decrement inventory, kick off fulfillment. The pattern is to write the verified event to an internal queue (SQS, EventBridge, Kafka) and let consumers pick it up independently. Each consumer maintains its own idempotency on the event ID; one consumer crashing doesn't block another. AWS EventBridge ships dead-letter queue support out of the box, with configurable retry attempts before a message is sidelined. The pattern keeps webhook receivers thin (verify signature, persist event, return 200) and pushes the heavy work to background workers, which is what makes p99 webhook receive time under 100ms achievable.

Idempotent refunds

Refunds are the case where idempotency keys most often get reused incorrectly. A merchant tries to refund a charge, the request times out, the merchant retries with a fresh key, and the same charge gets refunded twice. The pattern is to derive the refund key deterministically from the charge ID and the refund amount: `refund_{charge_id}_{amount_cents}`. The same logical refund always uses the same key. If a partial refund is supposed to be issued multiple times (which is rare and operationally suspicious), the key has to encode the increment number explicitly. Treating refund keys like idempotency keys for the original charge — generated fresh per attempt — is the most common production bug in this category.

How do I prevent duplicate agent charges in production?

The minimum viable stack is four pieces: a UUIDv4 idempotency key on every POST, a payments table with a unique index on the key written before the provider call, signature verification on every inbound webhook with a 5-minute replay window, and a processed-events table with a unique index on event ID. Anything beyond that is hardening; anything less is a duplicate-charge incident waiting to happen.

Production teams add observability on top. The metrics that matter are duplicate-key rate (how often the same key arrives at the provider, which should be greater than zero on a healthy retry loop), signature-verification failure rate (should be near zero; spikes indicate attempted forgery or rotation lag), webhook receive p99 latency (should be under 100ms or the provider will mark the endpoint unhealthy), and dead-letter queue depth (should be near zero; depth above a configurable threshold pages on-call). The ACP protocol explainer covers the protocol-level event types those metrics are computed against; the patterns above apply identically across ACP, AP2, MPP, and x402.

Operating in Production

Idempotency and webhooks are operational concerns, not just code concerns. Three habits separate teams that ship reliable agent payment systems from teams that ship incidents: monitoring the right signals, managing dead-letter queues actively, and rotating webhook signing secrets without dropping events.

Monitoring: instrument every signature verification, idempotency-key collision, and webhook delivery attempt. The Stripe Dashboard and Coinbase CDP console expose webhook delivery histories with response codes; mirror those in internal observability so on-call engineers don't have to log into a third-party dashboard during an incident. Alert on signature failures (potential forgery), 5xx rates from your own endpoints (the provider will retry, then disable), and DLQ depth.

Dead-letter queues: a DLQ is not a graveyard. The point of routing messages there is so a human can inspect them and decide whether to replay or discard. The pattern is a daily review of DLQ depth, an automated re-drive job that retries DLQ messages after the underlying bug is fixed, and a runbook for the most common causes (parse errors, downstream service outage, schema drift). AWS SQS exposes DLQ messages by source queue and message attributes; teams that set up the DLQ but never look at it discover six months later that 40,000 events were silently dropped.

Signature rotation: every webhook secret has to be rotatable without dropping events during the cutover. The pattern is dual-secret support — the receiver accepts signatures from either the old or the new secret during a configurable overlap window, typically 24-72 hours. Stripe and Coinbase both support multiple active signing secrets per endpoint; receivers that hardcode a single secret have to take a deployment downtime to rotate, which is operationally awful and often skipped, which leaves stale secrets in place for years. Treat signing secrets like API keys: rotate on a schedule, not just after a leak.

Idempotency primitives compared

The table below maps the major agent payment idempotency primitives across Stripe, Coinbase, x402, AP2, and Visa TAP. Scope, window, and example fields differ; the underlying contract — same key returns same outcome — is the same.

Primitive

Provider/Protocol

Scope

Window

Carrier

Idempotency-Key header

Stripe, PayPal, Adyen, Checkout.com

Per POST/DELETE request

24 hours (Stripe)

HTTP header (UUID v4 recommended)

Idempotency key

Coinbase Commerce / CDP

Per POST request

Provider-defined; typically 24 hours

HTTP header or body field

EIP-3009 nonce

x402, USDC transferWithAuthorization

Per signed payment authorization

Bounded by validAfter/validBefore window

32-byte field in signed payload

Permit2 unordered nonce

Uniswap Permit2

Per signature, unordered

Bounded by deadline field

256-bit (word + bitmap)

Mandate ID

AP2 Intent / Cart Mandate

Per signed mandate

Mandate validity window

Detached JWS signed by user wallet

RFC 9421 nonce

Visa TAP

Per HTTP request

5–8 minutes of created timestamp

Signature input parameter

Shared Payment Token

ACP (Stripe / OpenAI)

Per merchant, per amount, single-use

Token expiry (issuer-defined)

Opaque token in checkout payload

The pattern across rows is consistent. Every standard published from 2015 (Stripe idempotency keys) through 2025 (Visa TAP) carries the same idea — a unique request-bound value that the receiver tracks for a bounded window — wrapped in whichever envelope the rail uses (HTTP header, signed payload field, mandate JWS).

Eco's Role: Cross-Chain Idempotency

Eco operates a stablecoin orchestration network across 15 chains. Cross-chain settlement adds a new failure mode that single-chain idempotency keys do not cover: a payment that succeeds on the source chain but fails on the destination, or a duplicate that lands on two chains simultaneously. Eco's intent layer carries an idempotency key end-to-end, so the same agent intent never settles twice regardless of which chain the liquidity ultimately routes through.

The mechanics are straightforward. An agent submits a payment intent — for example, "send 10 USDC to merchant X, settle on whichever chain the merchant prefers" — with a client-supplied idempotency key. Eco resolves the optimal route, sources liquidity through Hyperlane and Circle's CCTP across the 15 chains in scope, and finalizes settlement in seconds. If the agent retries the intent (because the response timed out, or the agent's own state was lost), the same idempotency key returns the original settlement record. The retry never produces a second cross-chain transfer, even though the underlying chains have no shared state. The total stablecoin float backing this layer is roughly $318 billion as of April 29, 2026 (DeFiLlama), with USDC at $77.3 billion and USDT at $189.5 billion. Webhooks fire on settlement finality and are signed with HMAC-SHA256 against the merchant's endpoint secret, with the same signature-and-replay defenses described above. The cross-chain agent payments article covers the orchestration mechanics in depth.

Related reading

Idempotency and webhooks are the reliability primitives every other agent payment topic in this cluster builds on. Each piece below covers a layer that depends on these patterns being correct: the protocol, the rail, the wallet, the spend control.

Sources and methodology. Idempotency-key behavior verified against the Stripe API reference; webhook signature scheme against the Stripe webhook signatures docs; Coinbase patterns against the CDP webhook docs; EIP-3009 nonce semantics against the EIP-3009 spec; Permit2 against the Uniswap docs; Visa TAP against the RFC 9421 spec and Visa's October 14, 2025 launch release. Stablecoin supplies pulled from DeFiLlama on April 29, 2026. Figures refresh quarterly.

Did this answer your question?