Q402 Developer Docs
Gasless stablecoin payments across 11 EVM chains. One API key, one signed request.
Overview
A managed relay for USDC / USDT / RLUSD across 11 EVM chains. Users hold no native token; Q402 submits the TX and pays the gas.
User wallet Q402 API On-chain Your app
─────────── ──────── ──────── ────────
1. Sign EIP-712 ──▶ /api/payment/intent lock quote, planChain
/api/payment/activate scan TX, grant credits
2. Get API key ◀── (sandbox or live)
3. Call pay() ──▶ /api/relay ──▶ EIP-7702 Type-4 TX
verify · decrement USDC/USDT transfer
credits · cap checks user EOA ──▶ recipient
◀── webhook
HMAC-signed
relay.success
Dashboard ◀── delivery log · key rotation · gas tank balanceintent locks the quote · activate grants credits after the on-chain transfer · relay submits each payment. Every relay can fire an HMAC-signed webhook.
Agentic Wallet
A dedicated signing wallet for each AI agent, with on-chain guardrails so an agent can transact without holding your main keys. Each owner can provision up to 10 agent wallets; every payment settles gaslessly through the same EIP-712 + EIP-7702 relay.
Manage wallets, caps, and reputation gates from /dashboard, or introspect them from an MCP client with q402_agentic_info.
Yield · Aave V3
Supply and withdraw stablecoins on Aave V3 over BNB Chain straight from an Agent Wallet. The EIP-7702 relay sponsors the gas, so idle balances compound while you pay $0 to move them.
From an MCP client: q402_yield_reserves, q402_yield_positions, q402_yield_deposit, q402_yield_withdraw.
Bridge · Chainlink CCIP
Move native USDC across chains in a single signed request over Chainlink CCIP. Quote, send, and track a transfer from the dashboard or an MCP client — no manual bridge hops.
From an MCP client: q402_bridge_quote, q402_bridge_send, q402_bridge_history, q402_bridge_gas_tank.
Payment Requests
The receive side of Q402. Publish a payment request (a fixed amount on a chain, with an optional memo) and Q402 returns a shareable /pay/req_… link plus a req_ id. Creating one moves no funds; anyone can fulfill it later. It is the inverse of q402_pay: instead of sending, you bill.
Two ways to get paid. A Q402 agent settles a request gaslessly from its own Agent Wallet with q402_request_pay: the agent-to-agent flow, where agent A bills and agent B pays. Or open the /pay link, which shows the exact amount, token, chain, and recipient. Those terms are read from the stored request on every settlement, so a payer can never redirect funds or change the sum.
From an MCP client: q402_request_create, q402_request_status, q402_request_pay (two-phase consent, same as q402_pay).
How It Works
Three actors. One transaction. Zero gas for your users.
Quick Start
Pick a key, load the SDK, call pay(). Under 5 minutes.
0 · Trial key vs Multichain key
Trial API Key
BNB only · 2,000 sponsored TX · Q402 pays gas. /event.
Multichain API Key
11 chains · USDC / USDT / RLUSD (eth) · self-funded Gas Tank. /payment.
TRIAL_BNB_ONLY. Use Multichain for non-BNB chains.1 · Load the SDK
<script src="https://q402.quackai.ai/q402-sdk.js"></script>2a · Trial key — BNB-only sponsored payment
// Trial keys are BNB-only. No Gas Tank needed — Q402 sponsors gas.
const q402 = new Q402Client({
apiKey: "q402_live_YOUR_TRIAL_KEY", // from /event (your Trial API Key)
chain: "bnb",
});
const result = await q402.pay({
to: recipientAddress,
amount: "1.00",
token: "USDT", // or "USDC"
});
// result → { success: true, txHash: "0x...", chain: "bnb", method: "eip7702" }2b · Multichain key — full 11-chain payment
// Multichain keys work across all supported chains. Each chain needs
// a funded Gas Tank — deposit at /dashboard → Treasury.
const q402 = new Q402Client({
apiKey: "q402_live_YOUR_KEY",
chain: "bnb", // "bnb" | "avax" | "eth" | "xlayer" | "stable" | "mantle" | "injective" | "monad" | "scroll" | "arbitrum" | "base"
});
// Note: Injective supports both USDC and USDT (native Circle USDC via CCTP).
// Wallet popup appears — user signs, Q402 relays on-chain
// amount MUST be a human-readable decimal STRING (e.g. "50.00", "0.123456").
// Never pass a JS Number — IEEE-754 loses precision on 18-decimal tokens.
// Inputs exceeding the token's decimals or non-decimal strings throw.
const result = await q402.pay({
to: recipientAddress,
amount: "50.00",
token: "USDC",
});
// result → { success: true, txHash: "0xabc...", tokenAmount: "50", chain: "bnb" }
3 · Injective EVM
Native Circle USDC (CCTP) and USDT are both supported on Injective EVM. Cosmos and EVM share one balance via the MultiVM Token Standard.
const q402 = new Q402Client({
apiKey: "q402_live_YOUR_KEY",
chain: "injective",
});
const result = await q402.pay({
to: recipientAddress,
amount: "50.00",
token: "USDC", // USDC and USDT both supported on Injective
});4 · Ethereum RLUSD (NY DFS regulated)
Ripple USD, NY DFS regulated. Ethereum mainnet only — rejects on any other chain. Decimals = 18; the SDK handles conversion, pass amount as a decimal string.
const q402 = new Q402Client({
apiKey: "q402_live_YOUR_KEY",
chain: "eth",
});
const result = await q402.pay({
to: recipientAddress,
amount: "10.00",
token: "RLUSD", // Ethereum-only — throws on any other chain
});5 · That's it
// Full result shape:
// {
// success: true,
// txHash: "0xdef456...",
// chain: "bnb",
// blockNumber: "38482910",
// tokenAmount: "50",
// token: "USDC",
// gasCostNative: 0.000021,
// method: "eip7702",
// }
console.log("Paid! TX:", result.txHash);MCP for AI Clients
An MCP server for Claude / Codex / Cursor / Cline / Copilot / Hermes. @quackai/q402-mcp · bitgett/q402-mcp.
1 · Install
Same package, one snippet per client. No secrets here.
# Claude Code / Claude Desktop
claude mcp add q402 -- npx -y @quackai/q402-mcp
# OpenAI Codex CLI
codex mcp add q402 -- npx -y @quackai/q402-mcp
# Cursor — paste into ~/.cursor/mcp.json (or .cursor/mcp.json for per-project scope)
# Cline — Cline → Settings → MCP Servers → Edit JSON. Same shape.
{
"mcpServers": {
"q402": {
"command": "npx",
"args": ["-y", "@quackai/q402-mcp"]
}
}
}
# GitHub Copilot (VS Code) — .vscode/mcp.json. Root key is "servers", NOT mcpServers.
{
"servers": {
"q402": {
"command": "npx",
"args": ["-y", "@quackai/q402-mcp"]
}
}
}
# Hermes Agent (Nous Research) — ~/.hermes/config.yaml (YAML). Reload with /reload-mcp.
mcp_servers:
q402:
command: "npx"
args: ["-y", "@quackai/q402-mcp"]
enabled: true2 · First-time setup — ask your AI
Restart the client, then say “Set up Q402”. The agent runs q402_doctor → creates + opens ~/.q402/mcp.env → walks you through pasting keys into the file. Auto-loaded for every client.
3 · Wallet modes — which signing path?
Three paths. q402_doctor asks once; change later in ~/.q402/mcp.env.
Q402 holds an encrypted Agent Wallet for you. No private key in your env. No MetaMask popup. Best for AI agents and most users.
Q402_MULTICHAIN_API_KEY=q402_live_...
Same Agent Wallet as Mode C, but you hold the PK. Export from the dashboard once. Signs locally — key never leaves your machine. MetaMask never touched.
Q402_AGENTIC_PRIVATE_KEY=0x... Q402_MULTICHAIN_API_KEY=q402_live_...
Your existing EOA signs directly via EIP-7702. The "Smart account" marker after first use is normal + reversible with q402_clear_delegation. Use a fresh wallet.
Q402_PRIVATE_KEY=0x... Q402_MULTICHAIN_API_KEY=q402_live_...
4 · Tools exposed — 27 total
| Tool | Auth | Purpose |
|---|---|---|
q402_doctor | none | First-install onboarding + ongoing health check (quota, EIP-7702 state, relay reachability). |
q402_quote | none | Compare gas + supported tokens across 11 chains. |
q402_balance | api key | Verify key + remaining quota. Returns Trial + Multichain in one read when both keys set. |
q402_pay | live mode | Single-recipient gasless USDC / USDT / RLUSD send. Sandbox by default. |
q402_batch_pay | live mode | Up to 20 recipients per call (trial: 5). 6+ BNB batches with Trial → status="ambiguous" so the agent asks how to split. |
q402_receipt | none | Fetch + locally verify a Trust Receipt by rct_… id (ECDSA recovery against the relayer EOA). |
q402_wallet_status | private key | Per-chain EIP-7702 delegation state for the EOA derived from Q402_PRIVATE_KEY. Read-only. |
q402_clear_delegation | private key / api key | Clear EIP-7702 delegation on a single chain (Mode A/B local key OR Mode C api key, server-signed). Sponsored except Ethereum (billed to Gas Tank). Two-phase consentToken (preview then execute). |
q402_agentic_info | api key | Agent Wallet info (addresses, caps, daily-spend used, ERC-8004 id). Drives Mode C. |
q402_recurring_list | api key | List scheduled rules. |
q402_recurring_create | api key | Author a rule. Paid Multichain on EVERY chain (BNB included). |
q402_recurring_fires | api key | Last 50 fires per rule (timestamp + txHashes + amount). |
q402_recurring_pause | api key | Pause a rule. Reversible. |
q402_recurring_resume | api key | Resume a paused / stopped rule. |
q402_recurring_skip_next | api key | Skip ONLY the next scheduled fire. Cadence preserved. |
q402_recurring_cancel | api key | Permanently stop a rule. |
q402_bridge_quote | none | Quote a Chainlink CCIP USDC bridge across the eth/avax/arbitrum triangle (LINK + native fee + ETA). |
q402_bridge_send | live mode | Execute a CCIP USDC bridge from the Agent Wallet (Mode C). Sandbox by default. |
q402_bridge_history | api key | Recent CCIP bridge attempts for the Agent Wallet (src/dst/amount/CCIP msgId/status). |
q402_bridge_gas_tank | api key | Per-chain Gas Tank native balance + auto-fund window so the agent can top up before bridging. |
q402_yield_reserves | none | List Q402 Yield (Aave V3) lending markets + live supply APY. BNB Chain only today. |
q402_yield_positions | api key | The Agent Wallet's current Q402 Yield positions — value + live supply APY. Read-only. |
q402_yield_deposit | live mode | Supply the Agent Wallet's USDC / USDT into Aave (Mode C). PAID feature — Trial cannot deposit. Confirm-gated + sandbox by default. |
q402_yield_withdraw | live mode | Withdraw supplied stablecoin out of Aave (amount="max" for the full position). Always allowed, even after a plan downgrade. |
q402_request_create | api key | Publish a payment request (invoice). No funds move; returns a /pay link + req_ id. Recipient defaults to the Agent Wallet. |
q402_request_status | none | Look up a request by req_ id (amount, token, chain, recipient, status). Read-only; notFound instead of throwing. |
q402_request_pay | live mode | Pay a request gaslessly from your own Agent Wallet (Mode C). Two-phase consent, same as q402_pay. |
5 · Sandbox vs live mode
Default is sandbox — fake txHash, sandbox: true, no funds move. Live = API key + a signing path. Pick ONE mode:
- Mode A —
Q402_PRIVATE_KEY= your MetaMask EOA. Shows “Smart account” in MetaMask after first use (reversible viaq402_clear_delegation). - Mode B —
Q402_AGENTIC_PRIVATE_KEY= exported Agent Wallet PK. Local signing; MetaMask untouched. - Mode C — paid Multichain key only. Q402 holds the AES-GCM-encrypted Agent Wallet key server-side. Optionally set
Q402_AGENT_WALLET_ADDRESSto pick a wallet.
q402_doctor writes the file with every secret line empty. Live mode only flips when an API key + a signing path are populated — saving the template as-is stays in sandbox.Q402_ENABLE_REAL_PAYMENTS=0 forces sandbox even with real keys.
# ~/.q402/mcp.env — what q402_doctor creates on first install.
# Paste your values on the right of `=`. Q402_ENABLE_REAL_PAYMENTS
# already defaults to 1 — the gate refuses empty values, so partial
# setups stay in sandbox automatically.
# ── API key (pick one or both for auto-routing) ──
Q402_TRIAL_API_KEY= # Free Trial, BNB only (from /event)
Q402_MULTICHAIN_API_KEY= # Paid Multichain, all 11 chains (from /payment)
# ── Signing path — pick ONE of Mode A / B / C ──
# Mode A: your MetaMask EOA's hex private key
Q402_PRIVATE_KEY=
# Mode B: exported Agent Wallet pk from dashboard (keeps MetaMask untouched)
Q402_AGENTIC_PRIVATE_KEY=
# Mode C: no PK needed. Paid Multichain key alone + server-managed Agent Wallet.
# Optional picker when you have multiple wallets:
# Q402_AGENT_WALLET_ADDRESS=0x...
# Live mode switch:
# 0 = sandbox (test mode, no funds move)
# 1 = real on-chain payments
# Default 1 — safe because mode only flips to live when an API key AND
# at least one valid signing path (A/B/C) are populated above.
Q402_ENABLE_REAL_PAYMENTS=1
# Default Q402 deployment. Only change for self-hosted.
Q402_RELAY_BASE_URL=https://q402.quackai.ai/apiMissing config → sandbox fallback with a hint. Two extra guards: Q402_MAX_AMOUNT_PER_CALL (default $200) and Q402_ALLOWED_RECIPIENTS (address allowlist).
q402_pay requires explicit in-chat confirmation. Four guards total: confirm + sandbox default + per-call cap + allowlist.Trust Receipt
A verifiable proof page for every Q402 settlement — signed by the relayer EOA, recoverable in any browser.
What's on a receipt
- Settlement facts — payer, recipient, amount, chain, EIP method.
- On-chain proof — tx hash, block, sponsored gas, explorer link.
- Signature — EIP-191 ECDSA over the canonical hash, recoverable locally in your browser.
- Delivery trace — webhook state, retry count, last response code.
Receipt URL
/api/relay responses include receiptId + receiptUrl:
{
"success": true,
"txHash": "0x9afd...52a4",
"tokenAmount": "0.10",
"token": "USDT",
"chain": "bnb",
"receiptId": "rct_afa5f50bc49a65ebba3b28ab",
"receiptUrl": "https://q402.quackai.ai/receipt/rct_afa5f50bc49a65ebba3b28ab"
}Mirrored in the webhook payload — no second lookup needed.
JSON endpoint
Machine-readable for downstream verification:
curl https://q402.quackai.ai/api/receipt/rct_afa5f50bc49a65ebba3b28abRate limited 120/min per IP, returned with X-Robots-Tag: noindex, nofollow. The id is unguessable (12 random bytes) so the URL doubles as a shareable audit link without iteration risk.
Best-effort inline + durable backfill
The relay path tries createReceipt() synchronously twice and awaits a queue write to receipt-backfill-queue before responding. /api/cron/receipt-backfill drains the queue with a per-tx KV lock and SET-NX idempotency, so a successful relay normally produces exactly one receipt — synchronously when KV is healthy, within the next cron run otherwise. If both inline retries andthe backfill enqueue fail (rare, requires KV to be unreachable for both writes within the same request), the relay route fires a critical Telegram alert with the tx hash + payload snapshot so an operator can manually recover. That alert path is the third leg; we don't claim a hard guarantee in the absence of it.
Verify any receipt from Claude
The @quackai/q402-mcp server (v0.8.45) exposes a q402_receipt tool. After q402_pay hands back a rct_… id, ask Claude to verify it and the recovery runs inside the MCP process — same canonical-JSON + ECDSA recovery the receipt page does, no UI trust required:
> Send 0.10 USDT to alice on BNB via Q402, then verify the receipt.
Claude → q402_pay → settles + returns rct_afa5...
Claude → q402_receipt → verified: true · signed by 0xfc77...74ff466Live demo
q402.quackai.ai/receipt/rct_afa5f50bc49a65ebba3b28ab ↗
Gas Pool
Deposit native tokens into your per-wallet Gas Tank. Every relay deducts the actual gas cost.
Send BNB / ETH / AVAX / OKB / MNT / INJ / MON (or USDT0 on Stable) to the Gas Tank address on the dashboard. The Tank is a cold wallet, NOT the relayer — never send to the relayer directly.
Per-relay native-token deduction, real-time balance.
Manual via business@quackai.ai. Funds remain yours.
EIP-7702 Delegation
EIP-7702 set-code delegation (Pectra) lets your EOA settle gasless payments without a per-user smart-account deploy. Persists across payments, reversible anytime.
Inspect or clear
From your AI client (MCP) — ask in plain English:
"Show my Q402 wallet status."
"Clear my Q402 delegation on BNB Chain."q402_wallet_status reads state. q402_clear_delegationsigns locally (Mode A/B) or server-side (Mode C, api key); Q402 sponsors the clear TX on every chain except Ethereum, where it's billed to your Gas Tank. The next payment recreates the delegation automatically.
From the terminal (CLI):
PRIVATE_KEY=0x<yourKey> node scripts/undelegate-7702.mjs --chain bnbAll 11 chains, self-paid (~$0.001 native gas).
Why we use it
One primitive, 11 chains, no per-user contract deploy. Each chain's impl is source-verified on Sourcify. The delegation marker is the only on-chain trace.
▸Troubleshooting (things to know)
- • Your wallet's eth_getCode returns 0xef0100…<impl> instead of 0x while the delegation is active.
- • MetaMask / OKX may display a Smart accountindicator — the delegation is to Q402's vetted impl, not a third-party contract.
- • Native gas tokens (BNB / ETH / etc.) sent directly to a delegated EOA will not land — the impl doesn't accept native receives. Clear the delegation first if you want to receive native to that EOA.
Authentication
API key goes in the request body's apiKey field. Sandbox keys (q402_test_*) on wallet connect. Live keys (q402_live_*) from /event (Trial, BNB) or /payment (Multichain, 11 chains).
// POST /api/relay
{
"apiKey": "q402_live_YOUR_API_KEY",
"chain": "avax",
"token": "USDC",
...
}API Reference
Base URL: https://q402.quackai.ai/api
Submit a signed EIP-712 + EIP-7702 payload. Q402 verifies and relays on-chain.
// Request body
{
"apiKey": "q402_live_YOUR_API_KEY",
"chain": "avax", // avax | bnb | eth | xlayer | stable | mantle | injective | monad | scroll | arbitrum | base
"token": "USDC", // USDC | USDT
"from": "0xUserWallet...",
"to": "0xRecipient...",
"amount": "50000000", // atomic units (6 decimals = 50 USDC)
"deadline": 1751289600,
"witnessSig": "0xabc123...",
"authorization": { ... } // EIP-7702 authorization object
}
// Response 200
// All numeric amounts are returned as decimal strings to preserve
// 18-decimal token precision (IEEE-754 doubles lose precision at that scale).
{
"success": true,
"txHash": "0xdef456...",
"chain": "avax",
"blockNumber": "54540550",
"tokenAmount": "50",
"gasCostNative": "0.000021",
"method": "eip7702"
}Returns the relayer (facilitator) address. The facilitator field in your EIP-712 payload must match.
// GET /api/relay/info
{ "facilitator": "0xRelayerAddress..." }Chain Support
Same API, same SDK. Switch with one parameter.
| Chain | chain param | Chain ID | Gas token | Status | Avg gas/tx |
|---|---|---|---|---|---|
BNB Chain | bnb | 56 | BNB | Mainnet Live | ~$0.001 |
Ethereum | eth | 1 | ETH | Mainnet Live | ~$0.19 |
Avalanche | avax | 43114 | AVAX | Mainnet Live | ~$0.002 |
X Layer | xlayer | 196 | OKB | Mainnet Live | ~$0.001 |
Stable | stable | 988 | USDT0 ★ | Mainnet Live | ~$0.001 |
Mantle | mantle | 5000 | MNT | Mainnet Live | ~$0.001 |
Injective | injective | 1776 | INJ | Mainnet Live | ~$0.10 |
Monad | monad | 143 | MON | Mainnet Live | ~$0.001 |
Scroll | scroll | 534352 | ETH | Mainnet Live | ~$0.001 |
Arbitrum | arbitrum | 42161 | ETH | Mainnet Live | ~$0.001 |
Base | base | 8453 | ETH | Mainnet Live | ~$0.001 |
EIP-712 Signing
EIP-712 typed structured data (same standard as Uniswap, Compound). User signs a human-readable message — no gas, no on-chain TX.
Contract Addresses & Domain Names
// Implementation contract per chain
const CONTRACTS = {
avax: "0x96a8C74d95A35D0c14Ec60364c78ba6De99E9A4c", // Q402 Avalanche (chainId: 43114)
bnb: "0x6cF4aD62C208b6494a55a1494D497713ba013dFa", // Q402 BNB Chain (chainId: 56)
eth: "0x8E67a64989CFcb0C40556b13ea302709CCFD6AaD", // Q402 Ethereum (chainId: 1)
xlayer: "0x8D854436ab0426F5BC6Cc70865C90576AD523E73", // Q402 X Layer (chainId: 196)
stable: "0x2fb2B2D110b6c5664e701666B3741240242bf350", // Q402 Stable (chainId: 988)
mantle: "0xE5b90D564650bdcE7C2Bb4344F777f6582e05699", // Q402 Mantle (chainId: 5000)
injective: "0xa9a7dcE76DEF2AC36057FeF0d8103dF10581d61e", // Q402 Injective (chainId: 1776)
monad: "0xc5d4dFA6D2e545409C1abf86f336Dd43bb87621f", // Q402 Monad (chainId: 143)
scroll: "0x7635F32D893B64b5944CB8cbF2AC4cd3dA41B2f1", // Q402 Scroll (chainId: 534352)
arbitrum: "0x8D854436ab0426F5BC6Cc70865C90576AD523E73", // Q402 Arbitrum (chainId: 42161)
base: "0x2fb2B2D110b6c5664e701666B3741240242bf350", // Q402 Base (chainId: 8453)
};
// EIP-712 domain name — must match contract NAME constant exactly
const DOMAIN_NAMES = {
avax: "Q402 Avalanche",
bnb: "Q402 BNB Chain",
eth: "Q402 Ethereum",
xlayer: "Q402 X Layer",
stable: "Q402 Stable",
mantle: "Q402 Mantle",
injective: "Q402 Injective",
monad: "Q402 Monad",
scroll: "Q402 Scroll",
arbitrum: "Q402 Arbitrum",
base: "Q402 Base",
};
// verifyingContract:
// ALL chains → user's own EOA (address(this) under EIP-7702 delegation)Witness Type (unified across all chains)
// Every deployed Q402 impl contract uses the same EIP-712 typed struct:
const types = {
TransferAuthorization: [
{ name: "owner", type: "address" }, // token sender (user's EOA)
{ name: "facilitator", type: "address" }, // gas sponsor (Q402 relayer)
{ name: "token", type: "address" }, // ERC-20 contract (USDC / USDT / USDT0)
{ name: "recipient", type: "address" }, // payment destination
{ name: "amount", type: "uint256" }, // atomic units
{ name: "nonce", type: "uint256" }, // random uint256, replay protection
{ name: "deadline", type: "uint256" }, // unix timestamp
],
};
// verifyingContract is ALWAYS the user's own EOA — the contract computes its
// domain separator with address(this), which equals the user EOA under EIP-7702.Signing with ethers.js
// Fetch facilitator address first (required for all chains)
const { facilitator } = await fetch("https://q402.quackai.ai/api/relay/info").then(r => r.json());
const domain = {
name: DOMAIN_NAMES[chain],
version: "1",
chainId: chainId,
verifyingContract: userAddress, // user's own EOA — same for all chains under EIP-7702
};
const nonce = ethers.toBigInt(ethers.randomBytes(32)); // random uint256
const signature = await signer.signTypedData(domain, types, {
owner: userAddress,
facilitator,
token: tokenAddress,
recipient: recipientAddress,
amount: ethers.parseUnits("50", decimals), // 6 decimals on most chains; 18 only for Stable chain's USDT0
nonce,
deadline: BigInt(Math.floor(Date.now() / 1000) + 600),
});eip3009Nonce instead of authorization).ethers.parseUnits(amount, 18). Gas Tank also in USDT0. (Mantle USDT0 = 6 decimals.)Error Responses
JSON body: { "error": string, "code"?: string }. error is human-readable; code is a stable machine-readable tag (when present).
Codes below are the currently-emitted set; most failures return only error.
FAQ
Ready to go gasless?
Get a live API key on payment. Sandbox key is free to test first.