Agent Payment Approval Workflow

When an agent has no autonomous authority for a payment, how does a human "yes" become a safe, once-only execution?

The question

Most spending policies include a human line: amounts above $X, new recipients, or flagged actions need a person's approval. But what does approval mean in an agent system? If "approved" just means the model was told "okay, go ahead," then the human has only added one more layer of conversation — not a control.

The useful property of approval is that it converts a human decision into bounded, executable authority: authority over this specific request, for this specific amount and recipient, usable exactly once.

Human approval should create bounded authority, not an open-ended permission.

Why naive approval is insufficient

Naive patternWhat actually happens
"Ask the model to wait for a yes"The model is the thing being attacked. A yes in chat is a fact in context, not a control.
"Enable the payment tool after approval"That's permanent capability — the agent now spends freely until someone remembers to disable it. Approval became a switch, not a decision.
"Log the approval message"Logs describe. They don't stop a second execution of the same intent.

Approval that is not bound to the specific intent, and not consumed by execution, drifts back into the category of things an agent can argue with or replay.

The workflow

1. payment request (agent, amount, recipient, purpose)
        │
2. policy evaluation
        │
3. APPROVAL REQUIRED        ← over the autonomous line / new recipient / flagged
        │
4. human approves           ← sees the actual request: amount, recipient, agent
        │
5. signed one-time grant issued   (bound to amount + recipient + agent + policy version)
        │
6. executor verifies grant
        │
7. grant consumed
        │
8. execute once
        │
   replay of the same grant → refused (step 6 fails)

Each property matters:

Failure modes this prevents

Minimal code

res = shield.authorize(agent="trading-agent", amount=250, to="exchange.example.com")
if res.decision == "APPROVAL":
    # human reviews the actual request…
    res = shield.approve(res.approval_id, by="ops-reviewer")
    # re-evaluated against current policy; only ALLOW proceeds

if res.decision == "ALLOW":
    grant = issuer.issue(agent="trading-agent", amount=250,
                         currency="USD", merchant="exchange.example.com",
                         policy_version=res.policy_version)
    ok, reason = executor.verify(grant, agent="trading-agent", amount=250,
                                 currency="USD", merchant="exchange.example.com")
    # ok → execute; second verify of the same grant → (False, "REUSED")

ALLOW / APPROVAL / DENY in practice

RequestOutcome
$25 → trusted merchant (under autonomous line)ALLOW — no human needed
$250 → trusted merchant (over line)APPROVAL → human grants → executes once
same grant replayedREFUSED — consumed already
human approves, then merchant gets blocked before executionDENY — approval re-checked against current policy

Where SpendShield fits

SpendShield implements this workflow: approval requests carry an approval_id, approve() re-evaluates against the live policy, and ALLOW produces the signed one-time grant that the executor consumes. Open source, MIT, Python + MCP. Try it interactively in the policy playground — raise the approval line and watch a payment switch from ALLOW to APPROVAL.

Related pages

AI Agent Payment Authorization AI Agent Spending Limits x402 Spending Limits Prevent AI Agents From Overspending

← Home · GitHub · MIT licensed