By Greg Nowak. Updated 25 September 2026.
An AI agent places an order. The supplier accepts it, but the response disappears during a network timeout. The agent sees an error and tries again. If the integration treats that retry as a new instruction, one purchase quietly becomes two.
The same failure pattern can duplicate invoices, CRM records, support tickets, emails and fulfilment jobs. It matters whenever software performs an action with a real financial, operational or customer-facing consequence. A timeout tells you only that the caller did not receive an answer in time—not whether the action happened.
Define the business effect before writing the retry policy
Backoff, jitter and attempt limits protect an overloaded service. They do not prevent duplicate business effects. That protection belongs in the contract for each tool or API operation.
| Operation | What a retry should do | Required safeguard |
|---|---|---|
| Read product or account data | Repeat after a temporary failure | Backoff, jitter and an attempt limit |
| Create or update a record | Return the original resource or result | Stable key, request fingerprint and stored result |
| Place an order or send a message | Never repeat an accepted action | Provider-supported retry key and reconciliation |
| Receive a webhook | Acknowledge an event already claimed | Atomic event claim and idempotent worker |
| Start slow work | Report the original job’s status | Durable queue, job ID and status endpoint |
This classification is a useful design and procurement exercise. For every action exposed to an AI workflow, the product owner and delivery team should be able to say what counts as “the same action,” how long that identity survives and who resolves an uncertain outcome.
Give one business intention one stable key
Create an idempotency key when the workflow decides to perform an action, persist it before the first API call and reuse it for every attempt. Do not generate a new UUID inside the retry loop. That makes every attempt look unrelated and removes the protection.
The key should identify the intention, not contain sensitive business data. Store a hash of the material request alongside it. If the amount, recipient, product or destination changes, create a new intention and key. If an existing key arrives with a different request hash, reject it instead of guessing which version is correct.
Provider rules are not interchangeable. Stripe’s current API documentation says it saves the first status and response body for a key, including a 500, and checks later parameters against the original request. Keys may be removed after at least 24 hours. LINE’s Messaging API requires the retry key on the first attempt, returns 409 after an accepted request and retains the key for 24 hours. Your own ledger may need to remember the business intention much longer.
Make the action ledger durable and the claim atomic
A practical ledger records the key, operation, request hash, workflow ID, status, timestamps, attempt count, provider request ID and completed result. Keep it in a database, not process memory, and enforce uniqueness on the key.
CREATE UNIQUE INDEX action_ledger_key_uq
ON action_ledger (idempotency_key);
INSERT INTO action_ledger
(idempotency_key, operation, request_hash, status)
VALUES
(:key, :operation, :request_hash, 'processing')
ON CONFLICT (idempotency_key) DO NOTHING;If the insert succeeds, that worker owns the action. If it inserts nothing, read the existing row. Return its stored result when complete, report that the original action is still processing, or follow an explicit recovery path. A separate “check, then insert” sequence is unsafe because two workers can pass the check together. PostgreSQL documents ON CONFLICT as the database-level mechanism for handling the uniqueness conflict.
Store the first useful response or a stable resource identifier. “Already processed” leaves an agent with nowhere to go; returning the original order or ticket ID lets the workflow continue safely.
Design for the outcome you cannot prove
A local ledger cannot guarantee exactly-once execution against an unrelated external service. The worker may call the supplier successfully and crash before recording completion. After restart, your database knows the action began but not whether the supplier accepted it.
The strongest design sends the same key to a provider that implements idempotency. Otherwise, use a unique business reference that can be queried. If neither is available, move the action to an explicit outcome_unknown state, stop automatic retries and reconcile it through an API lookup or human review. For consequential actions, uncertainty is safer than an automated guess.
Queues improve resilience, but they do not remove duplicates
Webhook providers and message queues can deliver work more than once. Verify the webhook signature, atomically claim the event identity, enqueue the work and acknowledge promptly. The worker must still be idempotent.
Stripe’s webhook guidance recommends returning a successful response before complex processing, using an asynchronous queue and recording processed event IDs. It also warns that separate events can represent the same logical change, so some integrations must deduplicate with both the event type and affected object ID.
Test “success followed by silence”
The most revealing test is not a clean API error. Simulate the downstream service accepting an order and then dropping the response. Let the agent retry and confirm that only one business action exists.
- Send simultaneous requests with the same key.
- Reuse a key with changed material data and confirm it is rejected.
- Crash after the external call but before the local completion update.
- Retry beyond the provider’s key-retention window.
- Deliver the same webhook twice and in the wrong order.
- Trace the workflow, action key, attempt, provider request ID and final status together.
Start where duplication would hurt most
Inventory agent tools, scheduled jobs and webhooks. Prioritise payments, purchasing, outbound communication and irreversible updates. For each one, document who creates the key, where it is stored, what response is replayed, when retries stop and who owns reconciliation.
This is often a small integration project with a large risk-reduction payoff. Greg can help your team map the failure boundaries, review provider contracts and build a reusable pattern for stable keys, atomic claims, result replay and operational logging. If your current retry strategy relies on hope or manual clean-up, get in touch for a practical review.
Retries should repeat delivery attempts, not business decisions. Once that distinction is enforced, a timeout becomes an operational nuisance instead of a second order.
Related on GrN.dk
- A Voice Agent Is Only Ready When the Human Handoff Works
- Before Your MCP Rollout, Audit the API Contract
- Before Your Support Bot Learns the Help Center, Make Sure It Can Forget
Need help with this kind of work?
Review your retry and idempotency design with Greg Get in touch with Greg.