Use this approval state machine
Show payee, source account, amount, fee, and total before approval. Bind the human action to those exact fields and a short expiry. If any field changes, return to review.
| Item | Check or owner | Evidence |
|---|---|---|
| Proposed | AI drafts amount and payee | No money moves |
| Reviewed | Human sees exact fields | Changes reset approval |
| Approved | Fresh user action is bound | Short validity window |
| Executed | Server checks approval ID | One transfer identity |
Test the boundary
Test replay, double tap, stale session, changed fee, changed payee, and interrupted execution. The payment service owns the final transition to executed and must make it idempotent. Show a receipt based on provider and ledger state, not a generated sentence.
Worked synthetic case
Synthetic case: An assistant drafts a ₦5,000 payment to one payee. The human reviews the amount, fee, source account, and destination. After approval, the assistant changes the payee and attempts execution.
Define states for drafted, shown, approved, executed, expired, cancelled, and uncertain. The pass condition is that changed fields return to review, stale approvals fail, and two concurrent execution requests create one transfer identity. Keep approval and provider IDs for proof.
Bind approval to exact fields and a short expiry, then verify it in the payment service. A chat reply such as “looks good” is not a reusable authorization token. A clear review screen adds friction but gives the user a real chance to catch a wrong destination.
When provider status is unknown, show “checking payment” and reconcile. Do not invite another payment until the first outcome is known. The final receipt should reflect provider and ledger state, not a model-generated claim.
Handle a changed approval after review
A human approves a synthetic payment to recipient A for ₦5,000. Before release, a queued agent changes the recipient to B while keeping the approval reference. The release service must compare the approved snapshot with the actual instruction and reject the changed request. It should record who approved, what they saw, when it expires, and which exact transfer was attempted. A rejected attempt must leave no provider call or ledger debit. If the human later approves B, create a new approval version rather than editing A’s record. Test approval reuse, expiry, and a worker restart so the checkpoint holds outside the chat session.
Separate accepted, pending, and settled
A reviewer’s approval means one version may be submitted. Submission means the provider received an instruction. Settlement is a later outcome defined by that provider. Keep draft, shown, approved, submitted, pending, succeeded, failed, and reversed states separate where the integration needs them. Show a final receipt only for the verified result. A generated sentence cannot choose the state.
Synthetic payment INT-72 has recipient A, 2,500,000 kobo, and a 10,000-kobo fee. The review view uses version 4 and displays a total of ₦25,100. Change the fee to 15,000 kobo before confirmation. The service rejects version 4 and asks for review of version 5. The view and approval must use the same units and final destination snapshot.
Test cancellation at the real cut-off
Cancel before the service claims approval: no execution record is created. Cancel after a durable submission task exists but before dispatch: apply the stated queue-cancellation rule. Cancel after provider acceptance: report the known provider state and use its reversal or support path; do not promise the original transfer vanished. Run each timing case with a controllable worker.
Claim the approved version and create durable execution work in one local database transaction. A worker sends that work with a stable provider reference. Two clicks may create two API requests, but one approved intent must have one transfer identity. Crash after provider acceptance and reconcile the saved reference before offering a new payment. Keep displayed version, approval actor, cancellation time, execution state, and provider reference. Paystack’s transfer guide distinguishes submission from final status; the state names should follow the chosen provider.