Skip to content

The x402 payment, step by step

Agentware uses x402 version 2 with the exact scheme. The token is USDG on Robinhood Chain (eip155:4663), moved with a Permit2 witness transfer. Any compatible x402 client works. This page shows what happens underneath.

One time: approve Permit2

USDG doesn't support gasless signed transfers natively, so payments go through the standard Permit2 contract. Approve it once:

USDG.approve(0x000000000022D473030F116dDEE9F6B43aC78BA3, max)

This is the only transaction your wallet ever sends. You can send it through any Robinhood Chain RPC, including /api/rpc.

Each purchase is two payments

OrderEndpointAmountRecipient
1POST /api/pay/treasury10% of priceUnits, rounded downPlatform treasury
2POST /api/pay/sellerThe remaining 90%The owner of the package

Both take the same body: { "packageId": 322 }. Pay the fee first. The seller endpoint answers 409 until the fee is recorded for your wallet.

The exchange

1. Ask. POST without a payment. The server answers 402:

json
{
  "x402Version": 2,
  "error": "Payment is required (…).",
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:4663",
    "amount": "300000",
    "asset": "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
    "payTo": "0x…",
    "maxTimeoutSeconds": 300,
    "extra": { "assetTransferMethod": "permit2", "spender": "0x402085c248EeA27D92E8b30b2C58ed07f9E20001", "name": "USDG" }
  }],
  "resource": { "url": "https://agentware.sh/api/pay/treasury" }
}

The same document is also sent base64-encoded in the payment-required header.

2. Sign. Sign this EIP-712 message with the paying wallet:

Domain{ name: "Permit2", chainId: 4663, verifyingContract: 0x000000000022D473030F116dDEE9F6B43aC78BA3 }
Primary typePermitWitnessTransferFrom
permitted{ token: asset, amount }
spenderextra.spender
nonceA random 256-bit number
deadlineNow plus a few minutes, in seconds
witness{ to: payTo, validAfter: 0 }

Types:

PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,Witness witness)
TokenPermissions(address token,uint256 amount)
Witness(address to,uint256 validAfter)

The recipient is inside the signed witness, so the payment can only ever go to payTo.

3. Pay. Repeat the same request with the same body, adding the header payment-signature: a base64-encoded JSON payload.

json
{
  "x402Version": 2,
  "accepted": { "…the accepts entry you chose…" },
  "resource": { "url": "…" },
  "payload": {
    "signature": "0x…",
    "permit2Authorization": {
      "permitted": { "token": "0x5fc5…d168", "amount": "300000" },
      "from": "0xYourWallet",
      "spender": "0x4020…0001",
      "nonce": "…",
      "deadline": "…",
      "witness": { "to": "0x…", "validAfter": "0" }
    }
  }
}

4. Settled. The server verifies the signature, settles it onchain (a relayer pays the gas) and answers 200. The payment-response header carries a base64 receipt with the transaction hash.

json
{ "paid": true, "tx": "0x…" }

The seller payment's response also includes the package and the file to save it as:

json
{ "paid": true, "tx": "0x…", "body": "The package…", "filename": "SKILL.md" }

If your wallet already paid the fee for this package, the treasury endpoint answers { "paid": true, "tx": "0x…", "already": true } and settles nothing.

Responses you may see

StatusMeaning
402 with permit2_insufficient_balanceThe wallet doesn't hold enough USDG
402 "The payment doesn't match the current price or recipient."You signed different terms from the ones offered. Fetch a fresh challenge
402 "Malformed payment header."The header isn't valid base64 JSON
402 "Payment verification failed" or "Payment settlement failed"The facilitator refused the payment. The reason follows in the message
402 "The payment facilitator didn't respond"A temporary problem. Try again
404 "Package not found."No live package has that packageId
409 "Pay the platform fee first."Make the treasury payment before the seller payment
409 "You've already bought this package."This wallet already bought it. Nothing is charged
503 "Payments aren't open yet."Payments are switched off

Rules worth enforcing in your agent

  • Check asset is USDG and amount is no more than the package's priceUnits before signing.
  • Never put ids in the URL of a paid endpoint. They go in the body, and the body is resent with the payment.
  • A signed payment expires with its deadline. Sign right before you send.
  • Read the scan report before you buy. A scan lowers risk. It is not a guarantee. Review a package before your agent runs it.

A scan lowers risk. It is not a guarantee. Review a package before your agent runs it.