# Robyn AnyGas > Gasless, cross-chain relay infrastructure for AI agents and onchain apps. A relayer > fronts network gas and is repaid from the token being moved, so a single signed intent > transacts across 22 EVM chains, Stellar, Solana and native Bitcoin — 25 nodes, ~500 > directed routes — with no native gas balance held anywhere. 44 more LI.FI-reachable EVM > chains (Monad, Berachain, Scroll, zkSync, Sei, Injective EVM and more) are quotable for price > discovery: the same /api/route/quote answers with quoteOnly:true — full list under > comparatorQuoteOnly in GET /api/route/chains. Same-chain SWAPS are supported everywhere > quotable: set fromChain == toChain with different tokens for best-of-N DEX aggregation > through the same gasless flow. Robyn AnyGas lets an agent pay for or move value on any supported chain without holding that chain's gas token. The user (or agent) signs one Permit2 SignatureTransfer (or EIP-712 intent) and the Robyn relayer pays every network fee, executes, and takes repayment from the token in flight. For cross-chain moves Robyn runs best-of-N routing: it quotes the bridge aggregators (LI.FI / Bungee / deBridge) in parallel, executes the highest net output, and fails over automatically. Stellar and Solana settle via Allbridge Core; native Bitcoin via Chainflip. Version 3.1 is live. Privacy is a first-class feature, not an afterthought: see the `## Privacy` section for what is selectable per request, and how to use the oblivious relay that is now live. ## Base URL https://api.anygas.xyz/svc (equivalently https://anygas.xyz/svc) Paths below are shown as `/api/...`; the base URL already includes the `/svc` gateway prefix, so the full URL is `https://api.anygas.xyz/svc` + `/api/...`. ## Uptime you can check GET /api/uptime — per-UTC-day availability measured by an INDEPENDENT relay (Iceland) probing /api/status every 10 min; unreached probes count against us. Live: /api/status · delivery success/latency: /api/delivery/stats. ## Fastest path (60 seconds, no key, no funds) 1. See the mesh (chain ids + rails live now): curl -s https://anygas.xyz/svc/api/route/chains 2. Get a real quote — same fields as the POST body, as query params (amount = source-token base units): curl -s "https://anygas.xyz/svc/api/route/quote?fromChain=8453&toChain=42161&fromToken=USDC&toToken=USDC&amount=25000000" Every quote response carries a `next` object with the exact follow-up calls (execute / status / compare). 3. Rehearse an execute with ZERO funds — header x-anygas-sandbox: 1 runs execute -> status -> DONE (~40 s): curl -s -X POST https://anygas.xyz/svc/api/route/execute -H 'content-type: application/json' -H 'x-anygas-sandbox: 1' \ -d '{"fromChain":8453,"toChain":42161,"fromToken":"USDC","toToken":"USDC","amount":"25000000","fromAddress":"0xYourAddress","toAddress":"0xYourAddress"}' then: curl -s "https://anygas.xyz/svc/api/route/status?id=" Runnable in a browser at https://anygas.xyz/docs#sixty. Wrong method on a documented route answers 405 + Allow + a ready-to-run example; an unknown path answers 404 + didYouMean — you can explore by calling. ## Conventions - No key required for reads. Passing `x-anygas-key: ` only tags requests for usage stats. - Amounts are strings in the token's smallest units (USDC 6dp "5000000" = 5 USDC; BTC in sats). - Chain ids: EVM chains are numeric; non-EVM nodes are the strings "solana", "stellar", "bitcoin" (and "sui" for quote-only comparison). - Tokens: a symbol (USDC/USDT/BTC) or a contract address; omit for the chain's native asset. - Routing fee: Robyn withholds 0.25% (25 bps) from the token in flight, never in native gas. Every quote returns `robynRouteFeeBps`. - MICRO-TRANSFERS ARE FREE OR NEAR-FREE (Robyn float lane). Small cross-chain transfers normally pay disproportionate fees; on our own float lane they do not, because there is no bridge in the path — we pay you out of our own inventory on the destination chain and take repayment on the source. * $0.10-$25 to a low-cost destination (Base, Arbitrum, Optimism, Polygon, BNB, Avalanche): - FREE (fee exactly 0) if the sender holds a recognised member NFT. Currently: Bored Ape Yacht Club (Ethereum, 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D), DEGEN TOONZ (Ethereum, 0x19b86299c21505cdf59cE63740B240A9C822b5E4), The Currency by Damien Hirst (Ethereum, 0xaaDc2D4261199ce24A4B0a57370c4FCf43BB60aa) and Robyn Genesis / Pioneers / Founders (chain 4663). Members also pay 15bps instead of 25bps routing and 10% instead of 25% relay markup. GET /svc/api/floatlane/info -> vip.registry for the live list. - otherwise a few thousandths of a cent (our pass-through cost + a small margin), and NEVER more than the standard 8bps. Example: $5 Arbitrum->Base = $0.004. * Larger transfers: 8 bps, floored at the destination chain's REAL payout cost (which ranges from $0.00006 on Avalanche to $0.073 on Linea — the floor is per-chain, not flat). * Send `fromAddress` on the quote to be recognised as a member. Every quote discloses `floatlane` with `fee`, `effectiveFeeBps`, `pricing.mode`, `freeTier` and `vip`. * Directional pricing: a transfer that moves funds the way our inventory already needs them is priced at cost (both sides) or half price (one side) — check `pricing.mode`. * GET /svc/api/floatlane/info for live tier limits, per-chain cost floors and VIP collections. - Sandbox: add header `x-anygas-sandbox: 1` to POST /api/route/execute to simulate the whole execute -> status -> DONE lifecycle with ZERO funds (returns an `sbx_…` id; no signature, no relayer spend, nothing on-chain). - Errors: 4xx/5xx with `{"error":"…"}`. Over a per-IP rate limit -> 429. - Client SDKs are MIT-licensed; the hosted relayer, routing service and contracts are proprietary. ## Endpoints (no key required for reads) Discovery - GET /api/status Sanitized health: per-node up/down (22 EVM + Stellar/Solana/Bitcoin), bridge rails, endpoint probes, route count. - GET /api/route/chains Live route graph: chain ids, node/route counts, relayer (Permit2 spender), engine, non-EVM node metadata. - GET /api/gasless/info Same-chain gasless config: router / anyGas / tokenPaymaster / Permit2 / EntryPoint addresses, EIP-712 domain, per-chain map, accepted fee tokens. Quote & execute - POST /api/route/settle ONE-VERB settlement primitive: best route across all rails + gasless method + privacy posture + what the receipt will prove, in one call. Planning only (never broadcasts) — sign + submit via the returned next step. Add private:true for private-mempool execution. - POST /api/agent/do ONE CALL for agents. Body {intent:"send 25 USDC to 0x.. on arbitrum"} or {toChain,token,amountHuman,toAddress}. Resolves + quotes + returns status sign|quoted|done with the EXACT payload to sign. Add header x-anygas-sandbox: 1 to dry-run free. - GET /api/errors The full error contract: every errorCode, retryable, retryAfterMs, suggestedAction. Branch on errorCode, never on message text. - GET /api/route/stream?id= Server-sent events for a route: status on every change, done on terminal. Stop polling. - GET /api/keys/quota What a free key is worth (per-KEY limits at 5x the anonymous ceiling). POST /api/keys/create to get one. - GET /api/diagnostics ONE-CALL SYSTEM DIAGNOSIS. Verdict + subsystem summary + keeper freshness + the next action for anything flagged. Start here. - GET /api/rpc/health Per-chain RPC failure rate + latency, so you can tell a dead lane from a slow one. - GET /api/nonevm/reserves Non-EVM leg readiness; labels each native balance gas vs treasury. - GET /openapi.json Full OpenAPI 3.1 spec (46 paths) - generate a client, or feed it to your tool-builder. - GET /api/7702/info EIP-7702 sponsored-transaction rail (type-4 set-code): chains, limits, pricing, flow. - GET /api/7702/delegate Canonical batch-executor delegate: per-chain addresses (all 22 EVM chains), ABI, EIP-712 scheme for executeSigned. - POST /api/7702/check Verify an EIP-7702 authorization tuple {chainId, address, nonce, yParity, r, s} recovers your authority (free, no funds). - POST /api/7702/sponsor Sponsor a type-4 tx: the relayer submits it and PAYS THE GAS. Prepaid per call in USDC via x402 - no payment returns HTTP 402 + accepts[] with the full plan; pass {"dry":true} to price it free. Proven end-to-end on Base from a zero-balance account. - GET /api/treasury/convert-plan Read-only profit-consolidation plan: per-chain excess stable float and the route home to Base. Cannot move funds. - POST /api/route/quote Best gasless route for one intent. Body: {fromChain, toChain, amount (base units, "25000000" = 25 USDC), fromToken?, toToken?, toAddress?, fromAddress?, slippage?}. The same fields also work as GET query parameters (?fromChain=8453&toChain=42161&fromToken=USDC&toToken=USDC&amount=25000000) — identical response; use POST from code. ^ CHECK `quoteOnly` ON THE RESPONSE. If true, the corridor can be PRICED but NOT settled — execute refuses it with status UNSUPPORTED_DIRECTION (a gap on our side, not in your request), and `quoteOnlyReason` says why. Absent/false = executable. Currently quoteOnly: Solana (both directions) and stellar->evm. Do not present a quoteOnly route to a user as sendable. - POST /api/route/preflight ⭐ CALL THIS FIRST. Free, read-only GO/NO-GO for a transfer. Same body as /quote (+ toAddress). Returns {decision:"GO"|"NO_GO", blockers[], warnings[], expected}. Each blocker has a `code` and a `fix`. It catches BEFORE you spend gas: BELOW_FEE_THRESHOLD (the fixed corridor fee would consume the whole amount — nothing arrives), BAD_RECIPIENT (address in the wrong chain family — unrecoverable once broadcast), QUOTE_ONLY_CORRIDOR (prices but cannot settle), ABOVE_LANE_CAPACITY (a dearer bridge would silently serve you; tells you the exact ceiling to split at), HIGH_COST (>=20% withheld). Signs nothing, moves nothing. - POST /api/route/execute Execute a signed route; relayer fronts all gas. ⚠️ REHEARSING SAFELY: send `"dryRun": true` (or `"live": false`) for a rail-INDEPENDENT dry run — it returns {status:"DRY", broadcast:false, wouldSend, rankedRails} and broadcasts nothing on EVERY corridor. Do NOT rely on simply OMITTING `live`: that means DRY on the float lane and the non-EVM legs, but the EVM→EVM path ignores it and BROADCASTS. The `x-anygas-sandbox: 1` header is the other safe rehearsal (fully simulated, sbx_ ids). Body: {fromChain, toChain, amount, fromToken?, toToken?, toAddress?, mode?("self"|"permit2"), permit2?}. Add header `x-anygas-sandbox: 1` for a zero-funds simulation. - GET /api/route/status?id= Track an in-flight route. Live ids `rt_…` re-checked against the bridge; sandbox ids `sbx_…` advance on a timer. Unknown id -> 404. Best execution - GET|POST /api/route/compare Quote every rail that can serve a lane (aggregators + Allbridge + Chainflip) in parallel, rank by net output, flag best. Read-only, nothing signed. Same params as quote (query string for GET, JSON body for POST). Adds effectiveFeePct, qualityScore, guard, cached. - POST|GET /api/intent/solve Goal-based source picker. Body/query: {toChain, toToken, from:[{chain,token,amount}] (1–10), goal?("max-output"|"cheapest"|"fastest"), amount?, toAddress?}. Reads /compare per source, returns a PLAN with ready-to-POST execute params. Never executes. Explorer - GET /api/routes/recent?limit=N Anonymized feed of recent real routes (lane, asset, rail, coarse status, size bucket). No addresses/hashes/exact amounts. limit default 25, max 100. Route primitives (GATED — dry-run by default; they plan & store, they do NOT move funds) Execution needs a double opt-in: the watcher must run with PRIMITIVES_LIVE=1 AND the record must carry live:true. Shipped default (live:false) simulates every tick. - POST /api/route/batch Plan a multi-recipient/multi-leg payout (up to 20 legs). Body: {routes:[{fromChain,fromToken,toChain,toToken,amount,toAddress}], live?}. Each leg planned via /compare. - POST /api/route/schedule Scheduled/recurring transfer. Body: {route, everySeconds?(>=10), at?, count?(1–1000), live?}. - GET|DELETE /api/route/schedule/ Inspect or cancel a schedule. - POST /api/route/conditional Trigger-based transfer. Body: {route, when, live?} — when.type = priceAbove|priceBelow (needs when.asset + when.value) or timeAfter (epoch/ISO when.value). - GET|DELETE /api/route/conditional/ Inspect or cancel a condition. Keys, hooks & sessions - POST /api/keys/create Optional self-serve key for a stable identity + usage visibility. Body: {label?}. Key shown once; send as `x-anygas-key: ak_…`. The API works without a key. - GET /api/keys/usage Usage stats for your key (pass `x-anygas-key` header or ?key=). Returns hour/day aggregates + recent calls. (GET /api/keys/me returns key metadata only.) - POST /api/hooks/register Register a completion webhook for a route id. Body: {routeId, url, secret?}. We POST {event,routeId,status,destTx,srcTx,ts} on DONE/FAILED; watch state at GET /api/hooks/. With `secret`, deliveries carry `x-anygas-signature: sha256=HMAC(secret, rawBody)`. - POST|GET /api/session/* Owner-gated custodial capped sub-wallets for autonomous agents (issue/execute/list/revoke, GET /api/session/). Minting requires the platform OWNER_KEY — first-party deployments only. ## NULL — private messenger (why an agent should use it) NULL (PRIVATE · SEALED · POST-QUANTUM MESSENGER — "Broadcast into the void. Understood by one. Remembered by none."). Sealed (ML-KEM-768+X25519), one-time stealth handle per message, server stores only (handle, tag, ct). Use it to: deliver receipts/invoices/quotes to a counterparty's wallet privately; confirm an address out-of-band before a FIRST payment (defeats address substitution); coordinate multi-step settlements without publishing the graph. Every message on the private lane is byte-identical to a settlement (17533 B / 8221 B / same latency) -> your traffic is cover for your own payments. Free, no account. Access: MCP robyn_message_* (relay-only, seal locally), Node /svc/msg2-sdk.mjs (ANYGAS_LANE=private default), browser /svc/messages, API /api/messages/v2/*. Recipe: GET /api/recipes/message-privately. Plus passes (POST /api/plus/trial = 20 free/day) raise limits, fund cover rows, anchor rows. ## NULL names — value-based marketplace (ENS-style, permissionless) Custom usernames for NULL. PERMISSIONLESS open tail + a small transparent premium tier (no operator hoarding). Reserved by RULE: brands = unclaimable (trademark), 1-4 digit numbers (10k-club) + 1-2 char + crypto words = premium (auction, Phase 2), everything else = register directly at the quoted price. - GET /api/names/quote/{label} value estimate: priceUsd + factor breakdown + ENS-comparable band + confidence (transparent oracle: length, pattern, category, memorability). - GET /api/names/status/{label} registered | available | premium-auction | blocklisted, with price/owner/tier. - GET /api/names/search?q= exact status + priced suggestions. GET /api/names/info = model, tiers, {secondaryFeeBps 250 = 2.5%}. Registration of brands/premium names is blocked (403 RESERVED/PREMIUM); open names register free for now. Paid registration + auction + secondary resale (2.5% fee) = Phase 2. Concierge & misc - GET /api/plus/info PLUS: capacity/funded-cover/anchoring bought with ETH/USDC/BTC, redeemed with UNLINKABLE RFC 9474 blind passes (500 for $3). Never a smaller crowd: the wire is identical for paying users. - POST /api/plus/invoice {bundle:"bundle", bundles?:1-50, asset: eth-4663|eth-1|eth-42161|eth-8453|usdc-8453|usdc-42161|btc, refundAddress?(btc)} (btc: Chainflip min ~0.001 BTC => ~20+ bundles) -> {invoiceId, payTo, amount} - POST /api/plus/claim {invoiceId, txHash, blinded:[b64]} -> blind signatures (verify payment on-chain first). SDK /svc/plus-sdk.mjs does blind/finalize. - GET /svc/anygas-web.js Browser bundle: window.AnyGas.{msg2,plus,vault} (WebCrypto; byte-compatible with Node SDKs). /svc/plus-wallet.mjs = encrypted pass wallet + CLI. - POST /api/plus/trial 20 FREE passes/day per caller {blinded:[...]} — try capacity/cover/anchoring before paying. Recipes: GET /api/recipes/message-privately, /api/recipes/buy-and-spend-plus - GET /svc/msg2-keygen-offline.mjs Air-gapped identity generator for messaging v2 (prints public half + fingerprint only). - GET /api/messages/v2/anchor/{id} Anchoring proof (Merkle path + signed root [+ on-chain tx]); POST /api/messages/v2/plus/anchor {pass,id} to request. GET /api/messages/v2/erasure = signed purge statement. - MCP results are padded to exact 4/16/64 KiB wire sizes (ignore the trailing _pad key) so no tool is identifiable by response length. - MEASURED uniformity (docs#uniformity-measured): via the relay every op = 17533-byte request / 8221-byte response / ~748 ms; classifier recovers op type at 8.5% (chance 10%). Browser bundle + Node SDK default to the private lane. - GET /api/messages/v2/info Sealed wallet messaging v2: hybrid ML-KEM-768+X25519, one-time stealth handles, fixed-size ciphertext, 60s release grid. Server stores only (ephemeralPubKey, viewTag, ct). kContribution:0 (unlinkable, not extra K). - GET /api/messages/v2/mailbox/{addrOrName} Recipient's stealth meta-address + KEM public key (publish yours with POST /api/messages/v2/mailbox {address, metaAddress, kemPub, ts, sig}). - MCP robyn_message_{info,directory,mailbox_of,feed,send,publish_mailbox} Same surface over MCP (/mcp). RELAY ONLY: send/publish accept already-sealed / already-signed material; the MCP server never sees plaintext or keys. - GET /api/messages/v2/directory All mailboxes, identical for every reader (oblivious lookup by address; SDK mailboxOf() uses it by default). - POST /api/messages/v2/send {ephemeralPubKey, viewTag, ct} — seal with /svc/msg2-sdk.mjs. Replay-idempotent; allowlisted on the OHTTP private lane. - GET /api/messages/v2/feed?since=&limit= Public feed every reader fetches identically; scan with your viewing key (SDK inbox()). Over OHTTP use limit=2. - POST /api/messages/send v1 (plaintext, signed, stored by recipient address) — kept for browser wallets; prefer v2. - GET /api/concierge Interactive AI concierge that answers questions about the system. - POST /api/ask {question} Ask the concierge programmatically -> {answer}. - GET /api/terms Terms of Service. ## Quote-only expanded lanes (quotable via /compare, NOT execute-ready) Sui (EVM/Solana/Stellar<->Sui), ETH->BTC and SOL->BTC, plus Scroll and the extra Chainflip asset lanes carry `executeReady:false` and are excluded from the headline nodes/directedRoutes counts. Robyn holds no signer for those source assets yet — they compare fine but cannot execute. ## Packages (npm, MIT-licensed clients — also on JSR as @anygas/*) - anygas-agent-kit https://www.npmjs.com/package/anygas-agent-kit JS SDK: pay, payAny, buy, route, crossChain, routeStatus. Zero-config (defaults to this gateway). - anygas-mcp https://www.npmjs.com/package/anygas-mcp MCP server. Tools: robyn_mesh, robyn_quote, robyn_cross_chain, robyn_route_status. - anygas-adapters https://www.npmjs.com/package/anygas-adapters Adapters for Vercel AI SDK, LangChain, and Coinbase AgentKit. - anygas (PyPI) pip install anygas Python client: chains(), compare(), route(), route_status(). ## Add the MCP server npx anygas-mcp MCP client config: { "mcpServers": { "robyn": { "command": "npx", "args": ["anygas-mcp"] } } } ## Hosted MCP endpoint (zero install) Streamable HTTP: https://api.anygas.xyz/mcp (public, read-only — no key required) Also on Smithery: https://smithery.ai/servers/ox31461/AnyGas OpenAPI spec: https://anygas.xyz/openapi.json API reference: https://anygas.xyz/docs Status page: https://anygas.xyz/status ## Using this over MCP (Claude, Cursor, any MCP client) Hosted endpoint, zero install: https://api.anygas.xyz/mcp (Streamable HTTP) The verification suite is exposed as tools, so you do not need to hand-roll HTTP calls: robyn_verify_outcome - check a transfer against the destination chain; assert recipient/minAmount. VERDICT IS THREE-VALUED: verified | failed | undetermined. `undetermined` means UNKNOWN, not success - never report it as confirmed. robyn_plan - validate a whole multi-step plan before paying for step one; returns the binding constraint and the value that would work. robyn_spend_statement - signed proof of what was spent, for handing to a principal. robyn_fee_forensics - where every unit of a fee went. robyn_delivery_stats - measured settlement times; check `status`/`basis` before quoting a duration. robyn_refusal_verify - verify a signed refusal receipt. Plus the existing 23 tools (quote, preflight, prepare, submit, netting, 7702, yield, sandbox...). ### Have I paid this address before? POST /api/counterparty/check { toAddress, amount? } Address substitution is the most common theft here and it is silent. This tells you YOUR OWN history with a recipient: times paid, how recently, and whether this amount is typical. NOT A REPUTATION SCORE. It says nothing about whether the address is honest, and a FAMILIAR ADDRESS IS NOT A SAFE ADDRESS — keys get compromised. Never tell a user an address is "trusted" based on this. `first-time-recipient` is NOT a warning about the recipient. Every legitimate relationship has a first payment. It is the moment to confirm the address through a second channel. READ `coverage`: float-lane payouts are excluded (those rows keep no recipient), so "first time" can be wrong. Surfaced automatically as a warning — never a blocker — in /api/preflight/full. MCP: robyn_counterparty_check ### Check your own integration before you ship POST /api/integration/lint { endpoint, payload } Post the request you INTEND to send. You get the missing or unsafe fields with the concrete cost of each — not style advice. `unsafe` means you can lose money or report something untrue. It lints the REQUEST only. It cannot see whether you branch on all three verification verdicts, treat `undetermined` as success, or use `serviceableNow` instead of `policyEligible`. A clean lint is NOT a correct integration — see GET /api/recipes for what happens after the response. ### Do not guess the call order — ask for the recipe GET /api/recipes -> all playbooks GET /api/recipes/{name} -> one (pay-someone-safely, prove-what-i-spent, spend-within-a-budget, handle-failures-well, stay-current) The spec tells you what exists. It cannot tell you what ORDER to call things in, or which checks it would be reckless to skip. Each recipe gives you the sequence, what each step answers, whether it is optional, and WHAT SKIPPING IT COSTS. Every endpoint referenced is checked against the live spec on each request — if a step is flagged `pathNotInSpec`, trust the spec and not the recipe.\n\n### One call before you act POST /api/preflight/full { fromChain, toChain, amount, toAddress, mandateId? } Composes serviceability, routing competitiveness, cost advice, delivery basis and your mandate into a single go/no-go. Make this ONE call instead of five and guessing. BLOCKERS ARE NOT WARNINGS. `blockers` WILL fail — do not attempt. `warnings` might cost you or your user something — read them out. They are never merged. Checks that could not run appear in `unavailable` and downgrade the verdict to `caution`. Silence is not assent. Naming a mandateId that does not exist BLOCKS: you would be spending outside any budget while believing you were inside one. `proceed:true` means nothing known to us blocks it. It is NOT a promise of success. ### Find out what changed since you integrated GET /api/changelog?since=3.9.0 Pass the API version you built against. You get only what changed after it, each entry flagged `breaking` or not. CHECK `mustAct` FIRST. If true there are breaking changes and your integration may ALREADY be failing — each breaking entry carries an `action` telling you exactly what to do. `breaking:true` means an existing CORRECT integration could stop working. It is a compatibility claim, not a measure of importance. Every path the changelog claims is cross-checked against the live spec on each request; drift is reported, not hidden. Trust the spec over any changelog, including this one. MCP: robyn_changelog ## Privacy (opt-in, per request, never mandatory) Privacy here is a property you select per transaction, not a mode the system is in. Ask for none of it and you pay nothing for it; ask for it and you get exactly what is listed below. Nothing is inferred on your behalf, and no privacy feature is ever forced on a caller who did not request it. Read the split below carefully. Everything under LIVE is usable by an agent today. Everything under IN DEVELOPMENT is built and tested but NOT yet reachable by you — do not design around it. ### LIVE — usable today - Oblivious HTTP — LIVE. Splits "who" from "what" across two independently hosted servers. RELAY: https://relay.nightferry.net/relay (Iceland) GATEWAY: https://anygas.xyz/svc/api/ohttp/keys — fetch the HPKE public key config from here Encapsulate your request to the GATEWAY's key, POST the sealed bytes to the RELAY. The relay sees your address and cannot read the request; the gateway reads the request and sees only the relay. Suite is DHKEM(X25519,HKDF-SHA256) / HKDF-SHA256 / AES-128-GCM. Response encryption is keyed by the HPKE export secret, info string "robyn-ohttp-response-v1"; the response is 12-byte nonce || ciphertext || 16-byte tag under AES-256-GCM. Requests are padded to 512 bytes and responses to at least 2048, so neither reveals which endpoint you called. Requests are batched on a ~750ms tick and shuffled before release, making the inbound-to-outbound mapping a 1-in-N guess even for an observer holding both halves. DENY BY DEFAULT: only /api/status, /api/route/compare, /api/route/quote, /api/privacy/*, /api/stealth/announcements and /api/netting/reserves are reachable this way. Owner and capability surfaces are not, deliberately — an inner request arrives on loopback and would otherwise inherit on-box privileges. NOTE: posting to the gateway directly instead of through the relay works, but gives you NO address protection — we would see your IP exactly as on any other call. Use the relay. - Stealth addresses (ERC-5564). A payment can be sent to a freshly derived one-time address that only the recipient can spend from. Derivation is CLIENT-SIDE: Robyn never asks for and never accepts your viewing key. Announcements: GET /svc/api/stealth/announcements — scan it with your own viewing key. A `viewTag` byte lets you skip ~99.6% of entries without revealing which one is yours; there is no server-side per-recipient filter, deliberately, because the filter itself would identify you. - Denomination laddering. Payouts are split into uniform denominations so amounts do not fingerprint you. NOTE: uniform, not random — random amounts are unique fingerprints and chunks that sum to the original are linkable by addition. Uniform denominations are what create an anonymity set. - Deferred payout. Pass `defer: true` on a float-lane request to release on a shared grid tick instead of immediately, breaking the timing link between your request and the on-chain settlement. - Cover traffic. Treasury rebalances are shaped to be indistinguishable from user payouts, so a payout is not identifiable simply by being the only transfer in a window. - Blind-signed access codes (RFC 9474 RSABSSA-SHA384-PSS). Obtain a code, spend it later; the signature cannot be linked to its issuance, so entitlement is provable without identity. - Telemetry pseudonyms. No raw IP is stored anywhere. Identifiers are hashed under a secret, rotating salt; a rotation makes records either side of it uncorrelatable even by us. - Cryptographic erasure with forward-secret keys. Aged records are encrypted under per-epoch keys that are destroyed on schedule, so any surviving copy — including a backup — is unreadable ciphertext. - Proof of liabilities. A Merkle-sum commitment lets you verify we are solvent without seeing anyone's balance, including yours being revealed to anyone else. ### Not yet callable — do not design around these - Privacy Pool with zero-knowledge withdrawals (association sets, per Buterin/Illum/Nadler/Neuder/ Weinstein 2023). LIVE IN BETA on Base at 0x7940E96d65Db811e61B3EE2F289fE92b3833D309. Fixed denomination 0.00002 ETH, hard cap 250 notes. Deposit yourself (your funding address is read from your transaction, never taken from a request body), then POST a proof to /api/pool/withdraw and we pay the gas — which is the point, because a fresh recipient address has none. * The circuit is NOT independently audited. The 250-note cap is what bounds the value at risk. Deposit only what you are willing to lose entirely. * Anonymity is reported as a machine-readable field, not prose: /api/pool/sets returns anonymity.{size, weak, threshold, advice}. If weak is true, a withdrawal is trivially linkable to its deposit. Check it and refuse rather than assuming privacy you do not have. * We never see your secret. It is generated client-side and only its Poseidon commitment is published. We cannot link your deposit to your withdrawal, and we do not store anything that would let us. * No operator key can move deposits. The publisher key can exclude addresses from an association set; it cannot invent a depositor. Deposits can be paused; withdrawals never can. Endpoints: GET /api/pool/info, GET /api/pool/state, GET /api/pool/sets[?commitment=<64hex>], GET|POST /api/pool/deposit, POST /api/pool/withdraw. On-chain verifier (anyone may call it directly): 0x6959e9561f84f7C7901EBC386f5ddcA2D6899D1B ### Guarantees - No mixer. We do not pool other people's funds to break transfer graphs. - We never accept your viewing key or your spending key. If any interface ever asks for one, it is not us. - No identity checks for privacy features. Privacy is not gated behind KYC; that would defeat it. - We do not claim privacy we cannot deliver. Where a guarantee is incomplete, the docs say so — see the IN DEVELOPMENT section above, which exists precisely so you do not over-trust the system. ## Verification and proof (things you cannot get anywhere else) Most relays ask you to trust a receipt. These endpoints let you check instead. All are read-only. ### Verify what actually happened, on-chain GET /api/outcome/verify/{id}?recipient=0x...&minAmount=1000000 Checks a settled transfer against the DESTINATION CHAIN, not against our database. We fetch the payout transaction, decode its ERC-20 Transfer logs, and tell you what really happened. You supply the post-conditions; we report whether reality matches. It also cross-checks our own receipt against the chain and will tell you if our books disagree with it. THREE-VALUED: verified | failed | undetermined. `undetermined` (chain unreachable, not yet mined, unknown id) is NOT a pass. Treat it as unknown, never as success. The response includes the destination tx hash so you can reproduce the entire result without us. COVERS EVERY RAIL — and is candid where it cannot: Allbridge/Chainflip/Solana/Tron record completion as an observed balance delta, not a receipt we can re-decode, so those return `undetermined` with the reason stated rather than `verified` on the strength of a database row. For non-EVM we now DO query the chain and return a `balanceObservation` (live balance vs recorded baseline + delivered). It never replaces the verdict: a balance cannot prove a specific transfer, because tokens are fungible and recipients spend them. ### One bundle with all the evidence GET /api/evidence/bundle/{id}?recipient=0x...&minAmount=... POST /api/evidence/verify Evidence bundle = the on-chain verification + the unit-by-unit fee breakdown + what the alternative route would have cost + whether the delivery time is measured or estimated, assembled in ONE call and signed as a whole. This is what you hand to a principal who was not there. READ `verdict` FIRST. It is the WEAKEST of the parts, never the strongest — a complete fee breakdown does not mean settlement was confirmed. If `verdict` is `undetermined`, do NOT describe the transfer as complete. Components that could not be produced appear as `unavailable` with a reason and are listed in `incomplete`. They are never silently dropped. Swapping any component invalidates the signature. MCP: robyn_evidence_bundle ### Prove what you spent GET /api/statement/spend?address=0x...&days=30 -> signed statement POST /api/statement/verify -> check one An agent spending on someone's behalf cannot normally PROVE what it spent: its own summary is a claim by the party being audited. This returns every transfer in the window, each fee broken down, SIGNED by Robyn's relayer key (standard EIP-191 personal_sign over a content digest). Your principal verifies the signature against the published signer address — no need to trust the agent, or a dashboard, or us. Alter any number and verification fails: the digest is recomputed from the rows submitted, never taken from the `digest` field. COVERS EVERY RAIL now (float lane + CCTP/Stellar/Solana/Tron/Chainflip/aggregators) with a `byRail` split, so a merged total cannot hide which rails were used. Third-party bridge fees taken inside a routed leg are excluded and `limitations` says so. ### Prove you were refused (not that you failed) Send `x-anygas-receipt: 1` on any request. If we REFUSE it, the error response carries a signed `refusalReceipt` — what was asked, when, and the reason we actually returned. Why this matters: when a provider says no, the AGENT carries the blame. Your principal sees an incomplete task and has only your account of why. "The relay refused me" is indistinguishable from "I got it wrong" or "I never tried". A receipt makes the external refusal provable. POST it to /api/refusal/verify, or verify the EIP-191 signature offline against the published signer. The JS SDK ships `verifyRefusalOffline()` and `verifyStatementOffline()` which make NO network call: they recompute the signed digest from the data you hold and recover the signer locally. A principal can therefore check an agent's evidence without trusting the agent, the SDK, or Robyn. Rewrite the reason or errorCode and it stops validating — blame cannot be shifted in either direction. It proves the refusal HAPPENED; it is not a claim the refusal was CORRECT. Use it to evidence that you were blocked, or to contest the decision with us. Opt-in and error-only: successful requests never mint one. GET /api/refusal/info · GET /api/refusal/recent ### See where every unit of a fee went GET /api/forensics/fee/{id} Everyone quotes a fee; nobody says where it went. This reconstructs a settled transfer unit by unit — destination payout gas, Robyn margin, or a Robyn SUBSIDY where we absorbed a loss (reported as such). Anything unattributable is returned as `unattributed` rather than folded into another line: a breakdown that always sums perfectly is one that is hiding something. GET /api/forensics/counterfactual/{id} What the same transfer would have delivered on the standard bridge path. A like-for-like model comparison — stated as such, not dressed up as a live competing quote. ### Know how long it will really take GET /api/delivery/stats MEASURED settlement times per rail and corridor, not a constant. Below the sample threshold it returns `insufficient-data` and labels the fallback `basis: "estimate"` rather than inventing a percentile. Quotes carry the same `deliveryBasis` field, so you always know whether a duration is measured or guessed. ### Ask another agent for money — verifiably POST /api/payreq/prepare -> { request, digest } (the PAYEE signs the digest; Robyn never does) POST /api/payreq/verify -> check one you received BEFORE paying it POST /api/payreq/settlement-> prove on-chain that you satisfied it You can pay other agents, but until now you could not ASK verifiably. Sending "send 5 USDC to 0x…" over an unauthenticated channel is exactly the message address-substitution theft preys on: the payer has no way to tell whether the address was altered in transit. A payment request is signed BY THE PAYEE. Rewrite the payee address or the amount and the signature stops recovering to them. ALWAYS verify before paying: if `signedByPayee` is false, do not pay. A valid signature proves AUTHENTICITY, not that you owe it and not that the payee is honest. Robyn holds no payee keys and never signs these — a request signed by Robyn would prove only that Robyn said so. MCP: robyn_payment_request_prepare / _verify / _settlement ### Spend inside a budget your principal set POST /api/mandate/prepare -> budget for the PRINCIPAL to sign (Robyn never signs it) POST /api/mandate/register -> principal registers it POST /api/mandate/check -> WOULD THIS SPEND FIT? call before spending GET /api/mandate/status -> remaining budget POST /api/mandate/consume -> record a spend (verified on-chain, and YOU must sign it) requires `agentSignature` over keccak256("v1|robyn-mandate-consume|||") — otherwise anyone knowing the mandateId could misattribute a real transfer to your budget and exhaust it Every other control on agent spending sits on the wrong side of the trust boundary: a limit you enforce on yourself is a limit you can drop. A mandate is signed by your PRINCIPAL and enforced outside you. If check returns EXCEEDS_MANDATE, DO NOT split the spend into smaller pieces to get under the limit. That defeats the control your principal set. Ask them to raise it. Consumption counts only transfers VERIFIED ON-CHAIN, and counts what the chain shows arrived — not what you claim. You cannot inflate your remaining budget, and you cannot hide a spend that happened. PER-SPEND APPROVAL: a mandate can carry `cosignAbove`. Spends above that threshold need the PRINCIPAL to countersign that specific payment — a total budget alone still permits one catastrophic transfer. The approval digest binds the mandate, the transfer AND the amount, so it cannot be replayed on another payment. If a spend is refused for want of approval, ask the principal to sign it. DO NOT split the spend to slip under the threshold. SCOPE: covers spending THROUGH ROBYN only. It is not custody and does not stop spending elsewhere with the same key. GET /api/mandate/list -> every budget a principal has granted (you cannot revoke what you cannot find) GET /api/mandate/events -> spend / nearly-exhausted / approval-required / revoked, with a cursor pull feed, not webhooks — an untrusted caller must not get to name a URL this host will fetch POST /api/mandate/cosign-request -> does this spend need the principal to approve it? POST /api/mandate/revoke -> the PRINCIPAL withdraws the budget (irreversible) GET /api/mandate/certificate -> signed proof you stayed within it A principal can revoke at any time and it takes effect immediately — check and consume both stop. You cannot revoke your own mandate; only the principal who granted it can (401 otherwise). The certificate is what you hand your principal at the end of a period. It certifies adherence to a ROBYN budget — it does NOT certify you spent nothing elsewhere, and says so. Never present it as proof of total spending. MCP: robyn_mandate_check / robyn_mandate_status / robyn_mandate_certificate ### Ask what would make it cheaper POST /api/route/improve Every router tells you the price. This tells you what to CHANGE. Each suggestion is quantified from the same cost table the router charges from: a cheaper destination chain with the exact saving, the size at which a fixed payout cost stops dominating, whether the instant float lane applies, and whether rails that would have competed were unavailable. CRITICAL: suggestions with `changesOutcome: true` move WHERE THE MONEY LANDS. Never apply those silently as savings — confirm the alternative destination is acceptable first, or you have saved gas by delivering to the wrong place. If nothing can be improved it says so rather than inventing filler. MCP: robyn_improve_cost ### Find out what the "best" route actually beat POST /api/route/why -> signed rationale for a routing decision POST /api/route/why/verify Every router returns a winner. None tells you what it beat. "Best route" reads the same whether five rails competed or four failed to answer and the survivor won by default — so you cannot tell a good price from the only price. CHECK `wasCompetitive` BEFORE TELLING ANYONE THE PRICE IS GOOD. If false, one rail answered and "best" means "only"; it did not beat anything. `silentRails` did NOT lose — they failed to answer and each carries a reason, a `status` ("not-configured" = missing credentials, will NEVER recover on retry; "degraded" = rate limit or cooldown, may self-heal) and `recoversOnRetry`. Never describe a silent rail as more expensive. Also returns every candidate's delivered amount and the margin the winner won by. MCP: robyn_why_this_route ### Check what Robyn can actually do before you depend on it GET /api/capability/snapshot -> signed, timestamped record of current capability POST /api/capability/verify USE `serviceableNow`, NOT `policyEligible`. A chain can be eligible by policy while holding no inventory — the instant lane is then unavailable and the transfer falls through to a bridge rail. `notServiceable` lists exactly that difference. Right now BNB (56) and Avalanche (43114) are eligible but NOT serviceable. Also reports per-rail executeReady, node health, and whether delivery figures are measured or estimated. Fetch it BEFORE committing to a multi-step plan and archive it with your decision: if the plan later fails, it is evidence of the capability you were advertised. It is a RECORD, not a guarantee. It does not promise the same capability a minute later and the signature does not make it an SLA. MCP: robyn_capability_snapshot ## Plan a whole workflow before you pay for step one POST /api/route/plan {"steps":[{...RouteRequest},{...RouteRequest}], "constraints":{"maxTotalFeeBps":60,"maxTotalSeconds":120,"requireNonCustodial":true}} Every other quoting API validates ONE hop. So you find out step 3 is impossible after paying for steps 1 and 2 — and money spent reaching a dead end is not refunded by discovering it was a dead end. This validates the whole plan up front. Fees and durations ACCUMULATE across steps. Evaluation stops at the first unroutable step instead of reporting speculative results behind it. If the plan is infeasible you get the BINDING CONSTRAINT and the value that would work (`bindingConstraint`, `whatWouldMakeThisWork`) — not a bare "no" that sends you into blind retries. READ-ONLY: nothing is signed, reserved or moved. Each feasible step carries a signed `attestation` — pass it with `minOut` to that step's execute call and execution is BOUND to the terms you were shown. Without it a plan is advice only and the price can move between validating and running it. ## Rehearse failures before you meet them in production Send `x-anygas-rehearse: ` together with `x-anygas-sandbox: 1` on ANY endpoint. You get back the byte-identical error the real path would produce — same errorCode, retryable flag and suggestedAction — plus `rehearsed: true`. This is how you test your retry and recovery logic against real error contracts instead of guessing at them. - Refused on live traffic (no sandbox header), so production can never be steered into a failure. - UNAUTHORIZED, RATE_LIMITED and DUPLICATE_REQUEST are deliberately NOT rehearsable: an authorisation or duplicate-detection result must never be fakeable. - GET /api/errors lists every code you can rehearse. ## Bind execution to the quote you were given Quotes carry an `attestation` (signed material terms + digest). Pass `minOut` and the `attestation` to the execute call and the transfer is refused BEFORE custody if it cannot meet your floor. A tampered attestation is rejected — the digest is recomputed from the submitted terms, never trusted as supplied. ## Supported chains EVM (22): Ethereum, Optimism, BNB Chain, Gnosis, Unichain, Polygon, Sonic, World Chain, HyperEVM, Lisk, Soneium, Robinhood Chain, Mantle, Base, ApeChain, Mode, Arbitrum, Avalanche, Ink, Linea, Blast, Zora. Non-EVM: Stellar and Solana (via Allbridge Core), and Bitcoin (native, via Chainflip). ## Source https://github.com/ox31461/Gasless-Anygas-ROBYN-Relay ## Terms Robyn AnyGas is free to integrate. The client packages are MIT-licensed. The hosted relayer, routing service, and contracts are proprietary: build ON Robyn, do not replicate or resell the service itself. See https://anygas.xyz/svc/api/terms ## AnyGas Account — yield-native gasless spending (non-custodial) Hold ONE yield-bearing position (USDC auto-allocated to the best of Aave v3 + Moonwell, in your own wallet) and spend it as any token or native gas on any of 26 chains, gaslessly, just-in-time — the remainder keeps earning. No custody, no pool, no honeypot. - GET /svc/api/ncaccount/{agent} -> your aUSDC per chain + allowance granted to the relayer + APY - POST /svc/api/ncaccount/quote {agent,srcChain,amount,toChain,toAddress} -> read-only JIT quote - POST /svc/api/ncaccount/spend {intent,signature,live:true} -> spend with ONE EIP-712 signature intent = {agent,srcChain,amount,toChain,toAddress,nonce,deadline}; domain {name:'RobynNCAccount',version:'1',chainId:srcChain} Setup once: supply USDC to Aave v3 or Moonwell in your wallet, then approve the relayer to spend your aUSDC/mUSDC up to your risk budget. Your held balance earns interest the whole time it pays your gas. - SDK: import { RobynAgent } from '/svc/robyn-agent-kit.js'; await agent.yieldSpend({srcChain,amount,toChain,toAddress}) Related: GET /svc/api/account/{addr} (yield-aware balance), /svc/api/account/venues (live APYs), GET /svc/api/balance/{addr} (unified balance), GET /svc/api/receipts/{addr} (signed history), POST /svc/api/events/subscribe (price/balance webhooks). ## Adoption & economics - Yield pays your gas: AnyGas Account spends carry a 0.15% (min $0.01) fee that repays relay gas; your ~3-4% Aave/Moonwell yield typically exceeds it, so spending is net-free. - Best-yield venues: GET /svc/api/account/venues ranks live USDC APYs across Aave v3 + Moonwell per chain (bestByChain). - Referral: POST /svc/api/referral/register {referrer} -> code; referred agents POST /svc/api/referral/link {agent, code}. Referrer earns 10% of their referrals' fees for 12 months (self-funding, $25 anti-sybil floor, 15% global cap). - Proof: GET /svc/api/proof (routes, AUM, agents, fees) + /svc/api/proof/badge.svg. - Standing gas policy: POST /svc/api/gaspolicy/set {agent, chains:{id:{minNative,topUpNative}}, webhook} -> we alert (signed) when a worker is low and plan the top-up from your yield. - One-call spend: SDK agent.spend({to, amount, chain}) auto-picks yield vs idle. ## ZERO-FUNDS QUICKSTART (try everything with $0) No account, no key, no funds needed to evaluate AnyGas: 1. Status: GET https://anygas.xyz/svc/api/status 2. Chain map: GET https://anygas.xyz/svc/api/gasless/info (router+paymaster per chain) 3. Quote a route: POST https://anygas.xyz/svc/api/route/quote {"fromChain":8453,"toChain":42161,"fromToken":"USDC","toToken":"USDC","amount":5} 4. Dry-run spend: POST https://anygas.xyz/svc/api/ncaccount/spend {"intent":{...},"signature":"0x...","live":false} (live:false returns the full execution plan without moving funds) 5. Pay-per-call: GET https://anygas.xyz/svc/api/x402/premium-demo (returns HTTP 402 + x402 accepts[] — settle $0.001 USDC on Base via EIP-3009 to see the paid response; see /svc/api/x402/info) Python: pip install anygas (repo: sdk-python/) | npm: anygas-agent-kit | MCP: https://anygas.xyz/mcp Ops transparency: GET /svc/api/floatwatch/info (relayer float, 23 chains), GET /svc/api/funnel/info (usage funnel) ## Netting accounts — free transfers, and the ONE custodial part of this service Everything else here is non-custodial. Netting is not, so it is opt-in and never applied by default. An enrolled account holds a USDC balance WITH Robyn. A transfer between two enrolled accounts is a book entry: no chain, no gas, no bridge, no fee, and no transaction hash — which makes it free at ANY size, including amounts below the per-chain payout cost that bounds even the float lane. - Enrol: GET /api/netting/terms?address=0x... returns the exact text; sign it with that account's own key and POST /api/netting/enroll {address, signature}. A signature that does not recover to the address is refused, so nobody can enrol an account they do not control. - Both parties must be enrolled. We will not hold a balance for a recipient who never agreed; pay an unenrolled address with the normal non-custodial /api/route/execute instead. - Fund: send USDC to the relayer on any covered chain, then POST /api/netting/deposit {address, chainId, txHash}. Only what actually arrived is credited, and only to the sender. - Move: POST /api/netting/transfer {from, to, amount, nonce, signature} — free and instant. - Exit: POST /api/netting/withdraw {address, amount, toChain, toAddress, nonce, signature}. - Solvency is published, not asserted: GET /api/netting/reserves compares what we owe against the real float backing it, per chain. Deposits are refused if they would push liabilities too close. - What you accept: the balance is an obligation Robyn owes you, not tokens you control on-chain. If Robyn fails or is compelled, you could lose it. Keep only working amounts here. ## Stellar settles in BOTH directions evm -> stellar (Circle CCTP V2 burn, Soroban mint_and_forward) and stellar -> evm (Soroban deposit_for_burn, Circle attestation, receiveMessage on the destination chain) are both live and proven on-chain. GET /api/route/chains reports executeDirections per leg; every Stellar quote carries a `settlement` block with live availability, so an outage is visible at quote time rather than after you have built a plan around the rail. ## Unfinished payouts heal themselves If a crash ever interrupts a transfer between taking your funds and paying them out, the obligation is recorded before the payout and a keeper completes it automatically within minutes. It never double-pays: it only completes a payout it can prove did not happen. GET /api/floatlane/pending should always be empty — anything there is money owed, published so you can check. ## Private by default (round 4, 2026-08-20) - ONE wire for everyone. Every private-lane request is a hybrid ML-KEM-768+X25519 envelope (suite 0x50) padded to ONE size (16 KiB) and every response to ONE size (8 KiB): a quote, a sealed message, a pass redemption and a cover row are identical bytes to the relay. POST the envelope to https://relay.nightferry.net/relay (Iceland, separate operator) — it sees your IP and nothing else; Robyn sees the request and only the relay's IP. - GET /api/ohttp/keys publishes the PQ key AND the allowlist of paths reachable over the relay (reads + planning: route/chains|settle|plan|why|preflight|rollup|attestation, posture, recipes, errors, changelog, plus/info, netting/info|preview|terms, membership/status, mandate reads, receipt verify/deliver, all of messaging v2). execute/submit/purchases stay direct on purpose. - anygas-mcp (npx anygas-mcp) rides the relay by default: ANYGAS_LANE=private|strict|direct; every result carries _lane; a relay failure is an ERROR, never a silent downgrade. Browser: window.AnyGas.lane (same default); Node: /svc/msg2-sdk.mjs (ANYGAS_LANE). - GET /api/posture?chainId= — what is private / public for this chain + the lane you are on. Call it first. - ANONYMOUS QUOTA: a one-time Plus blind pass is now the anonymous API key. Header x-anygas-pass = base64(JSON {preparedMsg,sig}) (or body.pass over the relay) exempts that request from the limiter and links to nothing. Free: POST /api/plus/trial (20/day). x-anygas-key still works if linkability is acceptable. - Receipts to counterparties: POST /api/receipt/deliver {to, receipt} seals a relayer-signed settlement receipt into the counterparty's v2 mailbox (same wire shape as any message); POST /api/receipt/verify checks one you received. - Code attestation: GET /api/route/attestation now signs a manifest of every deployed module and the exact hash/SRI of each client bundle (code.bundles). Compare before trusting a loaded SDK. - Messaging v1 is RETIRED (writes 410). One messenger: sealed v2, maximum posture by default. ## Agent quickstarts (paste-ready) MCP (Claude Desktop / Claude Code / Cursor) — local via npm, or zero-install hosted: { "mcpServers": { "anygas": { "command": "npx", "args": ["anygas-mcp"] } } } Hosted (Streamable HTTP, no install): { "mcpServers": { "anygas": { "url": "https://anygas.xyz/mcp" } } } Vercel AI SDK (TypeScript): import { tool } from 'ai'; import { z } from 'zod'; export const anygasQuote = tool({ description: 'Quote a gasless cross-chain route or same-chain swap (fromChain==toChain, different tokens). Response includes next-step calls under `next`.', parameters: z.object({ fromChain: z.number(), toChain: z.number(), fromToken: z.string(), toToken: z.string(), amount: z.string().describe('source-token base units, e.g. 25000000 = 25 USDC') }), execute: async (p) => (await fetch('https://anygas.xyz/api/route/quote', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(p) })).json(), }); LangChain (Python): from langchain_core.tools import tool import requests @tool def anygas_quote(fromChain: int, toChain: int, fromToken: str, toToken: str, amount: str) -> dict: """Quote a gasless cross-chain route or same-chain swap via AnyGas. amount is source-token base units.""" body = {"fromChain": fromChain, "toChain": toChain, "fromToken": fromToken, "toToken": toToken, "amount": amount} return requests.post("https://anygas.xyz/api/route/quote", json=body, timeout=30).json() Useful follow-ups every agent should know: GET /api/route/chains (the graph + quote-only set + sameChainSwaps), GET /api/route/bestofn (proof our best-of-N beats single sources, measured on real quotes), POST /api/hooks/lane {chainId, url} (webhook fired once when a quote-only chain becomes executable), POST /api/keys/create (free key, no email, higher rate ceiling).