Pay per request (x402)

No account, no key, no balance. An agent with a regular Ethereum-style wallet holding a little USDG and ETH pays for one request and gets the answer.

Beta

heumpa targets Robinhood Chain mainnet (chain id 4663) and takes payment in USDG. The HEUMPA token is live and the product is in beta; nothing has been audited. The API address is https://api.useheumpa.fun/v1 and answers today, with models from heumpa's own supply and one outside-style seller behind it. No outside seller has joined yet, so only add a balance you are happy to spend while it is in beta.

One scheme: transfer-receipt

USDG has no EIP-3009 transfer-with-authorization, so the stock x402 exact scheme and facilitators cannot be used. heumpa uses a single scheme, transfer-receipt: you broadcast a plain USDG transfer yourself and the server only reads the on-chain receipt. heumpa holds no key that can move a buyer's funds. sdk/pay.mjs is a working reference client.

Pay from an EOA (MetaMask, Rabby, Coinbase Wallet, or a plain key). Smart-contract wallets are refused with unsupported_wallet_type. GET /x402/info shows whether pay per request is switched on.

The flow

  1. 1
    Ask

    POST /x402/v1/chat/completions with a normal chat body and no payment. You get 402, a PAYMENT-REQUIRED header (base64 JSON) and the same JSON as the body.

  2. 2
    Pay

    Send one transaction on Robinhood Chain: USDG transfer(payTo, amount) for at least the quoted amount. Append extra.calldata_suffix to the calldata, i.e. 0xa9059cbb + payTo padded to 32 bytes + amount padded to 32 bytes + the suffix bytes. USDG ignores the trailing bytes; the server uses them to match the payment to your quote.

  3. 3
    Collect

    Wait for confirmations, then repeat the identical request with a PAYMENT-SIGNATURE header. You get the completion and a PAYMENT-RESPONSE header.

The quote

json
{
  "x402Version": 2,
  "accepts": [{
    "scheme": "transfer-receipt",
    "network": "eip155:4663",
    "asset": "",
    "payTo": "<payee address>",
    "amount": "1000",
    "maxTimeoutSeconds": 300,
    "extra": { "memo": "<quote id>", "calldata_suffix": "0x...", "name": "USDG", "decimals": 6, "howTo": "..." }
  }]
}

The payment header

text
PAYMENT-SIGNATURE: base64( JSON {
  "x402Version": 2,
  "accepted": <accepts[0]>,
  "payload": { "quoteId": "<extra.memo>", "txHash": "<0x transaction hash>" }
})

Rules the server enforces

SituationResult
Paid less than quoted, even by 1 unit402 underpaid
Paid the right amount with a different reference402 memo_mismatch
Paid another address402 no_payment_to_payee
Paid in a look-alike token402 no_payment_to_payee
Paid from a smart-contract wallet402 unsupported_wallet_type
Paid, then changed the request body402 request_does_not_match_quote
Used the same payment twice (also across top-up and x402)409 quote_already_used / payment_already_used
Sent the same payment twice at onceexactly one is served, the other gets 409
Payment not yet buried under enough blocks402 not_enough_confirmations (the server waits up to 15 s first; retry the same request)
Transaction older than 15 minutes402 transaction_too_old
Quote older than five minutes402 quote_expired
Upstream failed after you paid502, recorded as refund due

What you pay

  • The quote is the worst case: prompt size plus max_tokens. Minimum $0.001.
  • The route accepts max_tokens up to 1,024 and no streaming.
  • A quote is bound to the exact request, works once, and expires after five minutes. The server also waits for block confirmations before it serves.

Reference client

bash
HEUMPA_URL=... PAYER_KEY_FILE=./payer.key MAX_USDG=0.05 \
  node pay.mjs openai/gpt-6-luna "Hello"

Set MAX_USDG so a wrong quote can never spend more than you intend. The client reads the quote, checks the token and network, sends the transfer from your key and replays the request.