API Reference
Robyn AnyGas is gasless, cross-chain relay infrastructure for AI agents and onchain apps. A relayer fronts the network gas and is repaid from the token being moved, so one signed intent transacts across 22 EVM chains, Stellar, Solana and native Bitcoin — 25 nodes, 500 directed routes — with no native gas balance held anywhere.
Every endpoint below was verified against the live API before publishing. The base URL is https://api.anygas.xyz/svc (equivalently https://anygas.xyz/svc). Prefer not to hand-roll HTTP? The MCP server, JS SDK and Python package wrap all of this. The raw machine spec is at /openapi.json.
Quickstart
Pick the surface that matches how your agent runs. All four talk to the same live gateway and default to it with zero config.
MCP server (Claude, Cursor, any MCP client)
Run it locally with npx, or point at the hosted, read-only streamable-HTTP endpoint — no install, no key.
{
"mcpServers": {
"robyn": { "command": "npx", "args": ["anygas-mcp"] }
}
}
# or hosted (zero install, public, read-only):
# https://api.anygas.xyz/mcp — streamable HTTP
# tools: robyn_mesh, robyn_quote, robyn_cross_chain, robyn_route_statusJavaScript / TypeScript
// npm i anygas-agent-kit (MIT · also on JSR as @anygas/agent-kit) import { RobynAgent } from 'anygas-agent-kit'; const robyn = new RobynAgent(); // zero-config → this gateway const quote = await robyn.route({ // gasless cross-chain quote fromChain: 42161, toChain: 8453, fromToken: 'USDC', toToken: 'USDC', amount: '5000000' });
Python
# pip install anygas from anygas import Robyn robyn = Robyn() # zero-config → this gateway mesh = robyn.chains() # the live route graph quote = robyn.compare(fromChain=42161, toChain="solana", fromToken="USDC", toToken="USDC", amount="50000000")
First call — a quote with plain curl
No SDK, no key. Ask what a cross-chain move would deliver:
curl -s https://api.anygas.xyz/svc/api/route/compare \ -G --data-urlencode "fromChain=42161" \ --data-urlencode "toChain=solana" \ --data-urlencode "fromToken=USDC" \ --data-urlencode "toToken=USDC" \ --data-urlencode "amount=50000000"
execute → status → DONE flow with zero funds and no funded relayer: add the header x-anygas-sandbox: 1 to POST /api/route/execute. You get a real-shaped response and a sbx_… id that progresses BRIDGING → DONE in ~40s. See Sandbox mode.Conventions
- Base URL —
https://api.anygas.xyz/svc. The gateway strips/svcand proxies to the service. All paths below are shown as/api/…; prefix them with/svcover HTTP. - Amounts are always strings in the token's smallest units (e.g. USDC 6dp →
"5000000"= 5 USDC; BTC in sats). - Chain ids — EVM chains are numeric ids; non-EVM nodes are the literal strings
"solana","stellar","bitcoin"(and"sui"for quote-only comparison). - Tokens — a symbol like
USDC/USDT/BTC, or a token contract address. Omit for the chain's native asset. - Routing fee — Robyn withholds 0.25% (
25bps) from the token in flight; it is never taken in native gas. Every quote returnsrobynRouteFeeBps. - Optional API key — the API is keyless. Passing
x-anygas-key: <key>tags requests for usage visibility (see keys). It is never required. - Rate limits are per-IP, per-endpoint (shown on each endpoint). Over the limit →
429 {"error":"rate limited"}. - Errors —
4xx/5xxwith{"error":"…"}. Reads degrade gracefully rather than throw.
Sandbox mode
Add x-anygas-sandbox: 1 to POST /api/route/execute and Robyn simulates the entire lifecycle — no relayer spend, no signature, nothing on-chain. The response mirrors a live execute and hands back a sbx_… id. Poll GET /api/route/status?id=sbx_… to watch it walk RECEIVING → SWAPPING → SENDING → COMPLETED over ~40s. Sandbox routes live in memory for 1h and are never persisted or shown in the public explorer.
curl -s https://api.anygas.xyz/svc/api/route/execute \ -H 'content-type: application/json' \ -H 'x-anygas-sandbox: 1' \ -d '{"fromChain":42161,"toChain":8453,"fromToken":"USDC", "toToken":"USDC","amount":"5000000", "toAddress":"0x8EdE0eEb8C03a45886836A1baDec03CdB08cDFb2"}'
{
"status": "BRIDGING", "sandbox": true, "gasless": true,
"id": "sbx_611d3343c9b8a0edae",
"srcTx": "0xsandbox…",
"to": { "chain": 8453, "token": "USDC", "estimated": "4987500", "recipient": "0x8EdE…DFb2" },
"robynRouteFeeBps": 25,
"note": "SANDBOX — no funds moved. Poll GET /api/route/status?id=sbx_… (~40s)."
}Health & status
Sanitized, up/down health for the trust page: per-chain node health, bridge-rail health, and endpoint probes. No addresses, amounts or revenue. Cached ~30s.
{
"status": "operational", "ts": 1784012730722,
"nodes": { "up": 25, "total": 25,
"evm": [ { "chainId": 1, "name": "Ethereum", "up": true }, /* …22 EVM… */ ],
"nonEvm": [ { "name": "Stellar", "up": true }, { "name": "Solana", "up": true }, { "name": "Bitcoin", "up": true } ] },
"rails": [ { "name": "bungee", "up": true }, { "name": "debridge", "up": true }, /* … */ ],
"routes": 500,
"endpoints": [ { "name": "route/chains", "up": true }, /* … */ ]
}The live route graph: node chain ids, node/route counts, the Permit2 spender (relayer) your SDK signs for, the routing engine, and rich per-node metadata for the non-EVM legs. This is the source of truth for what is routable.
{
"nodes": 25,
"chains": [1,10,56,100,130,137,146,480,999,1135,1868,4663,5000,8453,33139,34443,42161,43114,57073,59144,81457,7777777],
"routableNonEvm": ["stellar","solana","bitcoin"],
"directedRoutes": 500,
"relayer": "0x1253D25A0B6a757CfD65CDf441E2d91DBeCeE6c5",
"permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
"engine": "LI.FI aggregation (Across / Gas.zip / Near / Squid / Symbiosis / Relay + ~15 bridges); Stellar + Solana via Allbridge Core; Bitcoin via Chainflip",
"nonEvmNodes": { "stellar": {…}, "solana": {…}, "bitcoin": {…}, "tron": {…}, "sui": { "quoteOnly": true, "executeReady": false } },
"comparatorQuoteOnly": { /* Scroll, Chainflip asset lanes, Sui — quotable, NOT execute-ready */ }
}nodes/directedRoutes are execute-ready. Anything under comparatorQuoteOnly or carrying executeReady:false (Sui, Scroll, and the extra Chainflip asset lanes) is quotable for comparison only — see caveats.Everything an integrator needs to sign a same-chain gasless intent: the deployed router / anyGas / tokenPaymaster addresses, Permit2, EntryPoint, the EIP-712 domain, the per-chain deployment map (gaslessChains) and the accepted fee tokens.
{
"service": "Robyn Gasless Pay",
"router": "0x8f4a466435264A2D56Bc80e6749Ea351d567D141",
"anyGasRouter": "0x298ab4Be2B3a8f1A2dB3650f578407AA1f952487",
"tokenPaymaster": "0x4afc3DD9143bC75fCfFDA52e9E5e50dA4Cb3A635",
"permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
"entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"chainId": 4663, "routes": ["direct","cheapest","fastest"], "recommended": "direct",
"gaslessChains": { "1": { "address": "0x68B5…Ae80", "stable": "0xA0b8…eB48", "version": "3.1" }, /* …per chain… */ },
"acceptedTokens": [ { "symbol": "WETH", … }, { "symbol": "USDG", … } ],
"domain": { "name": "RobynGaslessRouter", "version": "1", "chainId": 4663, "verifyingContract": "0x8f4a…D141" }
}Quote a cross-chain intent
Best gasless route for a single intent. EVM↔EVM runs best-of-N over the bridge aggregators (LI.FI / Bungee / deBridge); a Stellar / Solana / Bitcoin side is dispatched to the dedicated leg (Allbridge Core / Chainflip). One signed intent; Robyn fronts all gas on both sides plus the bridge.
POST with a JSON body. A GET query form is not implemented and returns 404 — send the body.| Field | Type | Notes | |
|---|---|---|---|
fromChain | id / string | required | Source node. |
toChain | id / string | required | Destination node. |
amount | string | required | Source-token smallest units. |
fromToken | string | optional | Symbol or address; omit for native. |
toToken | string | optional | Symbol or address; omit for native. |
toAddress | string | optional | Recipient; defaults to fromAddress. |
fromAddress | string | optional | Quote origin; anonymous default used if omitted. |
slippage | number | optional | Fraction, e.g. 0.005. |
curl -s https://api.anygas.xyz/svc/api/route/quote \ -H 'content-type: application/json' \ -d '{"fromChain":42161,"fromToken":"USDC","toChain":"bitcoin", "toToken":"BTC","amount":"50000000","toAddress":"bc1q…"}'
{
"gasless": true,
"from": { "chain": 42161, "token": "USDC", "amount": "50000000" },
"to": { "chain": "bitcoin", "token": "BTC", "estimated": "78660", "min": "76693", "recipient": "bc1q…" },
"bridge": "chainflip", "via": ["Chainflip (REGULAR)"], "durationSec": 408,
"robynRouteFeeBps": 25, "robynRouteFee": "125000",
"chainflip": { "egressAmount": "78660", "fees": [ {"type":"INGRESS",…}, {"type":"NETWORK",…}, {"type":"EGRESS",…} ] },
"note": "Bitcoin leg via Chainflip (NATIVE BTC — no wrapping). egress is in sats."
}EVM↔EVM lanes return a best-of-N shape instead: bridge, bestOf, and a compared[] array of per-aggregator outputs.
Execute a route, relayer-orchestrated and gasless. The relayer fronts all gas, tries ranked routes in net-output order and fails over on error, then returns a route id you can track. Use mode:"permit2" so the user signs one off-chain Permit2 SignatureTransfer and pays no gas.
| Field | Type | Notes | |
|---|---|---|---|
fromChain,toChain,amount | — | required | As in quote (smallest units). |
fromToken,toToken,toAddress | string | optional | Token symbols/addresses; recipient. |
mode | string | optional | "permit2" (user-signed pull) or "self" (operator moves own funds). Default "self". |
permit2 | object | cond. | Required for permit2: {owner, permitted:{token,amount}, nonce, deadline, signature}. |
sbx_ id, no relayer spend. See Sandbox mode. Non-EVM legs settle only once value lands; when the relayer isn't pre-funded for a lane, execute returns an honest FUNDS_REQUIRED rather than a fake tx.{
"id": "rt_1a2b3c4d5e6f", "status": "BRIDGING", "gasless": true,
"bridge": "across", "aggregator": "lifi",
"robynFee": "12500", "bridged": "4987500",
"from": { "chain": 42161, "token": "0xaf88…5831", "amount": "5000000" },
"to": { "chain": 8453, "token": "0x8335…2913", "recipient": "0x…", "min": "4980000" },
"srcTx": "0x…", "track": "/api/route/status?id=rt_1a2b3c4d5e6f"
}Track an in-flight route. Live ids (rt_…) are re-checked against the underlying bridge on each read; sandbox ids (sbx_…) advance on a timer. Survives a service restart.
{
"id": "sbx_611d3343c9b8a0edae", "sandbox": true,
"status": "BRIDGING", "stage": "RECEIVING",
"srcTx": "0xsandbox…", "destTx": null, "elapsedSec": 0,
"to": { "chain": 8453, "token": "USDC", "estimated": "4987500", "recipient": "0x8EdE…DFb2" }
}Unknown id → 404 {"error":"unknown route id"}. Live routes reach status:"DONE" with a destTx once delivered.
Best-execution comparator
Quotes every rail that can serve a lane in parallel — the bridge aggregators (LI.FI / Bungee / deBridge), Allbridge Core and Chainflip — normalizes them, ranks by net destination output, and flags the single best. Nothing is signed or moved. Adds advisory fields: effectiveFeePct (same-asset lanes), a 0–100 qualityScore, a min-economical-size guard, and a 12s response cached flag. Accepts the same params as quote, via query string (GET) or JSON body (POST).
{
"lane": { "fromChain": "42161", "toChain": "solana", "applicableRails": ["allbridge","chainflip"], "estOutUnit": "USDC smallest-units" },
"quotes": [
{ "rail": "allbridge", "ok": true, "estOut": "49822154", "etaSeconds": 120,
"best": true, "effectiveFeePct": 0.3556, "qualityScore": 97 },
{ "rail": "chainflip", "ok": true, "estOut": "49265533", "etaSeconds": 109,
"effectiveFeePct": 1.4689, "qualityScore": 96 }
],
"best": "allbridge", "comparedRails": 2, "railsAttempted": 2,
"guard": { "ok": true, "effectiveFeePct": 0.3556, "thresholdPct": 3 }, "cached": false
}guard.ok is false with a warning and a best-effort recommendedMinInput — fixed bridge costs dominate tiny moves.Goal-based source picker. State a goal (max-output · cheapest · fastest), a destination, and a set of candidate funding sources; the solver reads /compare once per source and ranks them. It returns a plan with ready-to-POST execute params — it never executes; the caller decides.
| Field | Type | Notes | |
|---|---|---|---|
toChain,toToken | — | required | Destination node + asset. |
from | array | required | 1–10 sources: [{chain,token,amount}]. |
goal | string | optional | Default max-output. |
amount | string | optional | Shared amount if sources omit their own. |
toAddress | string | optional | Recipient, echoed into plan.execute. |
curl -s https://api.anygas.xyz/svc/api/intent/solve \ -H 'content-type: application/json' \ -d '{"goal":"max-output","toChain":8453,"toToken":"USDC", "from":[{"chain":42161,"token":"USDC","amount":"5000000"}, {"chain":10,"token":"USDC","amount":"5000000"}]}'
{
"goal": "max-output", "to": { "chain": 8453, "token": "USDC" },
"plan": { // winner (null if nothing quoted)
"from": { "chain": 42161, "token": "USDC", "amount": "5000000" },
"rail": "allbridge", "estOut": "…", "qualityScore": 97,
"execute": { "fromChain": 42161, "fromToken": "USDC", "toChain": 8453, "toToken": "USDC", "amount": "5000000" }
},
"alternatives": [ … ], "skipped": [ … ], "sourcesConsidered": 2,
"note": "Plan only — NO funds moved. To run it, POST plan.execute to /api/route/execute yourself."
}Anonymized feed of recent real routes settled by the relayer (powers the public Explorer). No addresses, tx hashes or exact amounts — just lane, asset, rail, a coarse status band and size bucket. limit default 25, max 100.
{
"routes": [
{ "lane": "Solana → Arbitrum", "asset": "USDC", "rail": "Allbridge", "status": "pending", "when": "3h ago", "size": "small", "sandbox": false },
{ "lane": "Arbitrum → Bitcoin", "asset": "USDC → BTC", "rail": "Chainflip", "status": "pending", "when": "4h ago", "size": "small", "sandbox": false }
],
"count": 2, "note": "Anonymized recent cross-chain routes settled by the Robyn AnyGas relayer."
}Route primitives
PRIMITIVES_LIVE=1 and the record must carry live:true. The shipped default (live:false) simulates every tick — it logs the exact wouldExecute params and moves nothing. Do not assume a 200 here settles anything.Plan a multi-recipient / multi-leg payout. Body: { routes: [{fromChain,fromToken,toChain,toToken,amount,toAddress}], live? } — up to 20 legs, each planned via /compare. toAddress is required per leg.
{
"id": "batch_086f0679dfac4a905d", "status": "planned", "live": false,
"legs": [ { "index": 0, "route": {…}, "plan": { "ok": true, "rail": "allbridge", "estOut": "49822154", "effectiveFeePct": 0.3556 } } ],
"warnings": [],
"note": "Planned only — NOT executed. The watcher SIMULATES this batch (record live:false). No funds have moved."
}Scheduled / recurring transfer. Body: { route, everySeconds?, at?, count?, live? } — everySeconds ≥ 10, count 1–1000 (omit everySeconds for a one-shot). GET the id to inspect, DELETE to cancel.
{
"id": "sched_cde9a12f1107fa3652", "route": {…},
"everySeconds": 3600, "remaining": 24, "count": 24, "live": false, "status": "active", "runs": [],
"note": "Scheduled — live:false, so it SIMULATES (logs wouldExecute, moves no funds)."
}
// DELETE /api/route/schedule/sched_… → { "id": "sched_…", "status": "cancelled" }Trigger-based transfer. Body: { route, when, live? } where when.type is priceAbove · priceBelow (needs when.asset + numeric when.value) or timeAfter (epoch/ISO when.value). Price conditions arm against the on-chain feeds (/api/pricefeeds: ETH/USD, BTC/USD, USDG/USD).
{
"id": "cond_3728705d2654926bc1", "route": {…},
"when": { "type": "priceBelow", "asset": "ETH/USD", "value": 3000 },
"live": false, "status": "armed", "runs": [],
"note": "Armed — live:false, so it SIMULATES. Price source: on-chain /api/pricefeeds."
}Developer keys
Optional self-serve key for a stable identity + usage visibility. The API stays keyless; a key just tags your requests. Body: { label? }. The key is shown once — store it and send it as x-anygas-key: <key>.
{
"key": "ak_9fa383a39ca3b33b74775e7d8f3d04c70b98057c",
"label": "docs-example", "created": 1784012809055,
"note": "Store this key — shown once. Send it as x-anygas-key: ak_… . The API works without a key."
}Usage stats for your key — pass it via x-anygas-key header or ?key=. Returns hour + day aggregates and recent calls. (GET /api/keys/me returns just the key metadata.)
{
"key": { "label": "docs-example", "created": 1784012809055 },
"hour": { "total": 0, "errors": 0, "topPaths": [] },
"day": { "total": 0, "errors": 0, "topPaths": [] },
"recent": []
}Register a completion webhook for a route id — fire-and-forget a cross-chain move and get called on DONE/FAILED instead of polling. Body: { routeId, url, secret? }. Watch state at GET /api/hooks/<id>.
{
"id": "wh_1008e656e4d46f7aec", "routeId": "rt_abc123",
"url": "https://example.com/webhook", "status": "pending",
"note": "We POST {event, routeId, status, destTx, srcTx, ts} on DONE/FAILED."
}secret, each delivery carries x-anygas-signature: sha256=HMAC(secret, rawBody) — verify it before trusting the payload.Custodial capped sub-wallets for autonomous agents. The platform holds an encrypted session key; the agent gets a bearer token and every action is policy-checked (chain / token / recipient allowlists, per-tx and rolling-24h caps, expiry, revoke) before it is signed and forwarded to /api/route/execute. The agent never holds the key, and the funded balance is a hard ceiling.
POST /api/session/issue— owner mints a session + bearer (returns the address to fund).POST /api/session/execute— bearer (Authorization: Bearer <rsk_…>) runs a policy-gated gasless move.GET /api/session/<id>— status, policy, spend (bearer or owner).POST /api/session/revoke·GET /api/session/list— owner only.
Minting requires the platform OWNER_KEY, so this is for first-party / hosted deployments — not anonymous self-serve.
Honest caveats
- MIT clients, proprietary relay. The SDKs / MCP server / adapters are MIT-licensed. The hosted relayer, routing service and contracts are proprietary — build on Robyn; do not replicate or resell the service. See Terms.
- Settles only if value lands. Execute is honest: for lanes where the relayer isn't pre-funded (most non-EVM sends) it returns
FUNDS_REQUIRED/ anAWAITING_DEPOSITaddress rather than inventing a success. A route reachesDONEonly when the destination actually receives. - Small transfers are dominated by fixed costs. Bridge ingress/egress + network fees are largely size-independent; the compare
guardflags when your amount is uneconomical and suggests arecommendedMinInput. - Quote-only expanded lanes. Sui (EVM/Solana/Stellar↔Sui), ETH→BTC and SOL→BTC, plus Scroll and the extra Chainflip asset lanes, are quotable via /compare but not execute-ready (
executeReady:false) — Robyn holds no signer for those source assets yet. They are excluded from the headlinenodes/directedRoutescounts on purpose. - qualityScore is advisory. It blends output, speed and a static per-rail reliability prior (a heuristic, not a measured success rate).
bestis always ranked by netestOut, never by the score.