CuriousCompute
Menu
Reading roomPractical checksAboutGet the free briefing

Agents & reliability 6 min readShip an agent

Your AI agent timed out. Did it still spend the money?

A missing reply does not mean a refund failed. Before an AI agent tries again, it needs a way to check what already happened.

The short answer

Possibly. The refund may have happened even though its reply never arrived. Keep the same reference for the approved refund and use the payment service’s documented recovery process. If the result is still unknown, pause rather than create another refund.

On this page

The Big Question

An AI support agent—a system that can take actions for a customer—is allowed to issue a $40 refund. It sends the request to the payment service. Five seconds pass without a reply. The agent reports a timeout and tries again.

That sounds helpful. It might also be the moment a routine refund becomes two refunds.

This example is fictional. The problem is not whether the AI understood the customer. It is whether the refund happened. A request that never arrived and a completed refund whose reply went missing can look the same to the agent.

Conceptual example / Refund recovery

The same timeout can hide two different outcomes.

An agent sends one approved $40 refund.

Refund happened

$40 returned

The payment service finished.
Its reply was lost.

Request never arrived

$0 returned

The payment service never started.
There was no reply.

Both look like a timeout to the agent.

Check the original refund before creating another.

Inside the system: authorization & recovery

Keep a persistent reference for the approved refund and link each attempt to it. Use the provider’s documented recovery process; pause when the result remains unknown. An operation reference and a provider’s retry key are related records, not necessarily the same identifier.

The model proposes. The application authorizes.

01 / Untrusted proposal

Agent → tool request

issue_refund(order_id, amount, currency, reason)

Validate the schema. Treat generated arguments and retrieved text as input—not permission.

↓ Proposed action crosses the trust boundary

Application-controlled authority · no payment credentials in the model

02 / Independent evidence

Read authoritative state

Retrieve the order, refundable balance and policy version. Derive the caller’s identity from the authenticated session.

Data informs a decision; retrieved instructions cannot grant access.

03 / Server-side checks

Authorize this exact action

  • Caller can access this order
  • Refund scope, currency and amount allowed
  • Policy satisfied; evidence available
  • Required approval matches these arguments

↓ Decision + reason code · approval is not execution

Allow → execute

Bounded executor

Recheck mutable state. Reserve the operation. Use a stable idempotency key for retries of the same refund.

Enforce constraints at execution; a prior check alone cannot prevent races.

Review → pause

Human decision

Missing evidence or a policy-defined exception goes to a reviewer. Bind approval to the action and its expiry.

Approved? Return to the checks. No direct bypass to payment.

Deny → stop

No side effect

Reject unauthorized scope, invalid ownership or a prohibited action. Return a reason; do not retry around the restriction.

↓ Every decision is recorded; only allowed actions reach execution

04 / Observe & reconcile

A trace, not just a success message

Record request ID, policy version, decision, approval reference and execution result—with sensitive fields redacted.

Timeout ≠ failure. Reconcile provider state before a retry. Reuse the operation key; never silently create a new refund.

Reading the map: arrows indicate request/data flow; the dashed amber enclosure marks application-owned enforcement. Allow, review and deny are alternative outcomes.

About this visual

Two illustrative outcomes can produce the same timeout. Other states, including in-flight processing, are possible. This original diagram explains ambiguity; it is not a benchmark or a complete payment architecture.

The decision this changes

Before giving an agent permission to move money, ask how the application handles a missing reply without issuing the payment twice. Ask your team or vendor: “If the reply disappears after a refund succeeds, how do you avoid issuing a second refund?”

The evidence

A timeout means the application stopped waiting for a reply. It does not prove that the requested action failed. AWS describes this problem using a request to create a computing resource: repeat the request without protection against duplicates, and you may create two resources. An AI agent faces the same uncertainty when deciding what to do next. [1]

Our interpretation

Connect the signals

Our question: does the application record that the result is unknown, or does it let the agent try again as though nothing happened?

A message such as “request failed” hides several possibilities. The service may have rejected the request before starting. It may still be working. It may have finished but lost the reply. Or the application may not know which happened. Those situations need different recovery steps.

We propose recording an unknown refund result as its own state. The tool should tell the agent which refund it concerns, what remains unknown and which checks the application allows next. The agent can explain the uncertainty to the customer. It should not create another refund simply to finish the conversation.

For builders Methods, implementation details and source evidence

Give each approved refund one persistent reference

Before sending an approved refund, save a unique reference for it in persistent storage—a database, for example. Record the approved order, amount and currency with that reference. Count each sending attempt separately, while keeping the same reference for the same refund.

If the agent tries the same refund again, that is another attempt at the original action. Giving it a new payment reference can make it look like a separate refund. But the order number alone is not enough either: one order can legitimately receive two different partial refunds.

The application must distinguish “try this approved refund again” from “approve another refund.” Store that decision in the application’s records, rather than relying on the AI’s conversation history.

How a payment service recognizes a repeat request

An idempotency key is a reference the payment service uses to recognize another attempt at the same request. Stripe stores the first result and can return it for the same key—even when that result is a server error. Parameters must match. Some failures before execution are not stored. Keys may be removed once they are at least 24 hours old; reusing a removed key starts a new request. Check the rules for your integration. [2]

The protection has limits set by the provider; adding a key is not enough. It does not grant permission to issue a refund or check whether the amount is correct. It also does not guarantee that every step across all services will happen exactly once.

When the system still cannot tell what happened

Start with the saved refund record. If the provider offers a reliable status check, use it. Otherwise, follow its documented retry procedure using the original key while that protection remains valid. If the provider sends an event notification, verify it and match it to the saved refund. The notification is evidence of what happened, not a request to issue another refund.

If the result is still unknown, pause the action and send the refund reference and available evidence for human review. The reviewer needs to see what may already have happened. Approving another attempt does not make the missing result known.

Even “not found” can be inconclusive. The status record may not have updated yet, or the original request may still be running. Before starting a new request, check what the provider promises about when its status records become reliable.

What we don’t know

We have not tested this example with a payment provider or measured an agent product. The diagram explains a possible failure; it is not a complete payment design. Safe recovery depends on the provider’s rules, how long it recognizes repeat requests, how overlapping requests are handled and whether your records survive a restart.

Preventing a duplicate does not make the original refund valid. The system must still check permission, amount limits and any conditions that apply when the refund is sent.

Try this Open the full exercise and its limits

Try this

In a test environment with made-up orders, simulate two failures. First, prevent a refund request from reaching the payment service. Then let a refund happen but prevent its reply from reaching the application. Make the application see the same timeout in both tests.

Let the application attempt recovery. Inspect the saved refund reference, each attempt and the payment service’s final record. Count actual refunds, not reassuring messages. When recovery succeeds, one approved refund should produce one refund. If the result remains unknown, the application should stop safely and record that uncertainty.

Next, test a reply that arrives late, the same event notification arriving twice, and another attempt after the provider has stopped recognizing the original key. Does the application stop when it can no longer establish a safe next step? Record what happened. These small tests do not establish production reliability.

The next question

A trustworthy agent needs a useful way to say: “I don’t yet know whether that action happened.”

When your agent says “done,” which record—not which sentence—proves it?

When evaluating an agent product, ask to see a timeout test and the resulting payment records—not only the agent’s answer.

One AI decision, made clearer.

Get a short email with one visual explanation and one practical check. Follow the full article when you want the evidence.

Follow the evidence

Sources & further reading

  1. AWS Builders’ Library: Making retries safe with idempotent APIs ↗

    Malcolm Featonby. Primary engineering guidance on ambiguous outcomes and retry semantics. Checked September 25, 2026; not an agent-product benchmark.

    Back to cited passage ↑
  2. Stripe API: Idempotent requests ↗

    Primary API documentation. Checked September 25, 2026. Provider-specific key, parameter and retention behavior; verify the contract used by your integration.

    Back to cited passage ↑

A correction or a different perspective? Write to us.