Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.hel.io/llms.txt

Use this file to discover all available pages before exploring further.

x402 is a machine-native payment protocol built on HTTP 402 Payment Required. An agent makes a normal HTTP request to a paywalled endpoint, the server responds with payment instructions, the agent signs an on-chain authorization, and retries the request — the SDK handles the round-trip automatically. MoonPay Commerce exposes two x402-paywalled endpoints: checkout (one-time payments against a paylink) and deposit (recurring or balance-based payments).
All payments use real mainnet USDC. There is no testnet x402 endpoint. Confirm the amount, chain, and recipient with the user before every payment.

What is x402?

x402 is a “pay-to-play” standard for HTTP APIs. Instead of an account, API key, or subscription, the client pays per request directly in stablecoins. Key properties:
  • No subscription — pay only for what you use
  • Permissionless — no account creation, KYC, or merchant approval
  • Crypto-native — USDC settled on Base, Ethereum, Polygon, Arbitrum, BSC, or Solana
  • Agent-friendly — every step is a single HTTP exchange; no UI, redirects, or sessions

How the Flow Works

1. Agent      → POST /v1/x402/checkout/<paylinkId>?payerAddress=<wallet>
2. Server     → 402 Payment Required (PAYMENT-REQUIRED header: amount, payTo, network)
3. Agent      → signs on-chain authorization (EIP-3009 / SPL token transfer)
4. Agent      → POST same URL with PAYMENT-SIGNATURE header
5. Server     → verifies + settles on-chain → 200 OK (PAYMENT-RESPONSE header: txHash)
Steps 2–5 are handled automatically by any x402-compatible client (the MoonPay CLI, @x402/fetch, x402 Python, etc.) — application code only sees a single POST and the final response.

API Endpoints

Base URL: https://api.hel.io
MethodPathDescription
POST/v1/x402/checkout/{paylinkId}One-time checkout payment against a paylink
POST/v1/x402/deposit/{depositId}Recurring / balance-based deposit payment
GET/v1/paylink/{id}/publicFetch product metadata (requires Origin header)
Both POST endpoints return HTTP 402 until a valid PAYMENT-SIGNATURE is presented.
MoonPay Commerce requires ?payerAddress=<wallet> on every request. The server pre-allocates a per-payer deposit wallet on the initial 402 response, so it must know the payer’s address before it can respond. Standard x402 clients do not send this automatically — append it to the URL manually.Without it, the server returns 400 with "x-payer-address header or ?payerAddress= query param required".

Supported Chains

ChainCAIP-2Notes
Baseeip155:8453Lowest gas — recommended default
Ethereumeip155:1Higher gas
Polygoneip155:137Low gas
Arbitrumeip155:42161Low gas
BSCeip155:56Low gas
Solanasolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpFastest settlement (~5–10s)
When multiple chains appear in accepts[], prefer Base unless the agent’s wallet is funded elsewhere. If gas spikes beyond the per-chain threshold, that chain is excluded from accepts[]; if all chains are excluded, the server returns 400 instead of 402 — retry later.

Amount Conversion

USDC has 6 decimals: minimalUnits = USD × 1_000_000.
USDMinimal units
$1.001000000
$3.003000000
$10.0010000000
$100.00100000000
The amount returned by the 402 response is the total cost including fees and gas — no further calculation is required client-side.

Pay with the MoonPay CLI

The fastest way to make an x402 payment is the MoonPay CLI (mp). It manages the wallet, signs the on-chain authorization, and handles the 402 round-trip.

Prerequisites

  • MoonPay CLI installed and authenticated — mp wallet list should show your wallet
  • Wallet funded with mainnet USDC on the target chain
  • A MoonPay Commerce paylink URL (e.g. from moonpay.hel.io)
Check your balance before paying:
mp token balance list --wallet <wallet-name> --chain <chain>

Step 1 — Discover the product

Before paying, fetch the public paylink metadata so you can confirm the product, price, and supported chains:
curl -s "https://api.hel.io/v1/paylink/<paylinkId>/public" \
  -H "Origin: https://app.hel.io"
Returns: product name, description, price (in minimal USDC units), supported blockchains.

Step 2 — Get the payer address

mp wallet list
Copy the address for the wallet you’ll pay from.

Step 3 — Confirm with the user

Restate the amount, chain, and recipient before spending real funds. For an agent integration, this is a hard requirement — never auto-execute.
“About to send $10.00 USDC on Base mainnet for DEX Screener Token Boost (paylink abc123), signed from wallet <name>. Proceed?”

Step 4 — Pay

Fixed-price paylink:
mp x402 request \
  --method POST \
  --url "https://api.hel.io/v1/x402/checkout/<paylinkId>?payerAddress=<YOUR_WALLET_ADDRESS>" \
  --wallet <wallet-name> \
  --chain base
Dynamic-price paylink — append &amount=<minimalUnits>:
mp x402 request \
  --method POST \
  --url "https://api.hel.io/v1/x402/checkout/<paylinkId>?amount=10000000&payerAddress=<YOUR_WALLET_ADDRESS>" \
  --wallet <wallet-name> \
  --chain base
Recurring deposit payment:
mp x402 request \
  --method POST \
  --url "https://api.hel.io/v1/x402/deposit/<depositId>?payerAddress=<YOUR_WALLET_ADDRESS>" \
  --wallet <wallet-name> \
  --chain base

Step 5 — Confirm settlement

The CLI prints the settlement transaction hash on success. Surface it to the user:
“Paid $10.00 USDC on Base mainnet (settle tx 0x…). Wallet balance: $X → $X-10.”
Settlement is asynchronous — the HTTP 200 returns as soon as the signature is verified, but the on-chain sweep from the per-payer deposit wallet to the merchant takes ~30–120s on mainnet.

Pay with an x402 SDK

Any x402-compatible client works against MoonPay Commerce — just remember to append ?payerAddress=<wallet> to the URL.

Prerequisites

  • Node.js, Python, or Go
  • Wallet private key (loaded from environment variable — never commit)
  • USDC balance on the target chain

Install

# Fetch-based client (recommended)
npm install @x402/fetch @x402/evm viem

# For Solana support
npm install @x402/svm @solana/kit

Make a Payment

import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const payerAddress = signer.address;

const client = new x402Client();
registerExactEvmScheme(client, { signer });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

// MoonPay Commerce requires ?payerAddress on the URL
const url = `https://api.hel.io/v1/x402/checkout/<paylinkId>?payerAddress=${payerAddress}`;

const response = await fetchWithPayment(url, { method: "POST" });

console.log("Status:", response.status);
console.log("Settlement:", response.headers.get("PAYMENT-RESPONSE"));

Multi-Chain Support

Register multiple schemes to pay from any supported network:
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { base58 } from "@scure/base";

const evmSigner = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const svmSigner = await createKeyPairSignerFromBytes(
  base58.decode(process.env.SOLANA_PRIVATE_KEY!)
);

const client = new x402Client();
registerExactEvmScheme(client, { signer: evmSigner }); // Base, Ethereum, Polygon, Arbitrum, BSC
registerExactSvmScheme(client, { signer: svmSigner }); // Solana
The client picks the scheme that matches the chain offered in accepts[]. If the wallet is funded on Base only, ensure your client preference selects Base when multiple chains are offered.

Manual Payment Flow (No SDK)

If an SDK isn’t an option, you can drive the protocol with raw HTTP calls.

Step 1 — Initial request

curl -i -X POST \
  "https://api.hel.io/v1/x402/checkout/<paylinkId>?payerAddress=<YOUR_WALLET_ADDRESS>"

Step 2 — Decode the 402 response

The server returns HTTP 402 with a PAYMENT-REQUIRED header containing Base64-encoded JSON:
{
  "x402Version": 1,
  "resource": {
    "url": "/v1/x402/checkout/<id>",
    "description": "<product name>",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "10000000",
      "payTo": "0x<deposit-wallet>",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USD Coin", "version": "2" }
    }
  ]
}
Key points:
  • amount is the total cost in minimal units — fees and gas are already included.
  • payTo is a per-payer deposit wallet, not the merchant’s address. Funds sweep to the merchant after settlement.
  • Each entry in accepts[] is one supported chain — pick the one your wallet is funded on.

Step 3 — Sign the on-chain authorization

Step 4 — Retry with the signed proof

curl -i -X POST \
  "https://api.hel.io/v1/x402/checkout/<paylinkId>?payerAddress=<YOUR_WALLET_ADDRESS>" \
  -H "PAYMENT-SIGNATURE: <base64-encoded-signed-payload>"

Step 5 — Read the settlement receipt

The 200 OK response includes a PAYMENT-RESPONSE header with the on-chain settlement transaction hash:
PAYMENT-RESPONSE: { "txHash": "0x…", "payer": "0x…", "network": "eip155:8453" }

Error Handling

HTTPCode / ConditionWhat to do
402No payment signatureDecode PAYMENT-REQUIRED, sign, retry with PAYMENT-SIGNATURE
400Missing payerAddressAppend ?payerAddress=<wallet> to the URL
400PAYLINK_INACTIVEPaylink disabled — do not retry
400PAYLINK_DELETEDPaylink deleted — do not retry
400X402_UNSUPPORTED_FEATURESPaylink requires customer detail fields; not payable via x402
400FEE_MARGIN_INSUFFICIENTGas exceeds fee margin on all chains; retry later
400FEE_RATE_EXCEEDS_PRICEFee rate exceeds payment price; surface to user
400Empty accepts[]All chains circuit-broken on gas; retry later
400Invalid payer addressValidate format: 0x… for EVM, base58 for Solana
400Invalid amount paramMust be a positive integer string in minimal units
403PAYLINK_SANCTIONEDAccess restricted; do not retry
403Payer mismatchSigned payer doesn’t match provisioned identity; verify wallet
404Paylink not foundVerify the paylink ID is correct for production
409Settlement in progressA submission is already settling; do not re-submit
429Rate limitedBack off — limit is 10 requests / 60s per IP

Rate Limits

x402 endpoints are rate-limited per IP at 10 requests / 60 seconds. Agents that batch payments should serialize requests and back off on 429.

Troubleshooting

x-payer-address header or ?payerAddress= query param required (400) The payerAddress query parameter is missing. This is required by MoonPay Commerce and is non-standard for x402 — most SDKs do not send it. Always include ?payerAddress=<wallet> in the URL. Payment verification failed / on-chain revert The payer wallet has no USDC on the network the server allocated the deposit wallet for. Check mp token balance list --wallet <name> --chain <chain>accepts[].network in the 402 response tells you which chain to fund. All chains return 400 instead of 402 Gas spiked across all supported chains and the circuit breaker excluded them all from accepts[]. Wait for gas to settle (typically minutes) and retry. 409 Conflict — settlement in progress A previous submission for this transaction is still being processed. Do not re-submit; wait 30–60s for settlement to resolve.

Pre-Payment Checklist

Before executing any payment, verify:
  • Wallet has sufficient USDC balance on the target chain
  • Paylink ID is valid (confirmed via GET /v1/paylink/{id}/public)
  • ?payerAddress=<wallet> is included in the URL
  • Amount is correct — in minimal units (6 decimals), matches the product price
  • User has explicitly confirmed: product name, amount, chain, and wallet

Resources

x402 Protocol

Official protocol specification and ecosystem

x402 Quickstart for Buyers

Coinbase’s getting-started guide for x402 clients

MoonPay CLI

mp — sign and submit x402 payments from the command line

x402 GitHub

Reference clients in TypeScript, Python, and Go

Agent Payments Overview

Other agent-payable protocols and the merchant integration model

Webhooks

React to settled agent payments via paylink and deposit webhooks