One Timeout, Two Orders: How to Make AI Actions Safe to Retry

Illustrated infographic summarizing: One Timeout, Two Orders: Make AI Actions Safe to Retry

By Greg Nowak. Updated 25 August 2026.

An AI agent submits an order. The supplier accepts it, but the response disappears during a network timeout. The agent sees a failed call and tries again. Unless the integration recognises both attempts as one business action, the customer may receive two orders.

This is not limited to autonomous agents. The same failure can duplicate invoices, CRM records, support tickets, emails and webhook-driven jobs. A timeout means only that the caller did not receive an answer in time. It does not tell you whether the action happened.

Decide what a retry is allowed to repeat

Retries are useful for temporary network failures and overloaded services. Backoff, jitter and attempt limits control when requests are repeated, but they do not prevent repeated business effects. That protection must be part of each tool’s contract.

Operation Safe retry behaviour Minimum safeguard
Read product or account data Repeat after a temporary failure Backoff, jitter and an attempt limit
Create or update a record Return the first resource or result Stable idempotency key and stored status
Place an order or send a message Never repeat an accepted action Provider retry key, plus reconciliation
Receive a webhook Acknowledge a previously claimed event Atomic event claim and idempotent worker
Start slow work Report that the original job is still running Durable queue, job ID and status endpoint
A retry policy should be based on the operation’s business effect, not merely its HTTP method.

Give one business intention one stable key

An idempotency key identifies the intended action, not an individual HTTP attempt. Create it when the workflow decides to perform the action, save it with the workflow state and reuse it for every retry. Generating a new UUID inside the retry loop defeats the protection because every attempt then appears unrelated.

Keep the material request unchanged. If the amount, recipient or product changes, create a new intention and therefore a new key. Conversely, reject a reused key whose request fingerprint differs from the original.

Provider behaviour varies. Stripe’s API v1 documentation says that it stores the first status and response body for a key and compares later parameters with the original request. LINE’s messaging guidance requires the retry key on the first attempt and blocks a later request after the original has been accepted. Both also impose retention rules, so your own ledger may need to remember an intention for longer than the provider does.

Make the ledger durable and the claim atomic

A useful action ledger records the key, operation type, request hash, status, timestamps, attempt count, provider request ID and completed result. The database—not process memory—should decide which worker owns the action.

For PostgreSQL, a small claim can use a primary key and ON CONFLICT DO NOTHING:

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 work. If it does not, read the existing row: return its stored result when completed, report that it is still processing, or apply an explicit recovery policy. A separate “check, then insert” sequence is unsafe because two workers can pass the check simultaneously. PostgreSQL documents ON CONFLICT as the database-level alternative for handling concurrent uniqueness conflicts.

Store the first useful response or a stable resource identifier. “Already processed” is often too vague for an agent; returning the original order ID gives it a safe way to continue the workflow.

Know the gap a ledger cannot close

A local ledger alone cannot guarantee exactly-once execution against an unrelated external system. A worker can call the supplier successfully and crash before marking its ledger row complete. On recovery, the system still does not know whether to call again.

The strongest design passes the same key to a provider that supports idempotency. If that is impossible, use a unique business reference the provider can query, or introduce a reconciliation state such as outcome_unknown and stop automatic retries until the result is checked. For consequential actions, an honest uncertain status is safer than guessing.

Queues help throughput, not duplicate prevention

Inbound webhooks present the problem in reverse: providers may deliver the same event more than once. Verify the signature, atomically claim the event identity, enqueue the work and acknowledge promptly. The worker must remain idempotent because queue messages can also be retried.

Stripe’s current webhook guidance recommends recording processed event IDs, accounting for logically duplicated events and handling work asynchronously. The queue separates receipt from execution; the ledger prevents two deliveries from producing the same effect.

Test success followed by silence

The most revealing test is not “the API returned an error.” Simulate the downstream service accepting an order and then dropping the response. Allow the agent or worker to retry and verify that only one business action exists.

  • Send two simultaneous requests with the same key.
  • Reuse a key with a changed payload and confirm it is rejected.
  • Crash after the external call but before the local completion update.
  • Retry after the provider’s key-retention window.
  • Trace workflow ID, action key, attempt, provider request ID and final status together.

Start with the actions that can cause damage

Inventory every agent tool, scheduled job and webhook. Mark reads, reversible writes and consequential actions separately. Then document who creates the key, where it is stored, how long it survives, what response is replayed and how an uncertain outcome is reconciled.

Greg can help your team map these failure boundaries and build a reusable gateway for stable keys, atomic claims, result replay and operational logging. If retries currently depend on hope or manual cleanup, get in touch for a practical review.

Retries should repeat delivery attempts, not business decisions. With that distinction enforced, a timeout remains an operational nuisance instead of quietly becoming a second order.

Related on GrN.dk

Need help with this kind of work?

Make your AI integrations safe to retry Get in touch with Greg.

Sources

Seneste artikler

AI kan finde opsigelsesfrister og prisreguleringer i leverandørkontrakter, sende usikre fund til godkendelse og oprette de rette påmindelser.

Sådan automatiserer danske virksomheder Gmail og Microsoft 365 med hurtig sortering, begrænsede rettigheder og menneskelig godkendelse.

Samme kunde på flere kort i HubSpot? Se, hvordan CVR-match, AI-forslag og menneskelig godkendelse kan bruges til at rydde op med styr på felter, relationer og kundehistorik.

Få en ugentlig marketingrapport fra GA4 og Google Ads med kontrollerede beregninger, tydelige dataforbehold og et kort AI-udkast, der hjælper jer på mandagsmødet.

Brug AI til webshoppens alt-tekster med en overskuelig pilot: kortlæg billederne, få danske forslag, og kontrollér resultatet i WordPress og WooCommerce.

AI-baseret ticketanalyse kan afsløre gentagne klager, produktfejl og huller i dokumentationen – uden at virksomheden behøver endnu en chatbot.

OpenSSH 10 fjerner DSA og advarer om nøgleudveksling, der ikke er post-kvantesikker. Her får du en metode til at afgrænse SFTP-oprydningen uden at svække alle SSH-forbindelser.

Botforespørgsler overstiger nu menneskelig webtrafik. Lær at auditere AI-crawlere, fastsætte regler på stiniveau, håndhæve robots.txt og måle det forretningsmæssige afkast.

Cloudflares Tunnel-opdateringer fra 2026 forbedrer kortlægning, overvågning af replikaer, logstreaming og overdragelse – men synliggør samtidig svagt ejerskab og mangelfuld praksis for failover og logging.

Sådan bruger du AI til mødenoter og opfølgning, mens faste regler beskytter CRM-data, kundematch og pipeline mod fejl og forhastede ændringer.