Give an AI Agent an Idempotency Key Before It Can Order
An ordering agent can retry after a timeout and buy the same item twice. A durable idempotency key lets the order and payment systems recognize one purchase intent across repeated calls.
September 13, 2026 · 8 min read

Consider a procurement agent buying twelve toner cartridges. It prepares a cart, receives approval and calls an order tool. The commerce service creates the order, but its response disappears during a network timeout. From the agent’s perspective, the call failed.
It tries again and may create a second order unless the software below the model can recognize that both requests represent the same approved purchase.
An idempotency key supplies that recognition. It is a unique identifier that makes repeated requests for one intended action return one result rather than create new results. The important word is intended: retries must reuse the same key, while a genuinely new purchase needs a new one.
The key belongs in the execution path before the toner order can leave the system. Do not ask the language model to invent it at the moment of submission, and do not rely on a sentence such as “never place the same order twice.” The application hosting the agent should create and persist the key when the purchase becomes an approved business action.
Create the key at the approval boundary
The toner workflow may spend several turns choosing a supplier, checking stock and adjusting quantity. Those planning steps do not yet need an order key. The stable moment arrives when a human or policy engine approves a defined cart: twelve cartridges from a named supplier, shipped to one address, within a stated spending limit.
At that boundary, the orchestrator should create a purchase-intent record with a random unique identifier. The record holds the approved cart, supplier, account, shipping destination and current state. Its identifier can become the root for later idempotency keys, although each downstream operation should receive a namespaced value such as `purchase-intent-id:create-order` or `purchase-intent-id:authorize-payment`.
Persist this record before calling the supplier. Memory inside an agent run is not durable state; a process restart, context compaction or handoff to another worker can remove it. A conversation ID is also a poor substitute because one conversation may contain several purchases, while the same purchase may survive across several agent runs.
The tool schema can require an `idempotency_key` field, but the model should not control its value. The orchestration layer injects the stored key after validating that the proposed tool arguments still match the approved purchase. If the model changes twelve cartridges to thirteen after approval, the orchestrator should reject the call or request approval again rather than reuse the existing key for altered work.
This is the first practical control: no persisted purchase intent, no order submission.
Make the order endpoint enforce one result
The order service must treat the idempotency key as data, not decoration. When the first request arrives, it writes a record keyed by the customer or merchant account, operation type and idempotency key. A database uniqueness constraint, which prevents two rows from sharing that combination, closes the race in which two workers submit the toner order at nearly the same time.
The record should also store a hash of the meaningful request fields. A hash is a compact value derived from the request, useful here for detecting whether a repeated key carries a different cart, address or amount. If the same key returns with the same fields, the service returns the original order identifier and response. If the fields differ, it rejects the request as a conflict rather than guessing which version the caller meant.
There is a hard case between those outcomes. The service may receive a duplicate while the first request is still processing, before an order result exists. It can wait briefly, return an “in progress” status or tell the caller to poll a status endpoint; it should not start another creation path. The orchestrator then records the attempt and checks the purchase intent again instead of asking the model to reason from an ambiguous error message.
Response storage needs a retention policy. Keeping keys forever raises storage costs, while deleting them too soon allows an old retry to create a fresh order. Choose a window longer than the longest plausible queue delay, manual review and retry period in this workflow, then prevent archived agent jobs from resuming after that window without new approval. The exact duration depends on the business system, not on model behavior.
For the toner order, a successful retry should receive the same supplier order ID created by the first call. That is the observable test. Two HTTP requests are acceptable; two orders are not.
Carry the intent through payment without conflating operations
Order creation and payment authorization are separate side effects, meaning they change systems outside the agent and cannot be undone by deleting a chat message. Both need idempotency protection. They should not share one unqualified key, because an order endpoint and a payment endpoint perform different operations and may apply different retention or scoping rules.
The orchestrator can derive two stable, namespaced keys from the persisted purchase intent. Every retry of `create-order` uses the order key. Every retry of `authorize-payment` uses the payment key. If the payment provider accepts idempotency keys, pass the payment key through its supported request field or header and store the provider’s resulting transaction identifier beside the purchase intent.
This does not make the two services atomic, which would mean they either both complete or neither does. The order could succeed while payment remains uncertain, or payment could be authorized before the supplier rejects the order. The orchestrator therefore needs explicit intermediate states such as `order_created_payment_unknown`, plus a reconciliation job that queries existing order and payment records before attempting another write.
That fallback matters after the toner order times out. The worker first looks up the idempotency record or supplier order ID. If it finds an order, it advances the existing purchase intent. If payment status is unclear, it asks the payment service for the transaction associated with the stored key or transaction ID.
Only a confirmed absence permits another call, and that call still carries the original key.
Some external suppliers do not support idempotency. A local key cannot force their API to behave differently. In that case, place a gateway under your control in front of the supplier, serialize submissions for each purchase intent and store the external result before acknowledging completion. If the supplier creates an order but loses the response and offers no lookup by merchant reference, duplicate prevention cannot be guaranteed; route that uncertain state to a human instead of retrying automatically.
Keep retries below the model
A prompt can influence which tool the agent chooses. It cannot coordinate concurrent workers, commit a database row or determine whether a timed-out server completed its work. It may also be bypassed entirely when an HTTP client, queue consumer or orchestration framework retries a failed call without invoking the model again.
The retry policy belongs in deterministic code. That code classifies failures, reuses the stored key, limits attempts and moves unresolved work to a review queue. Deterministic here means the same recorded state triggers the same programmed rule, unlike model output that can vary between runs.
Tool permissions should reinforce the boundary. The agent may draft and revise a cart freely, but the `submit_order` tool becomes available only after the orchestrator has an approved purchase-intent record. On invocation, the host checks the cart hash, injects the idempotency key and writes an attempt record containing the request, response and downstream identifiers. Logs should avoid unnecessary payment or personal data while preserving enough information to explain whether a retry reused the correct intent.
There is a small cost. Each submission adds a durable write, conflict check and usually a lookup, while retained responses consume storage. Those costs are predictable and usually modest beside the operational work of detecting, canceling and refunding duplicate purchases. A supplier integration that cannot expose a stable reference or safely resolve an unknown result may still be unsuitable for autonomous submission.
Before enabling the toner agent, test the failure at step four: let the order service create the order, then cut the response before the orchestrator receives it. Restart the worker and allow the job to resume. The system passes only if the second request carries the original key, returns the original supplier order ID and leaves one payment authorization.
Also test two simultaneous workers, a repeated key with a changed quantity and a retry after key expiration. Those cases reveal whether the guarantee lives in a database constraint and state machine, or only in the cooperative behavior of one agent process.
Questions people ask
Can the agent generate its own idempotency key?
A model can output a string that looks unique, but it should not own the key lifecycle. The orchestration layer should generate the key, persist it with the approved purchase intent and inject it into every retry, so a restart or a different worker cannot silently choose another value.
Is an idempotency key the same as an order ID?
No. The idempotency key identifies the caller’s intention before the order necessarily exists; the order ID identifies the result created by the supplier. Store both. A repeated request presents the original key and should receive the same order ID from the first successful submission.
Does a payment idempotency key prevent duplicate orders?
It prevents repeated payment requests from creating multiple payment results only if the payment service enforces it. The order endpoint needs its own protection. A workflow can otherwise create two unpaid orders or one order with uncertain payment, even though one layer handled retries correctly.
What should the agent do when a supplier lacks idempotency support?
Use a controlled gateway and a merchant reference if the supplier lets you retrieve an existing order by that reference. When the external system offers neither idempotent submission nor reliable lookup, stop automatic retries after an unknown result and send the purchase to human review.
One story a day
The story of the day, in your inbox
One real story about AI each morning — no hype, no alarm, just company for the road.



