A time-of-check to time-of-use race in agent payments is the gap between the moment a system approves a spend and the moment money moves. If policy gets evaluated only at the start of that gap, the decision can be stale by the end of it. [CWE-367](https://cwe.mitre.org/data/definitions/367.html) has catalogued the pattern for decades.
That gap gets misreported in agent-payment marketing. The major issuer platforms closed it years ago.
Authorization-time decisioning is table stakes
Every serious card platform calls out synchronously, inside the authorization path, and waits for an approve or decline:
| Platform | Mechanism | Decision window |
|---|---|---|
| [Stripe Issuing](https://docs.stripe.com/issuing/controls/real-time-authorizations) | Real-time authorization webhook | 2 seconds |
| [Marqeta](https://www.marqeta.com/docs/developer-guides/about-jit-funding) | Just-in-Time Funding gateway, 200 to approve, 402 to deny | 3 seconds |
| [Lithic](https://docs.lithic.com/docs/about-authorization-intelligence) | Auth Stream Access, returns APPROVED, CHALLENGE or a decline | 6 seconds |
If you're evaluating an agent-payment vendor and the pitch is "we check at authorization, everyone else checks at issuance," the pitch is out of date.
Order matching isn't new either. Stripe's authorization webhooks carry the merchant data, the amount and your own order IDs, and Stripe documents amount confirmation against the expected order as a standard check. Google's [Agent Payments Protocol](https://ap2-protocol.net/en/specification) puts the same idea into an open standard: a Cart Mandate signs the exact items and price, and the Payment Mandate carries a hash of the matched intent and cart through to the network. We covered how those mandates work in [AP2 verifiable mandates explained](/blog/ap2-verifiable-mandates-explained).
So where does a stale decision still come from?
From the timeout path, mostly.
Read Stripe's real-time authorization documentation closely and you'll find that if your endpoint doesn't answer inside two seconds, Stripe falls back to your configured webhook timeout behaviour to approve or decline. Any platform with a synchronous deadline needs some such default.
That setting is where the race returns. A deployment that defaults to approve on timeout has an authorization path that stops evaluating policy under load, and load is when several agents are most likely to be spending at once.
The accurate version of the problem, then: a hook evaluates policy only when whoever holds the hook answers in time, and the fallback is a configuration choice most teams make once and never revisit.
What breaks when several agents share a budget?
Put several agents on one shared budget and the failure has nothing to do with how fast any single decision gets made.
Four agents work against a €10,000 monthly pool. Each asks whether there's room. Each is told yes. Each spends €3,000. Every decision was correct against the state it saw, and the pool is €2,000 over.
Per-card controls don't catch this. Velocity limits, [including rolling windows](https://docs.lithic.com/docs/velocity-limit-rules), constrain a card. This overspend happens across cards, against a budget those cards share.
Database isolation doesn't catch it either. Isolation levels from READ COMMITTED upward prevent dirty reads, and a dirty read was never the problem. The check and the debit are separate logical steps, and nothing orders the pair.
Retries compound it. A request that times out and gets retried can reserve twice against a single purchase.
How Shatale handles it
Two properties, held together.
Reservations against a shared budget are serialized. Concurrent requests get ordered, so they can't jointly exceed the pool, and a retried request doesn't reserve a second time.
Policy is re-evaluated when the card network routes the authorization, against the committed budget and the delegation as it stands at that moment. A transaction is approved only when it matches an expected order the agent placed. A mismatch is declined, which reduces unauthorized autonomous spend by refusing charges that don't correspond to an order.
Why can't you bolt this on?
Every platform in the table above hands you the hook. Someone still has to write the decision that goes inside it, keep budget state consistent under concurrency, and be right about it on every request inside a two-second deadline. Buying primitives means owning that correctness problem.
Shatale operates the decision rather than exposing it, so ordering is the platform's job instead of your integration code's.
What to ask
- When two of my agents spend against the same budget in the same second, what stops the overshoot?
- Is order matching enforced by default, or is it a webhook I have to implement correctly?
- What happens to a reservation when my endpoint times out and the request is retried?
- Who is the buyer of record at the merchant?
FAQ
What is a TOCTOU race condition in payments?
The gap between checking whether a spend is allowed and money moving. If the check happens when a credential is issued and the charge lands minutes later, the approval can be stale by then. [CWE-367](https://cwe.mitre.org/data/definitions/367.html) is the general catalogue entry for the pattern.
Do Stripe, Marqeta and Lithic evaluate policy at authorization time?
Yes. All three call out synchronously during the authorization and wait for a decision, inside 2, 3 and 6 seconds respectively. A vendor presenting this as a differentiator is describing table stakes.
What don't authorization-time webhooks solve?
Shared budgets across concurrent agents. Per-card velocity limits bound a single card. They don't order competing reservations against a pool that several agents draw on at once.
Card isolation is the other half of this problem. The companion piece on why an AI agent should never hold your card covers who the buyer of record is, and why the customer's own instrument stays out of the agent flow.
Shatale is the control layer for AI-agent payments. Its authorization architecture is the subject of European patent application EP26194994.5 (filed; priority 28 July 2026).