Engineering

Designing a confirm-and-pay flow an agent can run

The payment path end to end: where the write is intercepted, what the card must show, when a biometric is worth asking for, and why speech is never the credential.

VVoqal · Engineering10 min readPart of Safety

About this article

A payment is the one action where everything ambiguous about an agent becomes concrete. The user said something, a model understood something, and money either moves or does not. Every design decision in between is legible afterwards, in a support ticket or a chargeback.

This post is the payment flow specifically, from the spoken request to the audit row. Two adjacent pieces cover the general shape: the confirm card as a boundary in code rather than a line in a prompt is the architecture, and deciding which actions are worth a biometric is the tiering. This one is what happens on the path where the amount is real.

Intercept the write, do not instruct the model to pause#

The design mistake that produces the worst incidents is putting the confirmation in the prompt. “Always ask the user before transferring money” is a request, and a request is the wrong shape for a control on money movement. Anything that can change what the model reads can change what the model does, which is the whole argument in what prompt injection looks like when the agent can spend.

The structure that holds is a short circuit. Tools that write are registered as action tools, and when the model calls one, your server does not execute it. It returns a sentinel, reads the arguments the model produced, and builds a confirm card from them. The model has named an intention; nothing has happened.

Execution is a second request. It carries the identifier of the approved confirmation, and your server checks that identifier against its own record before touching the payments backend. Approval becomes a row you can query rather than a sentence in a transcript.

The dashed returns are the only things the model gets back. It is told a card was drawn and later that a payment settled.

One more rule earns its place here. Build exactly one confirm card per turn, in your server, and stop the model from authoring a second details card alongside it. Two cards showing overlapping numbers is how a user approves the wrong one.

What the card has to show#

The confirm card is the legal record of what the user agreed to, so it carries the things that decide the payment: amount and currency written out, the recipient in a form the user recognises, any fee, and when the money arrives. If the agent inferred the recipient from a nickname, the card shows what it resolved to, masked to the last four digits where the identifier is an account number.

It also has to be the card, not a card. Approving on a summary while the real arguments differ is the failure mode that makes the whole pattern worthless, so the card must be generated from the same parsed arguments the execute path will use. Drawing it as a widget from the render spec rather than as text is what makes that guarantee mechanical.

One consequence worth stating for anyone designing a signup path: this is a poor first experience. A write is the wrong thing to meet a new user with, which is why getting somebody to first value usually starts with a read.

A dark-theme confirm card for an instant settlement, listing the amount, the fee, an arrival estimate and the total above a single confirm button.
One card, built server-side from the parsed arguments. Every row is an argument the user can check before the call runs. Drawn by the widget renderer on the product's sample figures.

The spoken channel and the drawn channel do different jobs#

Speech is poor at numbers the user must verify. It arrives once, cannot be re-read, and an amount misheard by a factor of ten sounds almost identical to the correct one.

So the division is fixed. The agent says enough to orient the user and ask for a decision. The card holds every figure that matters. After settlement the agent says the payment went through and leaves the reference on screen, because nobody transcribes a reference number from audio correctly.

Two of the spoken rows are prohibitions. The agent does not read out account numbers and does not name the gate method.

The second prohibition is less obvious and worth keeping. The agent should not say “confirm with Face ID”. The device decides which gate it can offer, that decision can change between sessions, and an agent that announces a method the device then does not present has told the user their phone is broken.

When the biometric is worth asking for#

Not on everything. A confirmation tax applied uniformly trains people to approve without reading, which removes the value of the confirmation you are paying for.

Tier it by what the action can cost if it is wrong. In our own Paymob configuration, every money-moving tool is confirm-gated and exactly one of them escalates to a device biometric: instant settlement, which moves a merchant’s own balance out on request. Creating a payment link is tap-to-confirm, because the worst case is a link nobody uses.

Android documents the mechanism for the top tier plainly. Biometric authenticators are classed as BIOMETRIC_STRONG or BIOMETRIC_WEAK, a CryptoObject binds the prompt to a cryptographic operation, and the platform notes that auth-per-use keys “can be useful for high-value transactions, such as making a large payment or updating a person’s health records” (Android developer documentation). Binding matters: a prompt that only returns a boolean can be satisfied by anything that can set a boolean, while a key that will not perform its operation without the authentication is a different kind of guarantee.

There is an accessibility argument here that usually goes unmade. WCAG 2.2 success criterion 3.3.8 requires that a cognitive function test, such as remembering a password or solving a puzzle, is not required for any authentication step unless an alternative exists, and it treats biometrics as not being cognitive function tests (W3C, Understanding SC 3.3.8). Replacing a re-entered password with a biometric confirmation improves accessibility as well as security, and the accessibility half of that is rarely part of the pitch.

What the platform standards do and do not give you#

WebAuthn Level 3 became a W3C Recommendation on 25 August 2026 and defines the authorization gesture as a physical interaction that may involve either user verification or a simple test of user presence. Those are different guarantees: presence says somebody is there, verification says the authenticator checked who.

What the specification does not define is confirmation of the details of a specific transaction. Its use cases mention authorising a single transaction such as a payment, and the mechanics of showing an amount and binding an approval to it are not in the document. That gap is your application’s job, and it is the reason the confirm card exists as a server-generated artefact rather than as a property of the authenticator.

Payment regulation in several markets imposes its own requirements on authentication and on linking an authorisation to a specific amount and payee. Those rules vary by jurisdiction and change, so check the current text with someone qualified rather than a blog post, including this one.

Speech is not a credential#

Voice biometrics as an authentication factor for payments is the one idea in this area to reject outright.

The evidence for rejecting it is a decade of a well-run challenge series. ASVspoof 5 evaluated submissions from 53 teams against a crowdsourced database whose attacks were generated with 32 different algorithms, and the authors report that “while many solutions perform well, performance degrades under adversarial attacks and the application of neural encoding/compression schemes” (Wang et al., 7 January 2026). Adversarial conditions and codec compression are the attacker’s choice and the telephone network’s default.

The practical posture follows. Treat the microphone as an input channel with no trust attached. Anyone who can reach it can state any intention, and the design assumption should be that they will. What stops the attack is that the request has to resurface as a card on the enrolled device, in the hands of whoever holds it, and clear a gate bound to hardware.

The first two stages offer no resistance and are not meant to. The defence is that approval happens somewhere the caller is not.

The flow in a banking or merchant app#

The same skeleton holds wherever money moves, and the wider set of decisions a financial app faces sits around it.

Read tools stay open. Balances, transactions and payment status are answers, and gating them makes the agent useless while protecting nothing that the app’s own screens do not already show.

Write tools are all gated, and only some escalate. Publish the tiering as configuration rather than burying it in code, so a compliance reviewer can read which actions require what without reading a codebase.

Failures need a path. A declined biometric, an abandoned card and a timed-out confirmation are three different states and all three need to leave the system in a clean one. The commonest bug in this area is a confirmation that expires while the execute request is already in flight.

Every gate writes a row. Who asked, what the parsed arguments were, which gate was applied, what the user did and what the backend returned. That row is what answers the question a disputed payment eventually raises, which is not “did the model behave” but “what exactly did this person approve”.

Common questions#

How should an agent confirm a payment? By stopping the write in server code, resurfacing the parsed arguments as one confirm card, and executing on a separate request that carries the approval identifier. The confirmation must be a branch in your code, never an instruction in the prompt.

Should the agent read the amount out loud? It should say enough to ask for a decision and leave the figures on the card. Speech arrives once and cannot be re-read, and a misheard amount sounds like a correct one.

When is a biometric worth requiring? On the actions whose worst case is irreversible or large. Applying it everywhere trains people to approve without reading. Android documents auth-per-use keys for high-value transactions, which is the right tier for it.

Can voice recognition authenticate the payer? No. ASVspoof 5 found spoofing detection degrading under adversarial attacks and neural compression across 53 teams’ submissions. Treat speech as an untrusted input and put the trust in the device gate.

Does WebAuthn confirm what the user is approving? It defines the authorization gesture and the distinction between user presence and user verification. Confirming the details of a specific transaction is not defined in the specification, so showing the amount and binding approval to it remains your application’s responsibility.

Sources#

Filed underConfirmationBiometricsFintechSecurity

Next

How an action is gated, what the confirm step guarantees, and what leaves the device.

Read how the security model is wired

The rest of Safety

Open the cluster

Confirm-first actions, biometric tiering, scoped tokens and the audit trail you need before an agent touches money.

Elsewhere on the map