Robyn AnyGas

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.

No key required for reads Reads are safe & side-effect free v3.1 · live

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.

mcp.json
{
  "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_status

JavaScript / TypeScript

agent.ts
// 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

agent.py
# 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 · first quote
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"
Sandbox modeBuild and test the full 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 URLhttps://api.anygas.xyz/svc. The gateway strips /svc and proxies to the service. All paths below are shown as /api/…; prefix them with /svc over 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% (25 bps) from the token in flight; it is never taken in native gas. Every quote returns robynRouteFeeBps.
  • 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"}.
  • Errors4xx/5xx with {"error":"…"}. Reads degrade gracefully rather than throw.

Sandbox mode

No funds · no keySame shape as live

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 · sandbox execute
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"}'
200 · verified response (trimmed)
{
  "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

GET/api/status60 / min

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.

200 · verified (trimmed)
{
  "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 }, /* … */ ]
}
GET/api/route/chainsno key

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.

200 · verified (trimmed)
{
  "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 */ }
}
Read the flagsNodes counted in 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.
GET/api/gasless/infono key

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.

200 · verified (trimmed)
{
  "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

POST/api/route/quote40 / min

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 onlyThis endpoint accepts POST with a JSON body. A GET query form is not implemented and returns 404 — send the body.
FieldTypeNotes
fromChainid / stringrequiredSource node.
toChainid / stringrequiredDestination node.
amountstringrequiredSource-token smallest units.
fromTokenstringoptionalSymbol or address; omit for native.
toTokenstringoptionalSymbol or address; omit for native.
toAddressstringoptionalRecipient; defaults to fromAddress.
fromAddressstringoptionalQuote origin; anonymous default used if omitted.
slippagenumberoptionalFraction, e.g. 0.005.
curl · quote to native Bitcoin
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…"}'
200 · verified response (trimmed)
{
  "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.

POST/api/route/execute8 / min

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.

FieldTypeNotes
fromChain,toChain,amountrequiredAs in quote (smallest units).
fromToken,toToken,toAddressstringoptionalToken symbols/addresses; recipient.
modestringoptional"permit2" (user-signed pull) or "self" (operator moves own funds). Default "self".
permit2objectcond.Required for permit2: {owner, permitted:{token,amount}, nonce, deadline, signature}.
Header · x-anygas-sandbox: 1Set this header to simulate the whole flow with no funds — same response shape, a 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.
200 · live response shape (from router)
{
  "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"
}
GET/api/route/status?id=<id>no key

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.

200 · verified sandbox status
{
  "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

GETPOST/api/route/compare30 / min
Read-only · quote-only

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).

200 · verified — Arbitrum USDC → Solana USDC (2 rails ranked)
{
  "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
}
The guard protects small transfersWhen the best rail's effective fee at the requested size exceeds ~3%, guard.ok is false with a warning and a best-effort recommendedMinInput — fixed bridge costs dominate tiny moves.
POST/api/intent/solve20 / min
Read-only · returns a plan

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.

FieldTypeNotes
toChain,toTokenrequiredDestination node + asset.
fromarrayrequired1–10 sources: [{chain,token,amount}].
goalstringoptionalDefault max-output.
amountstringoptionalShared amount if sources omit their own.
toAddressstringoptionalRecipient, echoed into plan.execute.
curl · pick the best of two balances
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"}]}'
200 · verified response shape
{
  "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."
}
GET/api/routes/recent?limit=N60 / min

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.

200 · verified response
{
  "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

Gated · dry-run by default
These plan & store — they do not move fundsBatch / schedule / conditional only build a plan and write a JSON record. Execution belongs to a separate watcher and fires only under a double gate: the watcher process must run with 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.
POST/api/route/batch30 / min

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.

200 · verified (live:false, per-leg plan)
{
  "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."
}
POST/api/route/schedule30 / min
GETDELETE/api/route/schedule/<id>

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.

200 · verified (hourly ×24, simulated)
{
  "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" }
POST/api/route/conditional30 / min
GETDELETE/api/route/conditional/<id>

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).

200 · verified (armed, simulated)
{
  "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

POST/api/keys/create5 / hour

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>.

200 · verified
{
  "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."
}
GET/api/keys/usagekey required

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.)

200 · verified
{
  "key": { "label": "docs-example", "created": 1784012809055 },
  "hour": { "total": 0, "errors": 0, "topPaths": [] },
  "day":  { "total": 0, "errors": 0, "topPaths": [] },
  "recent": []
}
POST/api/hooks/register120 / min

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>.

200 · verified
{
  "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."
}
SignatureIf you set secret, each delivery carries x-anygas-signature: sha256=HMAC(secret, rawBody) — verify it before trusting the payload.
POSTGET/api/session/*owner-gated
Owner key required to mint

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/issueowner mints a session + bearer (returns the address to fund).
  • POST /api/session/executebearer (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/listowner 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 / an AWAITING_DEPOSIT address rather than inventing a success. A route reaches DONE only when the destination actually receives.
  • Small transfers are dominated by fixed costs. Bridge ingress/egress + network fees are largely size-independent; the compare guard flags when your amount is uneconomical and suggests a recommendedMinInput.
  • 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 headline nodes/directedRoutes counts on purpose.
  • qualityScore is advisory. It blends output, speed and a static per-rail reliability prior (a heuristic, not a measured success rate). best is always ranked by net estOut, never by the score.