# How do I require human approval before an AI agent spends money?

**Short answer:** Put the approval in code the model can't skip, at the point where money moves, not in the prompt. Make every tool that pays, buys, signs a payment or calls a paid API pause and ask a person first: in the OpenAI Agents SDK set needsApproval on that tool, in LangGraph call interrupt() before the payment, and in an MCP client keep confirmation prompts on for tool calls (the MCP spec says a human SHOULD be able to deny tool invocations). Show the person the exact merchant, amount, currency and payee, bind their yes to those details (for example a hash of them), and refuse to pay if anything changed after approval. Add hard limits below the approval step: a per-purchase and daily cap, an allowed-merchant list, and short-lived credentials. Card issuers such as Stripe Issuing enforce limits like these before authorization, and its real-time authorization webhook gives you only 2 seconds to answer, so a person can't approve inside that window; get the approval before the agent starts the purchase.

## Checklist
1. List every tool that can move money: card or wallet payments, x402 or other pay-per-call APIs, checkout, refunds, invoices, transfers, and anything that buys credits. Treat signing a payment authorization as spending.
2. Make each of those tools require approval in code: needsApproval: true (or a function) in the OpenAI Agents SDK, interrupt() placed before the payment call in LangGraph, a permission rule or hook in your coding agent, confirmation prompts left on in your MCP client.
3. Show the approver the facts, not the agent's summary: merchant or payTo address, amount with currency and decimals, network (mainnet or testnet), what is being bought, and whether the price is a ceiling (x402 upto) or exact.
4. Bind the approval to those facts: hash the canonical JSON of the purchase and check the hash again right before paying. If the amount, payee or item changed, ask again.
5. Give approvals a short expiry and make them single-use, so an old yes can't be replayed for a second purchase.
6. Set limits that hold even if approval is skipped or wrong: per-transaction and daily caps, allowed merchants or categories, and a card or wallet that only holds what the agent may spend.
7. Decide what runs without a person (small, allow-listed, under a cap) and what always needs one, and write it down. AP2 calls the unattended case 'human not present' and requires the user to confirm the conditions up front.
8. Keep a record of who approved what and when, and make the payment step idempotent so a resumed or retried run doesn't pay twice.

## Where the approval has to live
- A line in the system prompt saying 'ask before buying' is a request to the model, not a control. Text the agent reads from web pages, emails or tool results can talk it out of asking. Put the check in the tool runner, where the model's output can't remove it.
- OpenAI Agents SDK (JS): a tool with needsApproval set doesn't run. The run pauses and returns the pending call in result.interruptions; you approve or reject it on result.state and resume from the same state. needsApproval can be a function, so you can approve small allow-listed purchases in code and pause the rest.
- LangGraph: interrupt() pauses the graph and saves state with a checkpointer (use a durable one in production); you resume with Command(resume=...) on the same thread_id. The node re-runs from its start on resume, so code before interrupt() runs twice. Put the payment after the interrupt, never before it.
- MCP: the tools spec says that for trust and safety there SHOULD always be a human in the loop with the ability to deny tool invocations, and that clients SHOULD show confirmation prompts. Don't turn off confirmations for a server that has payment tools. For entering card details or completing a checkout, the elicitation spec says servers MUST NOT ask for payment credentials in form mode and MUST use URL mode, so the details go to the payment site, not through the client.

## Approval plus limits, not approval alone
- People approve quickly when asked often, so pair approval with limits that don't depend on attention. Stripe Issuing spending controls can block merchant categories and countries and set spending_limits per authorization or over an interval such as weekly or monthly (date-based intervals start at midnight UTC); if you set none, a new card gets a default limit of 500 USD per day.
- Stripe's real-time authorization webhook must answer within 2 seconds, or the authorization is approved or declined by your timeout settings. That is long enough for code checking an approval you already have, not for asking a person. Get the approval first, then let the payment run.
- AP2 (Agent Payments Protocol) separates the two cases. With the human present, the user approves the final cart on a trusted surface and that approval creates a signed Checkout Mandate. With the human not present, the user first confirms the conditions (for example 'under $100') with in-session authentication, and the agent may buy only inside them.

## What can go wrong?
- The approval screen shows the agent's description instead of the real payee and amount. Read them from the payment request itself.
- The price changes between approval and payment (a new quote, a higher x402 amount, a different token). Re-check the hash of the details and ask again.
- A resumed or retried run pays twice. LangGraph re-runs the node on resume, and network retries resend requests; use an idempotency key on the payment.
- One approval covers more than intended, such as 'yes' to one purchase being reused for the next. Make approvals single-use and expire them.

## Ask Rightshold (free, no key)
Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code (one-click setup: https://www.rightshold.com/start?ref=seo), then ask: "Before you pay for this, ask me to approve the exact purchase (merchant, amount, currency and what it's for) and wait for my yes or no."

```json
{
  "tool": "request_human_approval",
  "arguments": {
    "summary": "Buy 1 month of the Pro API plan from api.example.com for 20.00 USD.",
    "action": {
      "type": "purchase",
      "merchant_domain": "api.example.com",
      "amount_minor": 2000,
      "currency": "USD",
      "item": "pro-plan-1-month"
    },
    "expires_in_minutes": 30
  }
}
```
- Needs your API key. The action is hashed, and you decide once with decide_human_approval (approve or deny); the decision is a signed, timestamped receipt. The token works once.
- verify_human_approval (free) checks a decision receipt and that the action matches what was approved, so a changed amount or payee doesn't pass on the same yes.
- With an account key, set_spend_policy sets per-purchase and daily caps and allowed merchants, and authorize_spend returns a signed allow or deny for each purchase. Both record decisions only and never move money.
- REST, free: POST /v1/x402/price-tag decodes an x402 402 response so the approver sees the real amount, token, network and payTo.

_Rightshold records approval requests, your signed decisions and spending-limit checks. It doesn't hold funds, make or block payments, or stop an agent that never asks. Enforce the check in your tool runner, wallet or card controls._

## Sources
- [OpenAI Agents SDK (JS): Human-in-the-loop (needsApproval, interruptions)](https://openai.github.io/openai-agents-js/guides/human-in-the-loop/)
- [LangGraph: Interrupts (interrupt, Command(resume), idempotent side effects)](https://docs.langchain.com/oss/python/langgraph/interrupts)
- [Model Context Protocol specification 2026-07-28: Tools (human in the loop)](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)
- [Model Context Protocol specification 2026-07-28: Elicitation (URL mode for payment credentials)](https://modelcontextprotocol.io/specification/2026-07-28/client/elicitation)
- [Stripe: Issuing spending controls](https://docs.stripe.com/issuing/controls/spending-controls)
- [Stripe: Issuing real-time authorizations (2-second response)](https://docs.stripe.com/issuing/controls/real-time-authorizations)
- [AP2 (Agent Payments Protocol): overview, human present and not present](https://github.com/google-agentic-commerce/AP2/blob/main/docs/overview.md)

## Related
- [How to stop an AI agent from paying twice when it retries](https://www.rightshold.com/answers/how-to-stop-an-ai-agent-from-paying-twice-when-it-retries)
- [How to read an x402 402 Payment Required response as a price](https://www.rightshold.com/answers/how-to-read-an-x402-payment-required-response-as-a-price)
- [How to audit which human authorized an AI agent action](https://www.rightshold.com/answers/how-to-audit-which-human-authorized-an-ai-agent-action)

_Not legal advice. Updated 2026-10-30. JSON: https://www.rightshold.com/answers/how-to-require-human-approval-before-an-ai-agent-spends-money.json_