Skip to main content
x402 is an open payment protocol built on the HTTP 402 status code: the server responds to an unpaid request with a 402 challenge, and the payer (typically an AI Agent) retries the request with an on-chain stablecoin authorization signature to complete payment and access the resource. Waffo packages x402 as a standard acquiring capability — orders, webhooks, and reconciliation work exactly like your existing Waffo integration, while Waffo handles on-chain verification and settlement. This page serves two audiences: merchant developers (choosing an integration mode, calling Waffo APIs, deciding when to release resources) as the primary readers; AI Agent developers can focus on the 402 challenge structure and how to submit PAYMENT-SIGNATURE — the Agent-side interaction requires no merchant API key.

Two integration modes

Mode 1: Merchant-hosted 402

Your server acts as the x402 server: it returns the 402 challenge to the Agent, receives the signature, then forwards it to Waffo for settlement through order/create. Suits merchants who want the Agent to stay on their own API end to end and are willing to implement the x402 protocol details.

Mode 2: Waffo-hosted 402

You only create the order and hand the Waffo-hosted URL to the Agent; the 402 challenge, signature collection, and settlement all happen on the Waffo cashier. Suits merchants who want to go live quickly without touching x402 protocol details.
Both modes share the same order/create endpoint. The only differentiator is whether the request’s x402Info contains paymentSignatureHeader — with a signature it is Mode 1, without one it is Mode 2.

Prerequisites

x402 acquiring requires two provisioning steps, both arranged through the Waffo integration team:
  1. Enable the CRYPTO / USDC payment product in your merchant contract
  2. Configure your on-chain receiving parameters on the Waffo side
Until provisioning is complete, wallet/inquiry and order/create return error code A0010 (operation not allowed in merchant contract).

Current scope

The following are hard boundaries of the current version — build against them and do not assume other capabilities:
  • Currency: USDC only; orderCurrency must be USDC (same-currency acquiring, no FX involved)
  • Network: Base only (eip155:8453 in production; Base Sepolia eip155:84532 in sandbox)
  • Scheme: exact only (settles exactly the amount in the challenge)
  • Authorization count: an EIP-3009 authorization is consumed once on-chain — one signature settles once; “authorize once, charge many times” is not supported
  • Idempotency: paymentRequestId is required; repeated order/create calls for the same order return the existing order’s real status without settling again (see Idempotency)

Fetching on-chain receiving parameters

Before constructing the 402 challenge in Mode 1, call POST /api/v1/wallet/inquiry to fetch the receiving parameters. This endpoint is query-only: it creates no order and validates no amount.
curl -X POST https://api-sandbox.waffo.com/api/v1/wallet/inquiry \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "X-SIGNATURE: YOUR_RSA_SIGNATURE" \
  -d '{
    "paymentRequestId": "x402-order-10001",
    "merchantInfo": { "merchantId": "M000001" },
    "orderCurrency": "USDC"
  }'
{
  "code": "0",
  "msg": "Success",
  "data": {
    "network": "eip155:84532",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "payTo": "0xc15Ea3D0b7A29c41F8b26aD5c30F49E20e510e71"
  }
}
FieldDescription
networkCAIP-2 network identifier. Sandbox returns Base Sepolia eip155:84532; production returns Base mainnet eip155:8453
assetUSDC token contract address (varies by environment and network; Base mainnet in production is 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913)
payToWaffo platform receiving address. Waffo receives on-chain and settles to you per your contract
All three fields come from Waffo configuration — use them verbatim when constructing the challenge. Do not replace payTo with your own wallet address: the receiving address and amount inside the Agent’s signature are strictly verified before settlement.

Mode 1: Merchant-hosted 402

You need to do four things:
  1. Call wallet/inquiry for network / asset / payTo, then build the 402 challenge from your pricing (amount in atomic units, scheme fixed to exact) and return it to the Agent
  2. Take the signature from the Agent’s retry request header PAYMENT-SIGNATURE and place it as-is into x402Info.paymentSignatureHeader of order/create (do not decode and reassemble it)
  3. Decide on release from the synchronous response’s orderStatus: release on PAY_SUCCESS, wait for the webhook or order/inquiry final state on PAY_IN_PROGRESS, reject on ORDER_CLOSE
  4. When releasing, return the response’s x402Info.x402Response to the Agent as the PAYMENT-RESPONSE header — the x402-compliant settlement receipt
curl -X POST https://api-sandbox.waffo.com/api/v1/order/create \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "X-SIGNATURE: YOUR_RSA_SIGNATURE" \
  -d '{
    "paymentRequestId": "x402-order-10001",
    "merchantOrderId": "x402-order-10001",
    "orderCurrency": "USDC",
    "orderAmount": "9.99",
    "orderDescription": "API usage credits",
    "notifyUrl": "https://merchant.example.com/webhook/waffo",
    "merchantInfo": { "merchantId": "M000001" },
    "userInfo": { "userId": "agent_7f3e", "userTerminal": "WEB" },
    "paymentInfo": { "productName": "ONE_TIME_PAYMENT", "payMethodName": "USDC" },
    "x402Info": {
      "x402Request": true,
      "paymentSignatureHeader": "eyJ4NDAyVmVyc2lvbiI6Miwic2NoZW1lIjoiZXhhY3QiLC4uLn0="
    }
  }'
{
  "code": "0",
  "msg": "Success",
  "data": {
    "paymentRequestId": "x402-order-10001",
    "merchantOrderId": "x402-order-10001",
    "acquiringOrderId": "A202607060001",
    "orderStatus": "PAY_SUCCESS",
    "x402Info": {
      "x402Request": true,
      "paymentSignatureHeader": "eyJ4NDAyVmVyc2lvbiI6Miwic2NoZW1lIjoiZXhhY3QiLC4uLn0=",
      "x402Response": "eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6IjB4Li4uIn0="
    }
  }
}
The remaining required fields (goodsInfo, orderRequestedAt, and so on) are identical to a regular one-time payment order — refer to the API Reference. paymentSignatureHeader is a base64-encoded x402 PaymentPayload. Decoded, it looks like this (generated by the Agent-side x402 SDK — merchants never construct it):
{
  "x402Version": 2,
  "scheme": "exact",
  "network": "eip155:84532",
  "payload": {
    "signature": "0x<65-byte EIP-712 signature>",
    "authorization": {
      "from": "0x<Agent wallet address>",
      "to": "0xc15Ea3D0b7A29c41F8b26aD5c30F49E20e510e71",
      "value": "9990000",
      "validAfter": "0",
      "validBefore": "1783340000",
      "nonce": "0x<32-byte random nonce (anti-replay, non-sequential)>"
    }
  }
}
authorization.to in the signature must equal the payTo returned by wallet/inquiry, and authorization.value must equal the atomic-unit representation of orderAmount (see “Amounts and decimals” below). Any mismatch fails verification before settlement and the order moves to ORDER_CLOSE. The price you quote to the Agent in the 402 challenge must therefore match the orderAmount of order/create.
x402Response is the base64-encoded settlement receipt (x402 SettlementResponse), returned only when orderStatus = PAY_SUCCESS. Decoded:
{
  "success": true,
  "transaction": "0x8f4e2b7c9d1a4f6e8b3c5d7a9e1f2b4c6d8e0a2c4e6f8a0b2c4d6e8f0a1b3c5d",
  "network": "eip155:84532",
  "payer": "0x51E2aF03D0A5c3C7dF83Db9F4b0a2E51D8c19aC3"
}
transaction is the on-chain transaction hash, usable for reconciliation and on-chain verification; payer is the Agent’s paying wallet address.

Mode 2: Waffo-hosted 402

You need to do three things:
  1. Call order/create with x402Info: { "x402Request": true } (no signature), plus successRedirectUrl / failedRedirectUrl
  2. Parse orderAction (a JSON string) from the response, take webUrl, and 302-redirect the Agent there; the 402 challenge, signature collection, and settlement all happen on the Waffo cashier from that point
  3. Release resources based on the orderStatus from the webhook or order/inquiry
curl -X POST https://api-sandbox.waffo.com/api/v1/order/create \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "X-SIGNATURE: YOUR_RSA_SIGNATURE" \
  -d '{
    "paymentRequestId": "x402-order-10002",
    "merchantOrderId": "x402-order-10002",
    "orderCurrency": "USDC",
    "orderAmount": "9.99",
    "orderDescription": "API usage credits",
    "notifyUrl": "https://merchant.example.com/webhook/waffo",
    "successRedirectUrl": "https://merchant.example.com/x402/return",
    "failedRedirectUrl": "https://merchant.example.com/x402/failed",
    "merchantInfo": { "merchantId": "M000001" },
    "userInfo": { "userId": "agent_7f3e", "userTerminal": "WEB" },
    "paymentInfo": { "productName": "ONE_TIME_PAYMENT", "payMethodName": "USDC" },
    "x402Info": { "x402Request": true }
  }'
{
  "code": "0",
  "msg": "Success",
  "data": {
    "paymentRequestId": "x402-order-10002",
    "merchantOrderId": "x402-order-10002",
    "acquiringOrderId": "A202607060002",
    "orderStatus": "AUTHORIZATION_REQUIRED",
    "orderAction": "{\"actionType\":\"WEB\",\"webUrl\":\"https://cashier.waffo.com/cashier/api/v1/x402/7sKq9WfR2mXt\"}"
  }
}
When the Agent visits webUrl, it receives HTTP 402 with the challenge in the response body (the x402 protocol places the challenge in the body — there is no dedicated response header):
{
  "x402Version": 2,
  "error": "PAYMENT-SIGNATURE header is required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "maxAmountRequired": "9990000",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "payTo": "0xc15Ea3D0b7A29c41F8b26aD5c30F49E20e510e71",
      "maxTimeoutSeconds": 600,
      "extra": { "name": "USDC", "version": "2" }
    }
  ]
}
After signing, the Agent retries the same URL using the same HTTP method as the first visit, carrying the signature in the PAYMENT-SIGNATURE request header (corresponding to X-PAYMENT in the x402 spec) — no merchant credentials required:
curl "https://cashier.waffo.com/cashier/api/v1/x402/7sKq9WfR2mXt" \
  -H "PAYMENT-SIGNATURE: eyJ4NDAyVmVyc2lvbiI6Miwic2NoZW1lIjoiZXhhY3QiLC4uLn0="
After settlement the cashier 302-redirects the Agent back to your successRedirectUrl or failedRedirectUrl. The hosted URL serves both browsers (renders a checkout page) and x402 clients (returns protocol responses).
The 302 redirect is only flow plumbing — it is not proof of payment. Treat an order as paid only when the webhook notification or an order/inquiry query shows orderStatus = PAY_SUCCESS. Redirects can be forged or lost, and settlement may not have finalized when the redirect fires.
On success, the webhook notification and the order/inquiry response carry x402Info.x402Response (same structure as Mode 1); the rest of the notification is identical to a regular order — see Webhook event types.

Mode comparison

DimensionMode 1: Merchant-hosted 402Mode 2: Waffo-hosted 402
Who acts as the x402 server (issues 402, receives signature)Merchant serverWaffo cashier
Must implement x402 protocol detailsYes (build challenge, parse signature header)No
order/create carries a signatureYes (x402Info.paymentSignatureHeader)No
Create responseFinal status directly, or PAY_IN_PROGRESSAUTHORIZATION_REQUIRED + hosted URL
RedirectsNone (Agent talks to merchant API throughout)Merchant 302 → cashier 302 back
Basis for releasing resourcesorderStatus from sync response / webhook / inquiryorderStatus from webhook / inquiry
Best forMerchants building their own x402 service with deep customizationMerchants shipping fast without touching protocol details
Order status semantics (PAY_IN_PROGRESS, AUTHORIZATION_REQUIRED, and so on) match other payment methods — see Payment lifecycle.

Amounts and decimals (two layers of units)

The same transaction carries amounts in two layers — do not mix them:
LayerFieldRepresentationExample (9.99 USDC)
Waffo API layerorderAmountString, up to 2 decimal places"9.99"
On-chain / x402 protocol layervalue, maxAmountRequiredString, atomic-unit integer (USDC has 6 on-chain decimals)"9990000"
Conversion: atomic units = amount × 106. The 2 decimal places for USDC in Currency and amounts refer to the Waffo API layer’s orderAmount; amounts in the 402 challenge and the EIP-3009 signature are always atomic units. In Mode 1 both layers must represent the same value, or settlement verification fails.

Error codes

x402 reuses Waffo’s existing external error codes; msg explains the specific reason:
CodeMeaningCommon x402 triggers
A0003Parameter validation failedThe challenge / signature scheme is not exact; paymentRequestId missing
A0007Transaction currency is not supportedorderCurrency is not USDC, or no usable same-currency USDC contract
A0010Operation not allowed in merchant contractCRYPTO/USDC payment product not enabled, or on-chain receiving parameters not configured
See the full list in the Error codes reference.

FAQ

The sandbox runs on the Base Sepolia testnet (eip155:84532) with USDC contract 0x036CbD53842c5426634e7929541eC2318f3dCF7e; production runs on Base mainnet (eip155:8453) with USDC contract 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. In both environments, always use the network / asset / payTo actually returned by wallet/inquiry.
Currently only USDC on the Base network. Passing any other orderCurrency returns A0007.
No. The x402 exact scheme is built on EIP-3009 transferWithAuthorization, whose nonce is consumed once on the USDC contract: once a signature settles it is void, and resubmitting it fails on-chain. Subscription-style or metered repeat charging is outside the scope of x402.
No. PAY_IN_PROGRESS only means on-chain processing is underway; it may still succeed or fail. Release resources only on orderStatus = PAY_SUCCESS, converged through the webhook or order/inquiry.
In the response body (JSON). There is no header carrying the challenge. The only protocol request header in the x402 interaction is PAYMENT-SIGNATURE, used by the Agent to submit the signature (corresponding to X-PAYMENT in the x402 spec); the settlement receipt is returned through the x402Info.x402Response field, corresponding to the spec’s X-PAYMENT-RESPONSE.
No. The AI integration tool uses AI coding tools to generate your Waffo SDK integration code; this page is about AI Agents paying you in stablecoin for your services. They can be used together but solve different problems.

Further reading