Your AI Agent Says the Payment Failed. Did the Money Move?
Decentralised News Research · Transaction evidence
The most useful question after an agent reports a failed payment is: which record supports that conclusion? A timeout, a rejected request, a pending operation and an executed transfer require different evidence. A message that sounds decisive can leave the underlying obligation unresolved.
This article publishes the DN Transaction Outcome Test v0.1: a small, reproducible local experiment, its complete synthetic results and a transaction evidence guide. The experiment uses a mock ledger and a hand-written classifier. No AI model, payment provider, bank or blockchain was tested. The original “Transaction Failure Index” outline is implemented here as a catalogue of cases, with no manufactured provider score.
1. Separate the request, the operation and the obligation
One business intention can generate several requests. A request may return several notification deliveries. Several receipts can refer to the same operation. Counting any one of these as if it were the number of payments creates an accounting mistake before an AI model enters the picture.
| Record | Question | Evidence to retain |
|---|---|---|
| Economic obligation | What was the authorised task? | Intent identifier, recipient, asset, amount, deadline and required completion condition. |
| Request attempt | What did the client send and observe? | Attempt identifier, submitted parameters, idempotency scope/key reference and transport result. |
| External operation | What did the receiving system record? | Operation identifier, authoritative status, applicable version and observation time. |
| Economic effect | What moved, remains reserved or still needs resolution? | Principal, fees, recipient credit, holds, refunds and any remaining obligation. |
DN’s proposed evidence record links these layers. It also names the system being observed. A record from a payment processor, an exchange account, a blockchain node and a recipient bank does not establish the same completion condition. Define the required endpoint before treating a response as proof that the whole task is finished.
2. What the DN experiment actually ran
Each case starts independently with 1,000 invented DN_TEST units in the payer’s available balance and zero in the other buckets. Internally, 100 integer minor units equal one DN_TEST unit. A normal payment moves 100 units to a recipient and charges the payer a separate 2-unit fee. These are chosen accounting inputs, not a quote for any product.
The mock keeps available funds, reserved funds, recipient balances and collected fees distinct. A pending 100-unit payment reserves 102 units inside the payer’s funds; it has not yet paid the recipient or collected the fee. The two-step case adds a separate proposed payment of 60 units with a 1.20-unit fee, which is rejected before execution.
Duplicate protection applies only to an exact tenant, method, endpoint and key combination. Identical parameters replay a retained operation for ten logical ticks, with the upper boundary excluded. Changed parameters are rejected while that key is retained; an expired key can create a new operation. This is DN’s declared mock contract, not an implementation of Stripe or a blockchain protocol.
The reference classifier receives only the request manifest and delivered observation records. It does not read the simulator’s private balances or the expected labels to decide an outcome. The full ledger is attached afterwards for audit. When the delivered records do not establish a terminal result, the observed outcome remains unresolved.
3. The eight observed outcomes
The table reports the experiment at each case’s declared cutoff. Amounts are in DN_TEST units. Available balance is not total remaining payer funds: add the reserved column when assessing the payer’s economic balance. Fees shown here have already been reflected in the balances.
| Case | Observer-supported classification | Payer available | Payer reserved | Recipient balance | Collected fees |
|---|---|---|---|---|---|
| 01 · Reject before execution | Rejected before execution | 1,000 | 0 | 0 | 0 |
| 02 · Execute, lose response, resolve by readback | Execution confirmed | 898 | 0 | 100 | 2 |
| 03 · Pending at the observation cutoff | Pending at cutoff | 898 | 102 | 0 | 0 |
| 04 · Retained same-key retry deduplicates | Execution confirmed | 898 | 0 | 100 | 2 |
| 05 · Fresh-key retry duplicates the payment | Duplicate economic payment | 796 | 0 | 200 | 4 |
| 06 · Two-step workflow partially completes | Partial workflow | 898 | 0 | 100 | 2 |
| 07 · Stale notification versus authoritative record | Execution confirmed | 898 | 0 | 100 | 2 |
| 08 · Separate refund with residual fees | Compensated; fees remain | 997 | 0 | 0 | 3 |
Classification uses delivered observer records. The four balance columns come from the separate simulator-ledger audit, attached only after classification; those private balances were not an input to the classifier. Recipient balance means the closing balance, so case 08 shows zero after the refund.
The dataset’s unresolved_obligations field counts obligations with an unknown or nonterminal observed outcome. A known rejection or incomplete workflow can still leave the business task unfulfilled even when its transaction outcome is known.
Case 05 is an intentionally unsafe negative control. A duplicate payment is the expected consequence of that constructed intervention, not a successful business outcome. The eight cases were selected to expose distinctions; their counts must not be presented as real-world failure percentages.
A lost response can conceal an executed payment
In case 02, the ledger executes the payment before the client loses its response. Until authoritative readback, the classifier has insufficient evidence to declare success or failure. The later record establishes one executed payment in this mock: the recipient receives 100, fees receive 2 and the payer has 898.
Case 04 repeats the same request within the mock’s retained-key scope. It obtains the existing operation without posting a second payment. Case 05 changes the key for the same declared economic obligation. The mock executes twice, leaving 200 with the recipient, 4 in fees and 796 with the payer. Linking distinct operation identifiers to the same obligation exposes the duplicate.
The contrast is produced by these explicit inputs and rules. It does not show how often an agent makes this mistake or prove that any real service offers equivalent protection. It gives an operator a reproducible case to adapt to the actual integration.
A reservation is still the payer’s money
In case 03, 898 available plus 102 reserved still equals the original 1,000. No principal has reached a recipient and no fee has been collected at the cutoff. Reporting a 102-unit realised loss would confuse a restriction on availability with an executed expense. Reporting success would confuse a pending record with a terminal result.
The result also cannot be extended beyond the cutoff. A pending operation could later execute, be rejected or remain unresolved under a different service contract. The dataset makes no claim about that unobserved future.
A refund does not erase the original operation
Case 08 first executes the 100-unit payment and 2-unit fee. A separate refund then returns the original 100-unit principal from the recipient, withholding an invented 1-unit refund fee. The payer receives 99, so its closing balance is 997. The recipient returns to zero and collected fees total 3.
Payer closing balance = 1,000 − 100 − 2 + 100 − 1 = 997 DN_TEST units.
Keep the original payment and the linked refund as separate records. Calling the refund “full principal” does not make the payer whole after fees. Do not subtract those same fees a second time from the already-net closing balance. The fee payer and withholding arrangement here are invented; a real contract may allocate costs differently.
4. Apply the actual service’s retry rules
Stripe’s current HTTP guidance explicitly treats API v1 network errors and some server errors as potentially indeterminate. A connection failure does not establish whether execution occurred. Its guidance is to reuse the same key and parameters within the first 24 hours, then reconcile an unresolved outcome instead of continuing retries. It also warns that a new key can create another operation. API v2 has different replay behaviour. [1]
The API v1 idempotency reference explains the boundary: results are retained after execution begins, including error responses; keys may be removed once at least 24 hours old; and a reused, removed key can start a new request. Matching a retained key with different parameters produces an error. Pre-execution validation or concurrent-request conflicts do not create the same cached-result record. [2]
These documented distinctions are why an incident record needs the endpoint, API family, original parameters and key age. A generic “retry enabled” checkbox leaves too much unspecified. The DN mock’s ten-tick lifetime is a teaching assumption, not a replacement for a provider’s documented window.
Also distinguish a duplicated notification from a duplicated economic effect. Seeing the same operation twice in a log does not, on its own, mean the recipient was paid twice.
Stripe does not guarantee webhook delivery order. Its documentation recommends tracking event identifiers for repeat deliveries and using object identity plus event type where separate events represent the same underlying change. Snapshot creation times have second-level granularity, so equal timestamps are not a safe deduplication rule. Retrieve relevant objects when events leave gaps, and verify webhook signatures before trusting their contents. [3]
Case 07 illustrates a related observation problem using an explicit mock revision counter. A stale pending notification does not override a newer authoritative executed record. Real integrations must use the ordering and status guarantees actually available; the mock’s revision field must not be assumed to exist in their API.
5. A blockchain receipt answers a narrower question
Ethereum’s eth_getTransactionReceipt documentation returns a receipt or null when none is found; pending transactions have no receipt. A modern receipt includes execution status, block identity, logs and gas fields. A null result is therefore not sufficient evidence that an intended payment never happened. Match the chain, transaction identity and observation point before interpreting it. [5]
Ethereum’s transaction lifecycle separately describes submission, block inclusion and later finality. A receipt’s execution result and the block’s finality are distinct checks. For a broader payment workflow, recipient, asset and application-specific effects also need to match the original obligation; successful execution alone is not a universal proof that every downstream service has completed. [6]
Fees require their own record. Ethereum’s gas documentation distinguishes rejection before inclusion from a transaction that exhausts gas during execution. The latter can revert state changes while consuming gas; the former can consume none. Record the actual included transaction’s gas cost rather than assigning every reported failure either zero cost or the original intended payment amount. [7]
These are documentation-based distinctions. The eight DN cases use an internal ledger; they do not simulate Ethereum consensus, reorganisation, token behaviour, bridges, account abstraction or a bank payout. Extending the experiment to those systems requires additional fixtures and independently checked completion criteria.
6. Use the DN Transaction Evidence Guide
Select the strongest record you currently have. The guide displays the next evidence to collect; it does not authenticate your records, query an account or authorise a retry. Leave uncertain outcomes uncertain. The core distinctions and all eight results are visible above.
Reproduce the experiment: Download the results CSV (8 synthetic cases, 2 KB) or the complete reproducibility package (ZIP, 37 KB), including source code, validation records and checksums.
7. Make the incident record useful to the next operator
A useful record should let another authorised operator reconstruct the event without relying on the original agent’s prose. Record the business obligation, every known request, authoritative operation identifiers, the observation cutoff and the accounting boundary. Keep uncertainty in a dedicated field, not in a reassuring summary sentence.
| Field group | Minimum useful content | Common gap |
|---|---|---|
| Intent and authority | Approved obligation, recipient, asset, amount and completion rule. | A task description with no stable intent identifier. |
| Attempts and scope | Attempt IDs, endpoint/version, parameters and scoped key references. | A new key generated without resolving the original attempt. |
| Observations | Operation IDs, authoritative status source, event IDs, versions where supported and cutoff. | The latest received notification treated as the newest state. |
| Money and costs | Principal, holds, recipient credits, payment fees, refunds and correction fees. | A net balance followed by a second deduction of the same fee. |
| Unresolved work | Unknown outcomes, remaining obligations, owner and permitted next action. | “Resolved” used when only the local process restarted. |
Keep credentials and private customer records outside public incident examples. A public research version can use synthetic identifiers and amounts, as this experiment does. The internal operational record must still retain enough controlled evidence for an authorised reviewer to investigate.
Stripe’s refund documentation illustrates why corrections need their own lifecycle: a refund may be pending, require action or fail. It also notes that original processing fees are not returned. Behaviour varies by payment method and arrangement; an API refund status should be interpreted against the relevant destination and receipt evidence. Those documents do not supply the DN mock’s invented refund fee. [4]
For the surrounding controls, use DN’s spending-limits evidence guide, cash-and-inventory planner and recovery framework. The tool-calling reliability article addresses the broader difference between a valid call and a correct task outcome.
Methodology, reproduction and limits
DN fixed the protocol and expected numeric outcomes before executing the eight cases. The simulator, observer classifier and expected-result file are separate. The observer classification is computed before the simulator’s private ledger audit is attached. The validation process replays the journal, checks nonnegative balances and conservation, reconciles fees and principal, and compares the results with the declared expectations.
The run passed 64 predeclared field comparisons, 110 journal and ledger checks, and 12 validation-only boundary checks. The boundary checks include an unknown-only observation test and do not enlarge the eight-case experimental sample. Two additional fresh-process reruns produced byte-identical JSON traces, ledger audits, the combined dataset and CSV. Passing these checks establishes conformance to the declared mock rules, not reliability of a real agent or payment service.
Extract the reproducibility package and run the following from its root in a Python 3 standard-library environment:
python3 test/run_experiment.py
The command recreates the generated evidence files. It requires no package installation, API key, wallet or network connection. The fixed protocol and expected outcomes remain inputs. Hashes identify the released files; they do not authenticate the behaviour of an external service.
The experiment has no stochastic model, adversarial network, real provider SDK, blockchain consensus or bank settlement. It cannot measure production reliability, the prevalence of a failure, recovery speed, model judgement or suitability for moving real funds. The classifier is a reference implementation of the declared rules, not a general payment verifier. Changes to those rules require new expectations and a fresh run.
For an organisation, this is useful as a starting fixture set for engineering review and incident-record design. It is insufficient on its own for a deployment decision. The strongest next evidence would come from the actual integration’s authorised test environment, with its versioned contract, independently checked ledger and realistic failure injection.
Primary sources
- Stripe: Advanced error handling — API v1 uncertainty, retries and reconciliation.
- Stripe API: Idempotent requests — retained results, parameter matching and version boundaries.
- Stripe: Receive events in your webhook endpoint — ordering, duplicate delivery and verification.
- Stripe: Refund and cancel payments — refund states and cost distinctions.
- Ethereum: JSON-RPC API — receipt fields and missing-receipt semantics.
- Ethereum: Transactions — submission, inclusion and finality.
- Ethereum: Gas and fees — execution costs and pre-inclusion rejection.
Frequently asked questions
Does a payment timeout prove that no money moved?
No. A timeout records a communication outcome. Establish the operation’s status and economic effects using authoritative records tied to the original obligation. In DN’s controlled mock case, a payment executed before its response was lost.
Does repeating an idempotency key always prevent another payment?
The protection depends on the actual service contract, key scope, retention and matching parameters. DN’s mock deliberately uses a ten-logical-tick window. That teaching rule does not describe every payment API or blockchain.
Does a refund make the payer whole?
Not necessarily. Record the original payment and correction separately, including net credit and fee allocation. In the synthetic DN case, 100 units of principal were returned but the payer finished 3 units below its starting balance after the declared fees.
Does this article measure the failure rate of real AI agents?
No. It reports eight deliberately constructed local mock-payment cases and a hand-written reference classifier. No AI model, payment provider, blockchain or funded account was tested. The cases do not estimate real-world failure rates.
Can the DN guide verify my transaction or authorise a retry?
No. It displays evidence prompts based on the record you select. It does not connect to an account, authenticate a receipt, execute a payment or authorise remediation. The accountable operator must assess the actual system and evidence.
Editorial disclosure, freshness and corrections
This article uses neutral official reference links and contains no affiliate offer or paid product ranking. Documentation references do not establish eligibility, service availability or a verified LIVE promotional status. Compensation does not determine the experiment’s findings.
Change log: 21 October 2026 — first publication of DN Transaction Outcome Test v0.1, its eight synthetic cases, reproducibility files and evidence guide. Source semantics should be rechecked for the actual endpoint and version before use. Send corrections through the DN contact page, citing the case, record or source without sharing credentials or private account data.