# Madhouse Wallet — Crypto Off-Ramp API (sell USDC, pay out to a bank)

> Paid, agent-callable crypto off-ramp API by Madhouse Wallet: sell USDC (or another stablecoin) and cash out to a bank account in 80+ currencies. Quote the FX rate and fees, discover the exact recipient bank fields per currency, create a sanctions-screened recipient, create a transfer that returns a Base USDC deposit address, fund it on-chain, then confirm and track the fiat bank payout. Convert crypto to fiat programmatically. Every call costs $0.01 USDC via the x402 protocol on Base, Polygon, or Arbitrum — no account or API key required. KYC of the funding wallet and sanctions screening are enforced server-side.

This file tells an AI agent exactly how to call this API. Every endpoint is
paid with the x402 protocol: $0.01 USDC per call on Base, Polygon, or Arbitrum.
No account or API key is required — only a funded EVM wallet.

## How payment works (x402)

- Every endpoint costs $0.01 USDC per call, paid with the x402 protocol on Base, Polygon, or Arbitrum (same recipient wallet on each).
- No account, signup, or API key is required — a funded EVM wallet is the only prerequisite for payment.
- The x402 payment challenge is returned BOTH in the `payment-required` response header (base64-encoded JSON) AND in the JSON body of the 402 response, so clients that read either will find it.
- Malformed requests (missing/blank required fields, bad currency, bad amount, bad wallet/UUID) are rejected with HTTP 400 BEFORE any payment is taken — you are never charged for a request that could not have succeeded on shape grounds.
- The x402 fee settles ONLY on a successful (2xx) response. A well-formed call that still fails downstream (e.g. an unverified funding wallet, or an unknown transfer id) returns an error and is NOT charged.
- Accepted networks: Base Mainnet (eip155:8453, USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913); Polygon Mainnet (eip155:137, USDC 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359); Arbitrum One (eip155:42161, USDC 0xaf88d065e77c8cC2239327C5EDb3A432268e5831).
- Payments go to: 0xf802283ff77D5972FFD109B91B70d34591B5459e.
- Recommended client: the official `x402` / `@x402/fetch` libraries handle the
  402 → sign EIP-3009 → retry loop automatically.

## Call order (the off-ramp flow)

```
offramp (alias of quote) → quote → requirements (→ requirements/refresh if conditional) → recipients → transfer → [send USDC on-chain to deposit_address] → confirm-transfer → transfer-status
```

### Step 0: GET /api/public/offramp
Crypto off-ramp entry point. THE off-ramp entry point. Alias of /quote: pass targetCurrency + sourceAmount to sell USDC/stablecoin and cash out to a bank account. Returns a quoteId and the recipient amount, then continue with requirements -> recipients -> transfer -> confirm-transfer.

- Produces (for later steps): quoteId, quote.targetAmount, usdToTargetRate
- Typically followed by: requirements, recipients, transfer
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/offramp)

### Step 1: GET /api/public/quote
Quote a crypto off-ramp (USDC to bank). Given a target currency (ISO 4217) and a USD source amount, returns the FX rate, fees, the amount the recipient receives (quote.targetAmount), and a quoteId that locks the rate for the transfer. Supports 80+ currencies.

- Produces (for later steps): quoteId, quote.targetAmount, usdToTargetRate
- Typically followed by: requirements, transfer
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/quote)

### Step 2: GET /api/public/requirements
Get recipient bank-detail requirements. Returns the dynamic recipient bank-detail fields required to pay out in a currency, grouped by account type, each field carrying its type (text/select/radio/date), whether it is required, validation rules, and (for select/radio) the allowed values. Read this to know exactly which details to collect before creating a recipient.

- Needs (from earlier steps): targetCurrency (as currency)
- Produces (for later steps): data[].type (account type id), field keys for details
- Call after: quote
- Typically followed by: requirements/refresh, recipients
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/requirements)

### Step 2 (optional): POST /api/public/requirements/refresh
Reveal conditional bank-detail fields. Only needed when a field has refreshRequirementsOnChange=true (e.g. picking a bank reveals branch fields). Send the currency, account type, and the details chosen so far; returns the updated field set with any newly-revealed fields.

- Needs (from earlier steps): currency, type, details (partial)
- Produces (for later steps): data[] (updated field set)
- Call after: requirements
- Typically followed by: recipients
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/requirements/refresh)

### Step 3: POST /api/public/recipients
Create a bank payout recipient. Creates the beneficiary from the currency, the account type id, the bank details collected via /requirements, the account holder legal name, and the funding wallet. Returns the recipient id used to create a transfer. The recipient is sanctions-screened upstream.

- Needs (from earlier steps): targetCurrency (as currency), data[].type, collected details
- Produces (for later steps): id (recipientId)
- Call after: requirements
- Typically followed by: transfer
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/recipients)

### Step 4: POST /api/public/transfer
Create the off-ramp transfer (USDC to bank). Creates the transfer from a locked quote and a recipient. Returns the transfer_id and a Base USDC deposit_address plus the exact amount to send there. This is where the on-chain funding step is defined.

- Needs (from earlier steps): quoteId (as quote_id), id (as recipientId), funding wallet_address
- Produces (for later steps): transfer_id, deposit_address, instructions.send_amount
- Call after: quote, recipients
- Typically followed by: confirm-transfer
- OFF-CHAIN ACTION: After this call, SEND the exact deposit amount of USDC on Base (from instructions) to deposit_address using your own wallet, then take the resulting on-chain transaction hash (tx_hash) to /confirm-transfer. This funding transfer is separate from — and much larger than — the 0.01 USDC x402 fee.
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/transfer)

### Step 5: POST /api/public/confirm-transfer
Confirm the funded off-ramp transfer. Submits the on-chain funding transaction hash so the payout pipeline proceeds. Pass transfer_id and the tx_hash of the USDC deposit you sent to deposit_address. Idempotent.

- Needs (from earlier steps): transfer_id, tx_hash (from the on-chain deposit you sent)
- Produces (for later steps): status
- Call after: transfer
- Typically followed by: transfer-status
- OFF-CHAIN ACTION: Requires the tx_hash of the USDC deposit you broadcast on-chain in the previous step. The API does not move funds for you — you are the on-chain actor.
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/confirm-transfer)

### Step 6 (optional): GET /api/public/transfer-status
Track the off-ramp / payout status. Returns the live transfer record by transfer_id: status, amount, currency, recipient and quote snapshots, deposit address, reference, and any error. Poll until status is completed or failed.

- Needs (from earlier steps): transfer_id (as id)
- Produces (for later steps): status, status_label
- Call after: transfer
- Full request/response schema: https://try.madhousewallet.com/openapi.json (operation for /api/public/transfer-status)

## Prerequisites (not ordinary inputs, but they gate success)

### Funding wallet verification
The funding wallet may need to be verified before a recipient/transfer can be created, depending on server policy. Verification status and the (human, browser-based) verification flow live on a SEPARATE service at kyc.madhousewallet.com, which has its own x402 discovery doc at https://kyc.madhousewallet.com/.well-known/x402 (endpoints: kyc/start, kyc/status, kyc/sanctions). Check kyc/status?wallet=0x… first; an unverified wallet is rejected at recipient/transfer creation. KYC (document + selfie) cannot be completed autonomously by an agent — it needs a human.
Check via: https://kyc.madhousewallet.com/api/public/kyc/status

### On-chain USDC funding capability
To complete a transfer the caller must be able to send USDC on Base to the deposit_address returned by /transfer and produce the transaction hash. The agent is the on-chain actor; the API never custodies or moves the payout funds.

## Worked example (NGN, $100)

```bash
# 1. Quote — get quoteId + how much the recipient receives
curl "https://try.madhousewallet.com/api/public/quote?targetCurrency=NGN&sourceAmount=100"   # pay $0.01 USDC
# 2. Requirements — learn the bank fields for NGN (which account type, which keys)
curl "https://try.madhousewallet.com/api/public/requirements?currency=NGN"
# 3. Create recipient — send the collected details + funding wallet, get recipientId
curl -X POST "https://try.madhousewallet.com/api/public/recipients" -d '{"currency":"NGN","type":"nigeria","accountHolderName":"Ada Lovelace","details":{"bankCode":"044","accountNumber":"0123456789", ...},"wallet":"0xYourFundingWallet"}'
# 4. Create transfer — from quoteId + recipientId, get transfer_id + deposit_address
curl -X POST "https://try.madhousewallet.com/api/public/transfer" -d '{"quote_id":"<quoteId>","amount":100,"recipientId":<id>,"customer_uuid":"<uuid>","customer_email":"you@example.com","source_token":"usdc","source_network":"base","wallet_address":"0xYourFundingWallet"}'
# 5. Fund on-chain: send the exact USDC amount on Base to deposit_address (NOT an API call)
# 6. Confirm — submit the on-chain tx hash of that deposit
curl -X POST "https://try.madhousewallet.com/api/public/confirm-transfer" -d '{"transfer_id":"<transfer_id>","tx_hash":"0x<fundingTxHash>"}'
# 7. Track
curl "https://try.madhousewallet.com/api/public/transfer-status?id=<transfer_id>"
```

## Common pitfalls

- Selecting a `select` field value: skip any option whose `key` is empty ("" /
  "Choose…" placeholder). The public requirements endpoint already strips these,
  but if you see one, do not send it.
- `amount` in /transfer must match the quoted `sourceAmount`.
- The 0.01 USDC x402 fee is separate from — and far smaller than — the payout you
  send to `deposit_address` in step 5.
- A blank/missing required field returns 400 for free; fix and retry.

## Install as an agent skill

A ready-to-use Claude-compatible Agent Skill is served live (it auto-triggers on off-ramp tasks once installed):
```bash
mkdir -p ~/.claude/skills/madhousewallet-offramp
curl -s https://try.madhousewallet.com/skill -o ~/.claude/skills/madhousewallet-offramp/SKILL.md
```

Machine-readable specs: https://try.madhousewallet.com/.well-known/x402 and https://try.madhousewallet.com/openapi.json · Agent skill: https://try.madhousewallet.com/skill
