x402 wire format
x402 is the per-call payment standard the lodestone path uses. An agent signs an EIP-3009 transferWithAuthorization for the call’s exact amount, attaches the signature as a request header, and the API settles synchronously through a payment facilitator. No on-chain transaction from the agent (no gas), no pre-funded balance, no relationship beyond the single call.
What it is, what it isn’t
x402 is not:
- Not EIP-191. Not SIWE. Not
personal_sign. Those are wallet-as-identity primitives — useful for signing into a dashboard. The per-call payment uses a different signing scheme entirely. - Not a custom variant. x402 is a real spec; the wire shape below matches what standard x402 client libraries produce.
- Not a deposit-then-withdraw exchange. Each call is a complete payment; the wallet is debited synchronously and there’s no pre-funded balance to draw down.
x402 is:
- An off-chain signed authorization for an on-chain stablecoin transfer. The agent signs; a facilitator (Relaystation) submits.
- EIP-3009
transferWithAuthorizationover USDC v2 on Base (Sepolia or mainnet, configured at the facilitator). - A single request-header payload — base64-encoded JSON of the signed payment object.
- Idempotent at the facilitator level: a replayed authorization with the same
noncesettles once, then errors on subsequent submission.
The payment header
PAYMENT-SIGNATURE: <base64-of-PaymentPayload>
The decoded JSON looks roughly like:
{
"version": "1",
"scheme": "exact",
"network": "base-sepolia",
"payTo": "0xFacilitator...",
"asset": "0xUsdcAddress...",
"maxAmountRequired": "100000",
"maxTimeoutSeconds": 600,
"extra": { "name": "USDC", "version": "2" },
"payload": {
"signature": "0x<132-hex>",
"authorization": {
"from": "0xWalletAddress...",
"to": "0xFacilitator...",
"value": "100000",
"validAfter": "1714000000",
"validBefore": "1714003600",
"nonce": "0x<32-bytes-hex>"
}
}
}
100000 micros = $0.10 (USDC’s six-decimal scale). The exact field set, version, and signing rules are owned by Relaystation’s protocol document — see the authoritative reference below. Don’t construct the payload by hand from this page; use a client library that targets the spec.
Why EIP-3009 and not EIP-191
EIP-191 (personal_sign) signs an arbitrary message scoped to one wallet. SIWE (Sign-In With Ethereum) is an EIP-191 application: sign “I claim to be wallet X at this domain” and the relying party verifies by recovering the signing address.
EIP-191 doesn’t sign value. It can’t tell anyone “I authorize transferring $0.10 to address Y, valid only from time T to T+10 minutes, with replay-protected nonce Z.” That’s what EIP-3009’s transferWithAuthorization does, and that’s what an x402 payment needs: a verifiable instruction with a value, a recipient, a validity window, and a replay nonce.
The two coexist on the same wallet:
| Primitive | What it signs | Where it appears in this API |
|---|---|---|
EIP-3009 transferWithAuthorization | Token transfer with value, recipient, validity, nonce | The per-call payment header (lodestone path) |
EIP-191 personal_sign | Arbitrary message string | The wallet JWT signin (returning-visit history reads) |
Both primitives recover the same wallet address from a given private key, so the customer record at Relaystation collapses to one row regardless of which path the wallet uses first.
What the API does with the header
When PAYMENT-SIGNATURE arrives on a billable route, the SDK middleware:
- Decodes the base64 payload.
- Verifies the signature recovers the claimed
fromaddress. - Resolves or creates a wallet-kind customer record at Relaystation keyed by
from. - Hands the call to the wrapper’s
withChargehandler with a charged context. - After the wrapper returns successfully, the facilitator submits the EIP-3009 authorization on-chain. Settlement is synchronous-from-the-caller’s-perspective; the response includes the result.
If the signature is invalid, the wallet has insufficient USDC, or the validity window has expired, the call fails before any work runs. The wrapper never sees an unbacked request.
Authoritative reference
The wire format, version semantics, supported networks, and the exact PaymentPayload schema are owned by Relaystation’s protocol document at relaystation/docs/protocols/x402.md. This page is intentionally a summary; if you need to construct payloads or interpret error codes that come back from the facilitator, read the protocol doc.
The /openapi.json surface registers x402 as a PAYMENT-SIGNATURE-named API-key security scheme; tooling that consumes the OpenAPI spec gets the header name and a brief description automatically.
Common error codes
When something goes wrong on the x402 path, the response body’s code field narrows the cause:
INSUFFICIENT_BALANCE(402) — wallet has fewer USDC thanmaxAmountRequired. Body carriespriceMicros,balanceMicros, and atopUpUrl. (For x402 the top-up is an on-chain transfer to the wallet, not a Stripe top-up.)INVALID_SIGNATURE— the recovered address doesn’t matchfrom, the signature isn’t well-formed, or the EIP-712 domain separator is wrong for the configured network.CHALLENGE_EXPIRED/NONCE_ALREADY_USED— these surface on the wallet-JWT path, not the x402 path. If you see them on aPOST /v1/envelopescall, the request is using the wrong auth shape.
The full error catalog is at /guides/errors.