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.

Pre-launch

heura targets Robinhood Chain mainnet (chain id 4663) and takes payment in USDG. It is not launched and nothing has been audited. The API address is https://api.useheura.com/v1 and answers today, but only one seller (run by heura) is behind it so far, so do not send real funds until launch is announced.

One scheme: transfer-receipt

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

Pay from an EOA (Phantom, MetaMask, Rabby, 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": "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
    "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
HEURA_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.