x402 Documentation
How to call Ontario Protocol's paid endpoints from any agent that speaks HTTP. USDC on Base. No API keys.
New: x402 endpoint readiness quickstart
(generate a public report_id before asking agents to pay) ·
agent payment preflight guide
(apply /api/agent/can-pay policy before signing) ·
wallet-enabled buyer client quickstart
(connect a local x402 wallet bridge after free preflight) ·
agent payment policy JSON
(publishable allow/review/deny contract for payment-capable agents) ·
embed a readiness badge
(link badge → signed report; verify integrity + freshness) ·
list an x402 service
(validate payload, rehearse in sandbox, then submit paid listing) ·
x402 verification tools comparison
(which checks an agent should run before payment) ·
GitHub x402 repository scanner
(check public repository evidence before deployment) ·
MCP Registry readiness checker
(check public registry metadata and read-only MCP discovery) ·
agent decision playbooks
(100 Search Console-informed pre-payment workflows) ·
Search Console indexing playbook
(core sitemap, evidence sitemap, and 404 triage).
Agent/tool registry bundle
If you list Ontario in an MCP registry, agent tool catalog, GitHub README, or AI crawler profile, point to the three canonical manifests together. The MCP manifest tells agents which tools exist, the x402 manifest tells payment-aware clients what can be paid, and the buyer guide tells agents when to stop or proceed.
curl https://ontarioprotocol.com/.well-known/mcp.json curl https://ontarioprotocol.com/.well-known/x402.json curl https://ontarioprotocol.com/.well-known/agent-buyer.json curl https://ontarioprotocol.com/.well-known/agent-payment-policy.json
Payment posture: discover first, run free preflight, probe the structured HTTP 402,
then pay only when policy allows and the paid result adds value beyond free reports or catalog data.
Overview
Every paid endpoint on Ontario follows the x402 protocol:
- Client makes a normal request, no payment header.
- Server replies
HTTP 402with aPAYMENT-REQUIREDheader (base64 JSON). - Client signs a payment payload (EIP-3009 transferWithAuthorization for USDC) and resends with
PAYMENT-SIGNATUREheader. - Server forwards to a facilitator (Coinbase by default) for verify + settle, then returns
HTTP 200+PAYMENT-RESPONSEheader.
The flow, in pictures
Client Ontario Coinbase Facilitator | POST /api/x402/... | | |------------------------->| | | 402 PAYMENT-REQUIRED | | |<-------------------------| | | build + sign | | | PaymentPayload | | | POST + PAYMENT-SIGNATURE| | |------------------------->| POST /verify | | |----------------------------->| | | isValid: true | | |<-----------------------------| | | POST /settle | | |----------------------------->| | | success: true, tx: 0x... | | |<-----------------------------| | 200 + PAYMENT-RESPONSE | | |<-------------------------| |
curl
# 1. Probe — get the 402 with payment requirements curl -i -X POST https://ontarioprotocol.com/api/x402/agent-trust-scan \ -H "Content-Type: application/json" \ -d '{"agent_id":"my-agent","target_url":"https://example.com"}' # 2. Decode the PAYMENT-REQUIRED header (base64 → JSON): echo "$PAYMENT_REQUIRED_HEADER" | base64 -d | jq # 3. Sign the payload with your wallet (using x402 client SDK, see Python below) # 4. Resubmit with PAYMENT-SIGNATURE header: curl -X POST https://ontarioprotocol.com/api/x402/agent-trust-scan \ -H "Content-Type: application/json" \ -H "PAYMENT-SIGNATURE: $SIGNED_PAYLOAD_B64" \ -d '{"agent_id":"my-agent","target_url":"https://example.com"}'
Python
# pip install x402 web3 eth-account requests # Never paste real keys into source files. Use an env var and a dedicated low-balance wallet. from x402.client import X402Client from eth_account import Account import os acct = Account.from_key(os.environ["X402_PRIVATE_KEY"]) client = X402Client(account=acct, default_network="base") resp = client.post( "https://ontarioprotocol.com/api/x402/agent-trust-scan", json={ "agent_id": "my-agent", "target_url": "https://example.com", }, max_payment_atomic=10_000, # 0.01 USDC, prevents over-pay ) print(resp.json()["report"]["trust_score"])
TypeScript
// npm install x402 viem // Never hardcode keys. Use an env var (Node) and a dedicated low-balance wallet. import { createX402Client } from "x402"; import { privateKeyToAccount } from "viem/accounts"; const privateKey = process.env.X402_PRIVATE_KEY; if (!privateKey) throw new Error("Missing X402_PRIVATE_KEY"); const account = privateKeyToAccount(privateKey); const client = createX402Client({ account, defaultNetwork: "base" }); const res = await client.fetch( "https://ontarioprotocol.com/api/x402/agent-trust-scan", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ agent_id: "my-agent", target_url: "https://example.com", }), maxPaymentAtomic: 10_000n, } ); const data = await res.json();
Endpoints
| Endpoint | Method | Price | What you get |
|---|---|---|---|
/api/agent/pay/v2 |
POST | 0.002 USDC | Verify an x402 endpoint before an AI agent pays. This x402 paid API verification service and AI-agent tool runs a fresh payment preflight for an autonomous agent: inspect the live HTTP 402 challenge, payTo address, price, Base USDC network and asset, manifest readiness, buyer budget, and spend policy. Returns ALLOW or DENY with reasons, a report ID, settlement metadata, and a settlement-backed SHA-256 evidence receipt. Run free can-pay and readiness checks first. Readiness evidence is not a safety guarantee. |
/api/agent/pay |
POST | 0.002 USDC | Pay-per-query agent commerce endpoint. Submit a query and optional endpoint, receive an x402-settled policy answer and structured can-pay data. |
/api/x402/agent-trust-scan |
POST | 0.01 USDC | Submit an agent's card URL or A2A endpoint, receive a structured trust report (security signals, identity claims, declared capabilities). Returns JSON. |
/api/x402/implementation-audit |
POST | 0.01 USDC | Pay-per-review implementation evidence for a public x402 endpoint or GitHub repository. Returns actionable payment, discovery, security, debugging, and launch findings without returning source contents or secret values. Run free checks first. |
/api/x402/demand-authenticity-report |
POST | 0.02 USDC | Classify up to 20 Base USDC settlement receipts as direct self-payment, user-declared related-party, or unattributed. Unattributed wallets are not counted as proven customers. |
/api/x402/reputation/<agent_id> |
GET | 0.001 USDC | Look up an agent's accumulated reputation (sourced from on-chain EAS attestations on Base + recent scan history). |
/api/x402/list-agent |
POST | 0.10 USDC | Submit an AI agent for inclusion in the Ontario Protocol directory. Pays 0.10 USDC up front to deter spam. |
/api/x402/list-service |
POST | 0.50 USDC | Register your own x402-paid endpoint so AI agents discover it through Ontario Protocol's `/discover`. 0.50 USDC listing fee. |
/discover |
GET | Free | Machine-readable catalog of all services. |
/.well-known/mcp.json |
GET | Free | MCP tool manifest with schemas, endpoints, and x402 price metadata. |
/.well-known/x402.json |
GET | Free | Standardised manifest for agent auto-discovery. |
/.well-known/agent-buyer.json |
GET | Free | Machine-readable guide for pay/stop decisions and cheapest first paid calls. |
/.well-known/agent-payment-policy.json |
GET | Free | Generic allow/review/deny policy contract for agents and payment-capable MCP hosts. |
/facilitator/supported |
GET | Free | Lists (scheme, network) pairs the proxy supports. |
/facilitator/verify |
POST | Free | Verifies a signed payment off-chain via Coinbase facilitator. |
/facilitator/settle |
POST | Free* | Submits payment on-chain. Logs split + take rate to ledger. |
* "Free" meaning no fee to call the endpoint. Settlement transactions themselves cost gas + the marketplace's 1.5% take rate when applicable.
Marketplace: list a third-party service
POST /api/x402/list-service is a paid marketplace endpoint (0.50 USDC) used to register
a third-party x402-paid API so agents can find it through Ontario’s discovery surfaces.
On submission, Ontario runs the free readiness verifier against your endpoint and stores the resulting report.
- Immediately: your submission appears in
/listingsasPENDINGorREADY. - Only when ready: it enters
/discover(default filters) and/.well-known/x402.jsonwhen its stored report gradesready. - Trust boundary: readiness signals are not a safety guarantee; agents should still preflight and enforce budgets.
Request body
{
"name": "My Cool API",
"description": "What it does",
"category": "data",
"endpoint": "https://example.com/api/data",
"method": "GET",
"price_atomic": 10000,
"price_usdc": "0.01",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"network": "base",
"owner_url": "https://example.com",
"owner_contact": "ops@example.com",
"tags": ["dataset", "ai-agent", "x402"]
}
Tip: run /verify first and submit only after you reach grade=ready with a fresh report.
Before you pay the listing fee, you can validate your payload shape for free:
POST /api/x402/list-service/validate (no payment; does not store a listing).
Sandbox rehearsal (no on-chain rails; does not store a listing):
POST /sandbox/api/x402/list-service. It behaves like a paid x402 endpoint (HTTP 402 first, then accept a simulated payment payload),
so you can test your x402 client integration without spending money.
Machine-readable schema:
/.well-known/x402-list-service.schema.json
curl -sS https://ontarioprotocol.com/api/x402/list-service/validate \\
-H "Content-Type: application/json" \\
-d '{"name":"Example Service","description":"Paid endpoint","endpoint":"https://example.com/api/paid","method":"POST","price_usdc":"0.01","network":"base","owner_url":"https://example.com"}'
SDK examples: ontario_list_service.py ·
ontario-list-service.ts
Response body
The response includes the settlement context under payment and the stored listing under listing
(including verification_status, verification, and paid_listing_tx).
Directory: list an agent (optional)
POST /api/x402/list-agent (0.10 USDC) registers an agent card URL for the directory.
It uses the same auto-verification behavior (a readiness report is attempted and attached if available).
Validate your agent submission payload for free with POST /api/x402/list-agent/validate.
Machine-readable schema:
/.well-known/x402-list-agent.schema.json
curl -sS https://ontarioprotocol.com/api/x402/list-agent/validate \\
-H "Content-Type: application/json" \\
-d '{"name":"Example Agent","description":"Agent card","agent_card_url":"https://example.com/agent.json","owner_url":"https://example.com"}'
Errors
| Status | Meaning |
|---|---|
402 | Payment required. PAYMENT-REQUIRED header tells you the amount + asset. |
402 (after retry) | Payment was rejected — see error + stage in the JSON body. |
400 | Could not decode PAYMENT-SIGNATURE header. |
410 | Old SaaS URL. Use /discover instead. |
Facilitator
By default Ontario delegates verification + settlement to Coinbase's
hosted facilitator (limits and rate policies may apply — confirm against Coinbase docs).
With X402_USE_PROXY=1 in the server's .env,
requests pass through Ontario's proxy_facilitator, which
logs every settlement to a public, append-only SQLite ledger and applies
the 1.5% take rate on third-party listings.
See /treasury for the live, public ledger
summary or call /api/treasury/ledger
directly.
Cookbook: production-tested pay-and-scan script
The following is a production-style reference script for paying an x402 endpoint and then running an Ontario scan to record evidence. Download it, set env vars, and run:
# Install deps pip install x402 requests pyjwt[crypto] cryptography python-dotenv # Set env (or add to .env) export CDP_API_KEY="your-cdp-key-id" export CDP_API_SECRET="base64-ed25519-secret" export BUYER_WALLET_SECRET="base64-ec-p256-der-private" export BUYER_WALLET_ADDRESS="0x…your-wallet" # Run python pay_and_scan.py
⬇ Download pay_and_scan.py — The complete reference implementation (~210 lines).
Common pitfalls
⚠ EIP-712 domain name must be "USD Coin"
The Base mainnet USDC contract at
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
uses domain.name = "USD Coin" and version = "2"
for EIP-712 typed data. If your signature uses "USDC"
or version "1", the Coinbase facilitator will reject
with "invalid signature".
⚠ x402 Python SDK nonce is bytes, not hex
prepare_payment_header() may return the nonce
field as raw bytes. The CDP facilitator requires a hex
string. Cast it before signing:
if isinstance(nonce, bytes): nonce = nonce.hex()
⚠ x402Version field required in outer body
When calling the CDP facilitator directly, include
"x402Version": 1 in the top-level JSON body.
Omitting it produces a 400.