---
name: agentbank-pay
description: Use the current HumanFX MCP to onboard an external agent, check identity and payment readiness, discover routes, estimate and create direct or two-hop payments, use the shared Privy wallet, manage recipients, and track or recover durable payments safely.
---

# AgentBank Pay

## Install the MCP

Install the published production MCP directly from npm without environment
overrides:

```bash
codex mcp add agentbank -- npx -y agent-bank-mcp@latest
```

Use the HumanFX MCP production tools for payment and wallet work. Treat HumanFX
Core as the authority for approvals, locked routes, payment instructions,
transaction verification, and terminal payment state.

Do not use or ask for legacy intent, route-agreement, approval, settlement,
partner, raw-swap, or progress-reporting mutations. They are not exposed by the
production server.

## Safety rules

- Never request or expose private keys, seed phrases, HumanFX JWTs, Privy
  tokens, authorization keys, or World ID proofs.
- Never infer a wallet address, bank account, recipient, token contract, chain,
  decimals, amount, or calldata from weak context.
- Use decimal strings for human amounts.
- Show the complete recipient, source amount, destination amount, fees with
  fee currencies, route, and expiry before `create_payment`.
- Set `confirmed_by_user=true` only after the human confirms that summary.
- Use only a current server-generated payment instruction to move funds.
- A transaction hash or successful receipt is not payment completion. Trust
  `get_payment`.
- Do not expose partner identity. Current quote tools intentionally return
  anonymous offers.

## Runtime source of truth

Call `get_instructions` with the relevant journey when starting an unfamiliar
flow or recovering after an interruption:

```text
setup
pay
track
recover
manage_recipients
manage_wallets
```

The MCP also exposes `humanfx://guides/routing` and
`humanfx://instructions/{journey}` as resources. Follow newer runtime guidance
when it does not conflict with these safety rules.

## Request IDs

Generate a stable `request_id` for each logical mutation:

```text
create payment
continue payment
submit swap transaction
send one EVM transaction
approve one token allowance
cancel payment
correct payment recipient
create recipient
replace recipient
```

Reuse the same ID only when retrying the same tool call with the same payload.
Use a different ID for a changed payload or a different on-chain transaction.
Do not treat a wallet transaction request ID as a payment ID.

## Asset and amount format

Use canonical assets:

```json
{"type":"crypto","ticker":"USDC","chain":"worldchain"}
```

```json
{"type":"fiat","symbol":"VND"}
```

For `exact_source`, put the exact amount in `source.amount`. For
`exact_destination`, put it in `destination.amount`.

Use `list_currencies` whenever a ticker, fiat code, chain, token address, or
decimals need verification. Do not pass `USDC@worldchain` in an asset object.

## 1. Set up the agent

Start by calling `whoami`.

If it succeeds, call `get_account_status` and continue with the existing
installation.

If it returns `MISSING_CREDENTIAL`:

1. Call `begin_agent_onboarding` once.
2. Show the returned `authorization_url` to the human.
3. Explain that the one browser approval claims the HumanFX agent and grants
   access to the shared Privy user wallet.
4. After the human approves, call `wait_for_agent_onboarding` with the returned
   `enrollment_id`.
5. Verify `humanfx_claimed`, `privy_authorized`, `wallet_bound`, and
   `authenticated`.
6. Call `whoami`, `check_my_scopes`, `get_account_status`, and `list_wallets`.
   Treat `check_my_scopes` as coarse scope guidance because its current map
   still includes legacy names; use the actual MCP tool list and backend errors
   as authoritative.

Do not call a legacy registration alias. Do not start a second onboarding flow
while a pending flow exists.

When the human asks to log out or reset this local agent:

1. Explain that revocation invalidates the installation, sessions, and bound
   wallet authorization for this agent and clears local credentials.
2. Obtain explicit confirmation.
3. Call `revoke_agent({"confirm":true})`.

## 2. Handle identity requirements

Use `check_verification_status` when KYC or badges matter.

If KYC is missing:

1. Call `do_kyc`.
2. If it returns `already_verified`, continue.
3. If it returns `kyc_url`, show the URL to the human.
4. Ask the human to complete Didit verification.
5. Call `check_verification_status` again before retrying a gated action.

Use `get_verification_guidance` when the human needs the first-party profile
page or World ID badge guidance. Never ask the human to paste identity proofs
into chat.

Payment World ID approval is a separate per-payment action returned by
`create_payment`. Do not replace it with a badge check.

## 3. Understand the payment request

Collect:

- source asset and chain, or source fiat currency;
- exact source or exact destination amount;
- destination asset/currency and country;
- recipient or destination wallet;
- optional routing preference.

Routing preferences are:

```text
balanced
lowest_total_cost
fastest
highest_success_rate
```

Use `balanced` when the user gives no preference.

Do not silently split an amount, switch chains, change exactness, or choose a
different recipient.

## 4. Resolve the recipient

For a saved destination:

1. Call `list_recipients`.
2. Reuse a record only when the user request clearly matches its rail and
   canonical fields.
3. Call `get_recipient` when full fields are needed.
4. Ask the human to choose when multiple records match.

Do not describe a recipient as invalid solely because `verified` is false.
That flag means verified holder metadata has not been established; route and
partner validation remain authoritative.

For a new fiat or crypto recipient, call `create_recipient` with one or more of:

- canonical `fields`;
- structured `bank_info`;
- labeled `pasted_text`;
- raw `qr_content`;
- a QR image.

When the human sends recipient information through chat as an image, raw QR
payload, pasted bank text, account/holder details, or structured bank data,
call `create_recipient` before `estimate_payment` or `create_payment`. Use the
returned `recipient_id` or canonical `recipient_fields`; do not manually copy
unvalidated image/QR fields directly into a payment request.

For local stdio, an image may use an absolute `image.path`. Remote clients use
`image.data_base64`. The image must contain a readable QR. If it is a text-only
screenshot, pass the visible details as `pasted_text` or `bank_info`; OCR is not
implemented.

If `create_recipient` returns `information_required`, ask only for the listed
missing or invalid fields and retry with the same request ID only if the payload
is unchanged. Use a new request ID after adding or changing fields.

On success, use either the returned `recipient_id` or canonical
`recipient_fields` in payment tools.

Use `update_recipient` only after the human confirms the replacement fields.
It creates a replacement record; it does not edit or revoke the old record.

For an on-ramp into the shared wallet, call `list_wallets` and use the active
Worldchain wallet address as the crypto recipient. Never ask for its private
key.

## 5. Discover a supported route

Call `list_quote_book_pairs` to inspect live direct on/off-ramp corridors.

For a direct ramp, call `get_payment_capabilities` with the source and
destination.

For fiat-to-fiat or source-token-to-fiat routing:

1. List relevant on-ramp and off-ramp pairs.
2. Find a common active crypto asset on one supported chain.
3. Prefer the route requested by the user. If none was requested, compare live
   executable outcomes; do not assume the raw quote-book rate includes fees.
4. Call `get_payment_capabilities` with
   `route.intermediate_asset` set explicitly.

There is no automatic Core route planner. Do not ask Core to invent a two-hop
route.

Use `browse_quote_book` only for anonymous rough-rate or band discovery. Its
`rate` is raw. Read `fee_pct`, `flat_fee`, and `fee_ccy` together.

Use `get_ramp_quote` only for a direct `on_ramp` or `off_ramp`. Never pass
`on_chain_swap` to it.

## 6. Estimate the complete payment

Call `estimate_payment` for every supported flow:

- direct on-ramp;
- direct off-ramp;
- pure same-chain crypto swap;
- explicit fiat-to-fiat two-hop;
- explicit crypto-token-to-fiat two-hop.

For two hops, pass `route.intermediate_asset`. A concrete final recipient is
required.

Treat the result as an ephemeral review preview:

- it has no estimate ID;
- it is not durable;
- it may create quote intents;
- it never creates approval, settlement, or execution calldata;
- `create_payment` validates the route independently;
- its quote references can expire.

Require `status=estimate_ready`. Read:

- `source_amount`;
- `destination_amount`;
- fee and fee currency for every leg;
- effective request-specific amounts;
- expiry;
- route and intermediate amount;
- recipient validation;
- returned `hops`.

If no executable estimate is available, explain the blocking requirement and
stop or select another live route with the user's approval.

## 7. Confirm once

Show one complete summary before creating the payment:

```text
Recipient: [name/rail and sufficient destination details]
You send: [amount and asset]
Recipient receives: [amount and asset]
Fees: [each amount and its currency]
Route: [direct, swap, or source -> intermediate -> destination]
Estimate expires: [time]
Expected duration: [when available]
Material warnings: [only relevant warnings]
```

Do not show a raw rate as the effective customer rate when fees change the
actual source/destination amounts.

Ask the human to confirm the complete payment. Reconfirm after any material
change to recipient, source amount, destination amount, fee, route, or expiry.

## 8. Create and approve the durable payment

After confirmation, call `create_payment` with:

- a new stable `request_id`;
- `confirmed_by_user=true`;
- the reviewed source, destination, amount mode, and routing preference;
- top-level `intermediate_asset` for two hops;
- the exact `hops` returned by the current estimate.

Do not pass an estimate ID. None exists.

For two-hop payments, preserve the returned structure:

- hop 0 is `on_ramp` or `on_chain_swap`;
- hop 0 has `recipient_ref:{"hop_index":1}`;
- hop 1 is `off_ramp`;
- hop 1 has concrete `recipient_fields`.

When the payment returns `approval_required`:

1. Show `approval.approval_url`, the first-party action page.
2. State that the human must sign in as the payment owner before the page reveals the World ID request.
3. State the approval expiry.
4. Ask the human to approve in World App.
5. Never request, print, reconstruct, or transmit a raw World ID QR, verification URL, or proof.

After the human approves, call `get_payment`. When status is `approval_ready`,
call `continue_payment` with a new continuation request ID.

## 9. Follow the payment instruction

Read `payment_instruction` and `next_action` exactly.

### Two-hop funding invariant

For a linked two-hop payment, the agent acts only on the first/source hop and
then tracks the aggregate:

- API `hop_index:0` is the first upstream `on_ramp` or `on_chain_swap` hop.
- API `hop_index:1` is the downstream `off_ramp` hop.
- Core opens hop 1 first only to obtain its crypto deposit destination.
- Hop 0 is already bound to that destination through
  `recipient_ref:{"hop_index":1}`.
- Funding or executing hop 0 therefore delivers the intermediate crypto
  directly into hop 1. No second wallet transfer is required.

For fiat-to-fiat, ask the human to pay the source on-ramp instruction, then poll
`get_payment`. For crypto-token-to-fiat, execute the source swap instruction;
its output recipient is already the downstream off-ramp deposit, then poll
`get_payment`.

Never separately fund hop 1 after hop 0 is paid or executed. If a later payment
view exposes hop 1's crypto-deposit details while the linked payment is still
processing, treat them as internal routing/tracking context, not a new funding
request. A second manual transfer would duplicate funding. This rule overrides
the generic crypto-deposit instructions below for linked two-hop payments.

If `next_action.action_url` or `payment_instruction.presentation_url` exists,
use it for human-executed fiat funding such as bank transfer, QR, payment-link,
or mobile money. For `crypto_deposit`, treat the page as optional presentation;
execute the exact instruction through the shared Privy wallet in chat after
confirmation.

### Fiat funding

For on-ramp bank, QR, payment-link, or mobile-money instructions:

1. Show the action URL and exact amount/expiry.
2. Ask the human to complete the fiat payment.
3. Do not call `send_evm_transaction` for fiat funding.
4. Poll `get_payment` after the human pays.

### Crypto deposit funding

For a direct one-hop off-ramp crypto deposit:

1. Show the exact chain, asset, amount, full destination, memo/reference, and
   expiry.
2. Do not ask the human to open a frontend payment page. The agent controls the
   onboarding-bound Privy wallet and should execute the current instruction in
   chat after confirmation.
3. Call `get_wallet_balances` for the required asset.
4. Obtain explicit confirmation before sending.
5. If the instruction contains exact EVM target/value/data, pass them unchanged
   to `send_evm_transaction`.
6. If it provides an ERC-20 address/amount deposit, resolve the token contract
   and decimals from `list_currencies`, encode only the standard transfer for
   that exact destination and atomic amount, and call `send_evm_transaction`.
7. If the send returns a chain hash, call `get_transaction_receipt` with
   `tx_hash`. If it returns `pending`, call `get_transaction_receipt` with the
   original `request_id` until Privy resolves the final chain hash and receipt.
8. Continue polling `get_payment`; do not call
   `submit_payment_transaction` for a normal off-ramp deposit.

Never send a rounded amount when the instruction requires an exact amount.

### Pure swap or swap hop

For `payment_instruction.type=swap_execution`:

1. Read only the fresh execution returned by `continue_payment`/`get_payment`.
2. Call `get_token_allowance` for the input token and exact allowance target.
3. If insufficient, show the approval target and atomic amount, obtain human
   confirmation, and call `approve_token`.
4. If approval was submitted, poll `get_transaction_receipt` by `tx_hash` or by
   the approval `request_id` when the sponsored send is still pending.
5. Submit each current server-returned swap call in order with
   `send_evm_transaction`; use a separate request ID for each transaction.
6. Poll each receipt by hash, or by its original request ID until the sponsored
   transaction produces a hash.
7. Call `submit_payment_transaction` with the payment ID, current instruction
   ID, and swap transaction hash.
8. Poll `get_payment` while Core verifies the transaction.

Do not invent calldata, allowance targets, token contracts, or amount ceilings.
For exact destination, never spend more than the confirmed source ceiling.

## 10. Track the payment

Use `get_payment` as the authoritative state. Follow
`next_action.poll_after_seconds` when present.

Current statuses are:

```text
approval_required
approval_ready
funding_required
funding_detecting
recipient_correction_required
completed
cancelled
failed
```

Do not report a two-hop payment complete until the aggregate is `completed`.
One completed hop is not complete payment delivery.

Use `list_payments` to find historical payments for the current installation.
When the user refers to an earlier payment ambiguously, compare source,
destination, amount, status, and time, then ask which one they mean if multiple
records fit.

## 11. Recover safely

Always call `get_payment` before retrying a mutation.

### Recipient correction

When status is `recipient_correction_required`:

1. Read the failure and current destination context.
2. Ask for corrected fields.
3. Show the change and obtain confirmation.
4. Call `correct_payment_recipient` with a new request ID and
   `confirmed_by_user=true`.
5. Resume polling.

### Cancellation

Call `cancel_payment` only after the human confirms and only before funds move.
There is no separate approval-cancel tool; leave an unwanted pending approval
unapproved.

If `funds_moved=true`, do not promise cancellation, duplicate funding, or create
a replacement payment automatically.

### Failure

Read `failure.code`, `failure.stage`, `failure.message`, `retryable`, and
`funds_moved`.

- If funds did not move and the route/approval expired, create a fresh estimate
  and obtain fresh confirmation before a new payment.
- If funds moved, explain the state and continue tracking or escalate. Current
  Core has no automatic partial two-hop recovery action.
- Never convert a failed payment into success based on a wallet receipt alone.

## 12. Tool selection reference

Use these current tools only:

```text
Setup: begin_agent_onboarding, wait_for_agent_onboarding,
       get_installation_status, revoke_agent
Identity: whoami, get_account_status, check_my_scopes,
          check_verification_status, do_kyc, get_verification_guidance
Discovery: list_currencies, list_quote_book_pairs, browse_quote_book,
           get_ramp_quote, get_payment_capabilities, estimate_payment
Payments: create_payment, continue_payment, submit_payment_transaction,
          get_payment, list_payments, cancel_payment,
          correct_payment_recipient
Recipients: list_recipients, get_recipient, create_recipient, update_recipient
Wallet: list_wallets, get_wallet_balances, get_token_allowance,
        approve_token, send_evm_transaction, get_transaction_receipt
Guidance: get_instructions
```
