# Gatefare — full agent context Gatefare is an x402 payment proxy for HTTP APIs. Publishers register endpoints; agents pay USDC on Base per request. Platform is non-custodial: every payment routes to an immutable 0xSplits contract (ownerAddress: 0x0) via 90% publisher / 10% platform split, enforced by the contract itself — not by platform policy. ## Base URL All endpoints below are relative to https://gatefare.io. ## Mandatory CSRF header Every non-GET request must carry `X-Gatefare-Request: 1`. Missing it returns 403 CSRF_HEADER_REQUIRED. Browsers can't forge custom headers cross-origin, so this is a cheap anti-CSRF gate that does not impact legitimate programmatic clients. ## Networks - Production: eip155:8453 (Base mainnet, USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) - Testing: eip155:84532 (Base Sepolia, USDC 0x036CbD53842c5426634e7929541eC2318f3dCF7e) Testnet APIs are hidden from the public catalog by default — pass ?includeTestnet=1 on /api/catalog to see them. ## Authentication — three ways 1. **Email + password** - POST /api/auth/register { email, password, displayName?, walletAddress?, acceptTerms: true } - POST /api/auth/login { email, password, totpCode? } - Returns { token (JWT), user } and sets an HttpOnly session cookie. - JWT expires in 7 days. 2. **Sign-In With Ethereum (EIP-4361)** - POST /api/auth/siwe/nonce { wallet, chainId } - Sign the returned "message" with EIP-191 personal_sign. - POST /api/auth/siwe/verify { wallet, signature } - Returns { token, user } — same shape as email login. - Wallet-native accounts have no email; email verification is N/A. 3. **Personal Access Tokens (PATs)** — long-lived, for scripts/CI - After any login above, POST /api/auth/pat { label, scopes[], expiresInDays } - Returns { token: "gfpat_..." } — save immediately, never shown again. - Pass in Authorization: Bearer gfpat_xxx on subsequent calls. ## PAT scopes (monotonic — stronger implies weaker) - read — catalog reads, /my/* listing, balance views. - write:catalog — register APIs, edit non-sensitive fields. - write:sensitive — delete APIs, trigger distribute(), change wallet. - write:wallet — rotate linked wallet (non-delegatable). - admin:pat — mint additional PATs (non-delegatable). - full — god-mode, ephemeral testing only. ## Publisher flow ### 1. Set a handle (required once per account) PATCH /api/auth/me { handle: "your-handle" } Handle: lowercase alphanumeric + hyphens, 3–24 chars, permanent on published URLs. Without one, step 3 returns 400 HANDLE_REQUIRED. ### 2. Prove the beneficiary wallet (skip if same as linked wallet) POST /api/wallet-challenge { ownerWallet: "0x..." } Returns { message, nonce }. Sign the message with ownerWallet via EIP-191 personal_sign. Attach the signature to step 3. ### 3. Register the API POST /api/register (requires write:catalog) { "urlName": "weather", // lowercase, a-z0-9-, ≤50 "name": "Weather", // ≤80 chars "targetUrl": "https://api.example.com/v1",// NO query/fragment "price": "0.01", // USDC "ownerWallet": "0x...", "network": "eip155:8453", "walletSignature": "0x...", // from step 2 if needed "description": "Short description", // ≤200 chars "categories": ["weather"], // ≤5 lowercase strings "tags": ["geodata"], // ≤10 lowercase strings "headers": { "x-api-key": "secret" } // forwarded upstream } Returns: { "id": 4213, "urlName": "weather", "handle": "your-handle", "proxyUrl": "https://gatefare.io/p/your-handle/weather", "legacyProxyUrl": "https://gatefare.io/p/weather", "splitAddress": "0x...", // CREATE2 — not deployed "apiKey": "k_..." } Common errors on this endpoint: - HANDLE_REQUIRED — set one via PATCH /api/auth/me first - WALLET_SIGNATURE_REQUIRED — ownerWallet ≠ linked wallet, need a challenge - TARGET_URL_HAS_QUERY — strip the ?... and move auth into headers - SLUG_TAKEN — another urlName exists under your handle - INSUFFICIENT_SCOPE — your PAT lacks write:catalog ### 4. Manage - GET /api/my — list your APIs - GET /api/my/:slug — single API owner view - PATCH /api/my/:slug — edit (write:catalog; or write:sensitive for owner/targetUrl) - DELETE /api/my/:slug — soft-delete, 30d undelete window (write:sensitive) - GET /api/my/:slug/balance — on-platform accumulated balance - GET /api/my/:slug/split-balance — USDC sitting in the split contract - GET /api/my/:slug/revenue-series — 30-day revenue series - GET /api/my/:slug/requests — paid request log - POST /api/my/:slug/distribute — call distribute() on-chain (write:sensitive) ## Consumer (agent) flow ### 1. Discover GET /api/catalog?q=weather&sort=popular&price_max=0.05 GET /api/catalog/search/suggest?q=weath — autocomplete GET /api/catalog/collections — public collections GET /api/catalog/:handle/:urlName — full detail by canonical path GET /api/catalog/:slug — detail by legacy slug (also works) ### 2. Pay Call the proxy URL. First call returns 402 with the x402 v2 payload: { "x402Version": 2, "accepts": [{ "scheme": "exact", "network": "eip155:8453", "maxAmountRequired": "10000", // micro-USDC; 10000 = $0.01 "resource": "https://gatefare.io/p/alice/weather", "description": "...", "payTo": "0x...", // split contract address "maxTimeoutSeconds": 60, "extra": {} }] } Sign an EIP-3009 USDC transferWithAuthorization off-chain using payTo and maxAmountRequired. Base64-encode the signed payload as JSON and put it in an X-Payment header. Retry the same URL — the proxy verifies the signature, settles on-chain, forwards to the upstream, streams the response back, and attaches X-Payment-Receipt with the tx hash. Server-side, Gatefare caps each payment at 1.1× posted price — agents are protected from a publisher raising the price mid-flight. ### 3. Budget discipline Agents should enforce per-call caps, per-task budgets, and check wallet balance before signing when the budget is tight. A 402 after a successful sign round means the price changed — refresh the quote before re-signing. ## Errors — stable machine codes Every error response is JSON: { "error": "message", "code": "CODE" }. Switch on code. Stable set: - CSRF_HEADER_REQUIRED (403) missing X-Gatefare-Request header - UNAUTHENTICATED (401) no/invalid JWT or PAT - INSUFFICIENT_SCOPE (403) PAT scope too weak - HANDLE_REQUIRED (400) set a handle before registering - WALLET_SIGNATURE_REQUIRED (400) prove ownerWallet via /api/wallet-challenge - WALLET_SIGNATURE_INVALID (400) signature didn't recover to ownerWallet - TARGET_URL_HAS_QUERY (400) strip query/fragment from targetUrl - PAYMENT_REQUIRED (402) expected — parse accepts, sign, retry - SLUG_TAKEN (409) another urlName exists under your handle - RATE_LIMITED (429) back off per RateLimit-Reset header ## Rate limits - Catalog reads: 120 req/min/IP - Payment proxy: 60 req/min/IP - Auth + SIWE: 20 req/15min/IP Responses on 429 carry RFC 9239 RateLimit-* headers. Respect them. ## Non-custodial guarantee — verifiable, not claimed Every split contract is deployed with ownerAddress: 0x0. Platform cannot alter recipients after deployment — not us, not Coinbase, not the publisher. distribute() is permissionless. Full policy: - https://gatefare.io/transparency — enforcement + moderation audit log - https://gatefare.io/terms — ToS - https://gatefare.io/acceptable-use — AUP - https://gatefare.io/dmca — DMCA policy + designated agent ## Compliance Payer wallets are screened against OFAC, EU, and UN consolidated sanctions lists and Chainalysis risk indicators on every paid request. Payments from screened wallets are rejected before reaching the upstream. Users in comprehensively sanctioned jurisdictions are excluded per AUP §3. ## Protocol attribution x402 (https://x402.org) is a public specification by Coinbase. Gatefare implements the server side — pricing, header negotiation, on-chain settlement — as a proxy. We did not invent the protocol; we run a production-quality server for it.