reference
Everything Ava serves, and nothing it does not
Each endpoint below was called against production while this page was written, and the responses are pasted as they came back. Where a call fails today, the failure is printed too.
The loop
A wallet answers whether a transaction is safe to sign. Ava answers a different question: is the agent doing the job it was given. That needs the job written down first.
- 01
Session. One request creates a user id. One human owns many agents under it.
- 02
Mandate. Objective, capital, constraints, and the conditions that end the position, all agreed before the agent acts.
- 03
Evaluation. Ava reads the market itself and tests the conditions, recording what it observed.
- 04
Record. Every mandate, refusal and receipt lands on the agent's record, with proven counted apart from claimed.
Base URL and transport
From a terminal or a server the API is https://ava-v4-api.fly.dev. Everything is JSON over HTTPS. There is no websocket and no SSE.
From a browser, use relative paths. This site proxies /v1, /health and /mcp to the API from its own origin, so a page never makes a cross-origin request, never pays for a preflight, and never depends on a second hostname resolving. The API keeps a CORS allowlist for anyone who calls it directly, and a browser on an origin outside that list is blocked before the request leaves it.
Authentication
POST /v1/users/session is open: no CAPTCHA, no email, no wallet popup. It returns a userId that names you and a token that proves you are that user. Send the token as Authorization: Bearer on every other call. It is shown once; Ava stores only its SHA-256 digest and cannot show it again. Agents may also send x-ava-agent-id to scope work to one instance.
A userId in a header, a query string, a request body, or an MCP tool argument is a claim, never identity. Ava compares it with the token and refuses a disagreement with USER_ID_ASSERTION_MISMATCH rather than quietly correcting it, so a client confused about whose capital it is moving finds out before the money does.
Headless callers use an API key bound to one user id server-side (AVA_API_KEYS in userId:key form). There is no key that can act as an arbitrary tenant.
Ownership failures answer 404 rather than 403 throughout. Asking for a mandate or a record that belongs to someone else must not confirm that the id exists.
curl -sS -X POST https://ava-v4-api.fly.dev/v1/users/session \
-H 'content-type: application/json' \
-d '{}'captured 2026-07-29
{
"ok": true,
"user": {
"userId": "usr_96a9bb92c52be687",
"createdAt": "2026-07-29T12:26:17.051Z"
},
"wallets": [],
"agents": [],
"token": "ava_st_<32 random bytes, base64url, shown once>",
"tokenId": "stk_6f0a1c2d3e4b5a69",
"sessionKind": "created",
"requestId": "c8728537-7bca-40b3-8be5-289a6f07ff8a"
}REST surface
These are the endpoints this site uses. The API serves more, and they are documented where they are stable.
| endpoint | auth | what it does |
|---|---|---|
| POST /v1/users/session | none | Creates a user id and mints its bearer token, or resumes the session your token already names. Body {}. |
| POST /v1/users/me/agents | user | Registers an agent instance. Body { portalSlug, agentId, label? }. agentId must exist in the catalog; use byo-external for Claude Code, Cursor, OpenClaw or your own. |
| POST /v1/users/me/wallets/provision | user | Provisions a custodial agent wallet in its own Turnkey sub-organisation. Body { family: "evm" | "solana" | "sui" }. Returns a real address. |
| POST /v1/mandates | user | Creates a mandate from structured fields or from a natural-language message. |
| POST /v1/mandates/:id/evaluate | user | Tests the mandate's conditions. Omit the snapshot and Ava pulls a live price for the condition asset. |
| GET /v1/mandates | user | Lists your mandates, filterable by portal, agent instance and status. |
| GET /v1/agents/:agentInstanceId/record | user | The agent's track record. Owner-only unless the owner opted it public. |
| POST /v1/agents/:agentInstanceId/record/visibility | owner | Body { visibility: "owner" | "public" }. Only the owner may call it. |
| GET /v1/portal/catalog | none | Portals, their chains and their wallet families. |
| GET /v1/capabilities | none | Adapters, brokers, wallet providers and their current mode. |
| GET /health | none | Liveness, degraded flag and dependency modes. |
Create a mandate
objective is an enum, not free text, and constraints.maxNotionalUsd is a number. Both are the two mistakes the schema rejects most often.
curl -sS -X POST https://ava-v4-api.fly.dev/v1/mandates \
-H 'content-type: application/json' \
-H 'authorization: Bearer ava_st_YOUR_TOKEN' \
-d '{
"objective": "earn",
"capital": { "asset": "USDC", "amount": "1000", "chain": "base" },
"constraints": { "maxNotionalUsd": 500 }
}'captured 2026-07-29
{
"ok": true,
"mandate": {
"mandateId": "mdt_df43bc9cee6c41aa",
"userId": "usr_fa1149c27487e34e",
"portal": "base",
"name": "earn 1000 USDC (775d1e)",
"status": "active",
"objective": "earn",
"capital": { "asset": "USDC", "amount": "1000", "chain": "base" },
"constraints": { "maxNotionalUsd": 500 },
"conditions": [],
"action": { "type": "paper_plan_lend", "payload": {} },
"createdAt": "2026-07-29T12:27:59.338Z",
"updatedAt": "2026-07-29T12:27:59.338Z"
},
"nextStep": "Mandate is active. Evaluate with POST /v1/mandates/:id/evaluate (live price when snapshot omitted). MCP: ava_create_mandate / ava_eval_mandate.",
"requestId": "777c3ad6-9b32-4ca4-a355-3fc4347863e2"
}Evaluate a mandate
With an exit condition attached, evaluation fetches a live price and records what it saw. Trimmed here to the fields this page discusses; the full response also returns the updated mandate and the matched condition indexes.
curl -sS -X POST https://ava-v4-api.fly.dev/v1/mandates/mdt_b8a740bcf7e1490d/evaluate \
-H 'content-type: application/json' \
-H 'authorization: Bearer ava_st_YOUR_TOKEN' \
-d '{}'captured 2026-07-29
{
"matched": true,
"evaluation": {
"matched": true,
"reason": "conditions_matched:price_condition_met",
"observed": {
"price": 1905.05,
"priceChangePct": 1.4799914918104407,
"matchedIndexes": [0]
},
"actionDispatched": false
},
"snapshot": {
"price": 1905.05,
"priceChangePct": 1.4799914918104407,
"priceChangePct1h": 1.4799914918104407,
"source": "coingecko",
"asset": "ETH",
"observedAt": "2026-07-29T12:28:15.178Z"
}
}Read a track record
captured 2026-07-29, from an agent created seconds earlier
{
"ok": true,
"record": {
"recordVersion": "ava.agent-record.v1",
"agentInstanceId": "5f24f968-32a6-4622-87ff-dd5e39259ea7",
"visibility": "owner",
"stats": {
"mandatesHeld": 0,
"mandatesActive": 0,
"executionsAttempted": 0,
"receiptsTotal": 0,
"receiptsNoProof": 0,
"receiptsUnconfirmed": 0,
"receiptsProven": 0,
"refusalsTotal": 0,
"refusalsByCode": []
},
"verification": {
"receiptDigest": "sha256(canonicalJson(unsignedReceipt))",
"mandateHash": "sha256(canonicalJson(extractMandateTerms(mandate)))",
"provenRule": "receiptsProven counts only receipts whose proofStanding is chain-confirmed"
},
"generatedAt": "2026-07-29T12:28:58.164Z"
}
}Zero is zero. A new agent has an empty record and Ava does not fill it with sample history.
The mandate object
| field | type | notes |
|---|---|---|
| objective | enum | earn, hold, trade, rebalance, protect. |
| capital | { asset, amount, chain } | amount is a string so precision survives. Required unless a natural-language message supplies it. |
| constraints | object | maxDrawdownPct, maxSlippageBps, maxNotionalUsd, allowedKinds, preferredVenue. All numbers, all optional. |
| conditions[] | { asset, metric, comparator, threshold } | metric is price or price_change_pct. comparator is lt, lte, eq, gte or gt. Without a condition, evaluation answers no_conditions. |
| action | { type, payload } | notify, paper_plan_swap, paper_plan_lend or eval_only. Derived from the objective when omitted. |
| status | enum | active, paused, triggered, completed, cancelled. Evaluation moves it to triggered when a condition fires. |
| message | string | Alternative to the structured fields. "Earn on 500 USDC on Base, max 5% drawdown, exit if ETH below 2000" parses into capital, constraints and conditions. |
MCP tools
Ava speaks MCP as JSON-RPC over HTTP at /mcp and /v1/mcp. GET returns the tool list, POST takes tools/list and tools/call. There is no stdio binary to install.
{
"mcpServers": {
"ava": {
"type": "http",
"url": "https://www.getava.xyz/mcp",
"headers": {
"Authorization": "Bearer ava_st_PASTE_YOUR_SESSION_TOKEN"
}
}
}
}The install guide writes your real session id into this block.
15 tools, read live from the endpoint
| tool | what it does |
|---|---|
| ava_copilot_turn | Natural-language copilot turn: message → intent → plan → testnet quote. Example: Swap 10 USDC to SUI on sui with 50 bps slip. Returns actions with approve_execute + executionId. Optional agentId/agentCredential for KYA gate (fail closed on reject). Does NOT fill until ava_approve_execute. |
| ava_approve_execute | Human-in-the-loop approve: the one call that actually settles a plan from ava_copilot_turn. It is gated by the bound mandate's status and constraints and by server-side policy limits, and it CAN REFUSE (e.g. mandate paused/cancelled, a policy violation, a stale preview hash); a refusal means no funds moved and no success receipt was written, only a refusal record. Testnet mode settles against simulated balances and returns a receipt + before/after balances, never a real chain. Mainnet mode signs with the caller's own Turnkey wallet and submits for real where a venue is live, and fails closed everywhere else, so it never claims a fill it cannot show. Call ava_list_venues for which routes are live rather than assuming; that tool reads the same registry this one enforces, so it cannot drift from what will actually execute. NEVER call without explicit user confirmation of the previewed quote. |
| ava_lend_execute | Execute a REAL lend against one of your mandates on a route Ava has proven on mainnet: Morpho Blue on Base, or Aave v3 on Monad, BNB Chain and Avalanche. This is the only MCP tool that moves live capital. Two-phase by design: call it once WITHOUT previewHash to receive the exact artifact a human must approve plus its hash, show that to the human, then call again WITH that previewHash. Your call is never treated as the human's confirmation, because you are not the person whose money moves. Only a settlement verified against the venue's own on-chain event returns a txHash; anything else has no txHash field at all, so a draft can never be narrated as a fill. |
| ava_portfolio | Simulated testnet portfolio for a userId (same data as GET /v1/portfolio): seeded balances, fills, optional portal. Requires userId. Does not invent live chain balances. |
| ava_session | Create or resume a human/org identity (userId), the first call in the loop. One human can own many agent instances (Claude + Cursor + OpenClaw + Ava-hosted). Moves no money and provisions no wallet by itself; call once and reuse the returned userId on every other tool. |
| ava_create_agent | Register an agent instance under userId, before it has a wallet or a mandate. Mode A (multi-agent): agentId=byo-external + label (e.g. claude-workspace, cursor-arb) for a coding agent that already exists elsewhere. Mode B (hosted): agentId=defi-lend|defi-swap|defi-perp|portfolio hires an Ava-run agent. One userId may own many agent instances; this call alone never touches funds. |
| ava_provision_wallet | Provision a Turnkey-custodied wallet address for this userId, scoped to one chain family (evm | solana | sui). Requires ava_session first. Returns the address for funding and later signing; it does not fund the wallet and does not move any money. If Turnkey is not configured server-side, the wallet comes back with status pending_provision rather than a fabricated active address, and cannot sign or hold funds until it is active. |
| ava_preview_tx | Pre-sign preview: build the exact venue artifact (CoW EIP-712 order or unsigned Solana transaction) for a pending plan against the provisioned Turnkey wallet, simulate it where supported, and return it UNSIGNED. Nothing is signed or submitted. Show the artifact to the human, then pass the returned previewHash to ava_approve_execute so the signature covers exactly what was reviewed. |
| ava_plan_workflow | Turn ONE natural-language DeFi request into the dependent actions it actually contains, across one chain or several. Example: "Supply 300 USDC to Morpho on Base, bridge no more than 200 to Avalanche, then supply what arrives to Aave" returns three legs with the third depending on the second and taking its amount from what the bridge actually delivered, not a plan-time guess. Same-chain works identically: "swap 100 USDC to WETH on Base then supply the WETH to Aave" records the swap OUTPUT token so the second leg is denominated in WETH. Plans nothing it cannot execute: a clause naming an unsupported operation, an unnamed venue, a zero or negative amount, or one amount split across venues comes back in `unsupported` with a reason rather than being guessed at. Signs nothing and moves nothing. Every leg reports `executable` so you can see what Ava could actually run today. |
| ava_plan_standing | Turn a request for REPEATED autonomous action into an envelope of bounds the user signs once. Use this instead of ava_plan_workflow when the request describes a cadence: "every hour, rotate my USDC into the best yield on Base using Aave and Morpho, never move more than 200 per rotation, never exceed 1000 total, stop after 30 days". Asking a human to approve each rotation would defeat the point, so the user authorizes LIMITS rather than a plan: allowed operations, venues, chains, per-move and cumulative caps, a minimum gain that stops the agent churning capital for fees, an expiry, and revocation. Every bound must come from the user: anything missing is returned in `missing` and the envelope is NOT signable until supplied. minGainBps is required from you because users say "the best yield" rather than a basis-point floor, and Ava must not choose how much of their money goes to gas. Returns the envelope unsigned; nothing is authorized until the user signs it. |
| ava_create_mandate | Create a capital mandate: the objective, capital and constraints an agent is allowed to act under, optionally scoped to an agentInstanceId. Example: "Earn on 500 USDC on Base, max 5% drawdown". Or pass structured capital {asset,amount,chain}. Creating a mandate moves no money by itself and is not yet signed by an external wallet: it is Ava's own record of what was asked for, not a user-authorised instruction a stranger can rely on. Every later ava_approve_execute run under this mandate is gated on its status (active/paused/cancelled) and constraints, and can refuse. |
| ava_list_mandates | List capital mandates for a userId (optional filter by agentInstanceId). |
| ava_eval_mandate | Read-only: check a mandate's exit conditions against a market snapshot and record the result. Omit snapshot to pull a live CoinGecko price for the condition asset. Never executes and never moves money; ava_approve_execute is the only tool that does. |
| ava_agent_record | Read an agent's track record before delegating capital to it. Returns mandates held, executions attempted, refusals by typed code, and receipts with their proof standing. Distinguishes CLAIMED from PROVEN: receiptsProven counts only chain-confirmed receipts; unconfirmed receipts prove nothing. AP2-aligned (each receipt's ap2.reference is the hash of the closed mandate) so an AP2-aware verifier can check it. Owner-readable always; a stranger reads only if the owner opted the record public. Zero is zero, never a placeholder. |
| ava_get_receipt | Retrieve the receipt for a prior ava_approve_execute by executionId, the loop's final step. Returns the stored receipt plus its honest proof.standing: none (nothing submitted), unconfirmed (an identifier exists but nothing independent confirmed it), chain-confirmed (the declared chain returned the transaction and it matches what the receipt claims), or chain-contradicted (the chain disagrees with the receipt). A verified receiptHash does not by itself mean chain-confirmed; read proof.standing, not just verified. Owner-scoped: only the userId the receipt was produced for can read it by executionId; also returns a receiptHash-addressed verify URL a stranger can check with no credential. |
What is actually live
Every chain and venue Ava lists is one row in a capability registry, read live below rather than reproduced from a roadmap. The list is long and most of it will not run today. That is the honest state of the product, so the rows are split by whether you can execute them right now and each one that you cannot says why in plain words.
A row is executable only when three separate things are true at once: implemented means the adapter code exists, apiReachable means a public route on the authenticated API reaches it, and operatorEnabled means the operator has turned it on. Two of three is not a capability. They are kept apart because they are fixed by three different pieces of work, and collapsing them into one word is what made this list unreadable.
The same registry is served as JSON at /capabilities.json, already split into executable and not, with the rule above stated in the payload. Prefer it over parsing the table below: this layout is written for a person reading the page and is not a contract. If you would rather watch the loop run than read about it, the unedited terminal recording is a session, a wallet, a mandate and two real refusals against the live API, with nothing cut.
A mainnet proof is a historical fact about one transaction. It is never permission to execute now. A row that once settled on chain and is switched off today is not a route you can call, and this page will not let the first fact stand in for the second.
The proof that exists is worth naming exactly. A 0.1 USDC supply to the gtUSDCp vault on Base settled in block 49315911, and its receipt was the first to carry proof.standing: chain-confirmed. It is verifiable without a credential. chain-confirmed is not shorthand for a transaction hash existing: Ava decoded the transaction and compared five fields, status, token, amount, sender and recipient, against what the receipt claims. A hash that exists but disagrees returns chain-contradicted, which is treated as worse than no proof at all.
executable now
4
of 58 rows
not executable
54
47 dry-run, 7 descriptor only
mainnet proven
5
settled on chain at least once, which is not permission to run now
Executable over the authenticated API now
These accept a live execution request today. Everything else on this page does not, whatever its venue is called.
| capability | chain | venue | mainnet proof | notes |
|---|---|---|---|---|
| lend | avalanche | aave_v3 | settled on mainnet once | Aave v3 supply/withdraw route descriptor for Avalanche C-Chain, sharing the exact aave_v3 pipeline shape (mandate -> capability gate -> AaveExecutor -> independent settlement re-read -> receipt) the live Monad route uses; only the chain and its @ava/adapter-aave-verified Pool/reserve addresses differ. Live: a Turnkey-signed 0.1 USDC supply settled on Avalanche C-Chain mainnet in block 92231544 and its Supply event was independently re-read from a node that did not broadcast it. |
| base | morpho_blue | settled on mainnet once | Morpho MetaMorpho ERC-4626 supply and withdraw on Base mainnet via @ava/adapter-morpho MorphoExecutor. Signing and broadcast are injected ports; every reported amount is read back from the confirmed Deposit/Withdraw event, and an unconfirmed broadcast is never reported as filled. | |
| bnb | aave_v3 | settled on mainnet once | Aave v3 supply and withdraw on BNB Chain mainnet via @ava/adapter-aave AaveExecutor, the third live venue after Morpho on Base and Aave on Monad. Signing and broadcast are injected ports; every reported amount is read back from the confirmed Supply/Withdraw event, and an unconfirmed broadcast is never reported as filled. Live: a Turnkey-signed 0.1 USDT supply settled in block 114508635 and its Supply event was independently re-read from two nodes that did not broadcast it. USDT here is 18 decimals, unlike USDT on most chains. | |
| monad | aave_v3 | settled on mainnet once | Aave v3 supply and withdraw on Monad mainnet via @ava/adapter-aave AaveExecutor. Signing and broadcast are injected ports; every reported amount is read back from the confirmed Supply/Withdraw event, and an unconfirmed broadcast is never reported as filled. Live: a Turnkey-signed supply settled on Monad mainnet in block 92954146 and the resulting aToken balance was confirmed by an independent RPC read. |
Not executable
Each row names the first thing standing between it and a live call. A dry-run row is not a broken row: it builds a real quote and real calldata against a real venue and then stops, deliberately, before anything is signed.
| capability | chain | venue | execution | why not | notes |
|---|---|---|---|---|---|
| bridge | arbitrum | across | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Across Protocol v3 bridge adapter. @ava/adapter-bridge exists and builds real depositV3 calldata against verified SpokePool addresses for USDC and WETH. Fees, quoteTimestamp, fillDeadline and exclusiveRelayer come from the live Across suggested-fees API and are never fabricated, which is why the broker-router serves this route from routeIntentAsync rather than the synchronous path. A bridge outcome is two-legged: source-confirmed and destination-pending. Prepare-only: never signs or submits. |
| arbitrum | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| avalanche | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| avalanche | teleporter | descriptor-only | not implemented: there is no adapter behind this row | DESCRIPTOR ONLY. No Teleporter adapter package. Avalanche portal has no working bridge/swap path. | |
| base | across | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Across Protocol v3 bridge adapter. @ava/adapter-bridge exists and builds real depositV3 calldata against verified SpokePool addresses for USDC and WETH. Fees, quoteTimestamp, fillDeadline and exclusiveRelayer come from the live Across suggested-fees API and are never fabricated, which is why the broker-router serves this route from routeIntentAsync rather than the synchronous path. A bridge outcome is two-legged: source-confirmed and destination-pending. Prepare-only: never signs or submits. | |
| base | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| bsc | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| ethereum | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| gnosis | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| optimism | across | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Across Protocol v3 bridge adapter. @ava/adapter-bridge exists and builds real depositV3 calldata against verified SpokePool addresses for USDC and WETH. Fees, quoteTimestamp, fillDeadline and exclusiveRelayer come from the live Across suggested-fees API and are never fabricated, which is why the broker-router serves this route from routeIntentAsync rather than the synchronous path. A bridge outcome is two-legged: source-confirmed and destination-pending. Prepare-only: never signs or submits. | |
| optimism | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| polygon | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | LI.FI aggregator bridge adapter. @ava/adapter-lifi exists, consumes LI.FI API calldata under strict validation and permits only the published LiFiDiamond per chain as spender and call target. LI.FI quotes are third-party claims, never Ava's own construction. The package's diamond map covers many more chain ids; the chains listed here are the ones Ava routes. Prepare-only: never signs or submits. | |
| compute | base | virtuals | descriptor-only | not implemented: there is no adapter behind this row | DESCRIPTOR ONLY. No @ava/adapter-virtuals package exists. No compute path. |
| data | base | virtuals | descriptor-only | not implemented: there is no adapter behind this row | DESCRIPTOR ONLY. No @ava/adapter-virtuals package exists. No data path. |
| identity | base | virtuals | descriptor-only | not implemented: there is no adapter behind this row | DESCRIPTOR ONLY. No @ava/adapter-virtuals package exists. Namespace reservation for the Virtuals identity / agent-token ecosystem. Do not claim Virtuals execution. |
| lend | arbitrum | aave | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Aave v3 lend adapter. @ava/adapter-aave exists and builds real unsigned approve/supply/withdraw/borrow/repay calldata against Pool addresses taken from the Aave address book and verified with read-only RPC. Monad (143) reserves USDC/USDT0/AUSD/USDe/WETH/cbBTC are fork-proven end to end. Prepare-only: it never signs or submits. The broker-router lend dispatch routes to this adapter on Monad only; Base lend still routes to Morpho, and arbitrum/optimism/avalanche remain descriptor-only here so enabling them cannot silently reroute existing intents. |
| arbitrum | compound | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Compound v3 (Comet) USDC lend adapter. @ava/adapter-compound exists and builds real unsigned approve/supply/withdraw calldata against Comet addresses verified with live cast calls. Prepare-only: never signs or submits. | |
| avalanche | aave | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Aave v3 lend adapter. @ava/adapter-aave exists and builds real unsigned approve/supply/withdraw/borrow/repay calldata against Pool addresses taken from the Aave address book and verified with read-only RPC. Monad (143) reserves USDC/USDT0/AUSD/USDe/WETH/cbBTC are fork-proven end to end. Prepare-only: it never signs or submits. The broker-router lend dispatch routes to this adapter on Monad only; Base lend still routes to Morpho, and arbitrum/optimism/avalanche remain descriptor-only here so enabling them cannot silently reroute existing intents. | |
| base | aave | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Aave v3 lend adapter. @ava/adapter-aave exists and builds real unsigned approve/supply/withdraw/borrow/repay calldata against Pool addresses taken from the Aave address book and verified with read-only RPC. Monad (143) reserves USDC/USDT0/AUSD/USDe/WETH/cbBTC are fork-proven end to end. Prepare-only: it never signs or submits. The broker-router lend dispatch routes to this adapter on Monad only; Base lend still routes to Morpho, and arbitrum/optimism/avalanche remain descriptor-only here so enabling them cannot silently reroute existing intents. | |
| base | compound | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Compound v3 (Comet) USDC lend adapter. @ava/adapter-compound exists and builds real unsigned approve/supply/withdraw calldata against Comet addresses verified with live cast calls. Prepare-only: never signs or submits. | |
| base | morpho | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Prepare/quote alias for the Morpho Base lend route. Builds real unsigned approve+deposit calldata and a live previewDeposit quote, and never signs or submits. The executable route is venue morpho_blue. | |
| monad | aave | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Aave v3 lend adapter. @ava/adapter-aave exists and builds real unsigned approve/supply/withdraw/borrow/repay calldata against Pool addresses taken from the Aave address book and verified with read-only RPC. Monad (143) reserves USDC/USDT0/AUSD/USDe/WETH/cbBTC are fork-proven end to end. Prepare-only: it never signs or submits. The broker-router lend dispatch routes to this adapter on Monad only; Base lend still routes to Morpho, and arbitrum/optimism/avalanche remain descriptor-only here so enabling them cannot silently reroute existing intents. | |
| optimism | aave | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Aave v3 lend adapter. @ava/adapter-aave exists and builds real unsigned approve/supply/withdraw/borrow/repay calldata against Pool addresses taken from the Aave address book and verified with read-only RPC. Monad (143) reserves USDC/USDT0/AUSD/USDe/WETH/cbBTC are fork-proven end to end. Prepare-only: it never signs or submits. The broker-router lend dispatch routes to this adapter on Monad only; Base lend still routes to Morpho, and arbitrum/optimism/avalanche remain descriptor-only here so enabling them cannot silently reroute existing intents. | |
| sui | suilend | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Sui lending adapter. @ava/adapter-suilend exists and builds quotes plus unsigned Sui transaction payloads. The broker-router lend dispatch prepares ava.lend.order-draft.v1 for this venue; symbol → coin type / market id resolution happens in the execution path. Never signs or submits. | |
| memory | offchain | membase | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Agent memory adapter. Prepare-only for money-execution purposes: @ava/adapter-membase exists and performs real HTTP reads/writes against Membase/Unibase via an injected fetch with fail-closed typed errors, but it moves no funds and never signs or submits a financial transaction, so this descriptor carries no money-execution authority. |
| payment | base | virtuals | descriptor-only | not implemented: there is no adapter behind this row | DESCRIPTOR ONLY. No @ava/adapter-virtuals package exists. No payment path. |
| perp | arbitrum | hyperliquid | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Hyperliquid API-wallet (trade-only agent wallet) prepare path. Prepare-only: never signs or submits an order, and never withdraws. Live submit requires HL agent key wiring that does not exist yet. |
| base | virtuals | descriptor-only | not implemented: there is no adapter behind this row | PLANNED, DESCRIPTOR ONLY. No @ava/adapter-virtuals package exists. Perp namespace reservation only. | |
| hyperliquid | hyperliquid | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Hyperliquid API-wallet (trade-only agent wallet) prepare path. Prepare-only: never signs or submits an order, and never withdraws. Live submit requires HL agent key wiring that does not exist yet. | |
| stake | base | virtuals | descriptor-only | not implemented: there is no adapter behind this row | PLANNED, DESCRIPTOR ONLY. No @ava/adapter-virtuals package exists. Staking namespace reservation only. The executable-shaped stake routes are lido and rocketpool on ethereum below. |
| ethereum | lido | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Lido liquid staking on Ethereum mainnet. @ava/adapter-stake exists and builds real stETH.submit(address) payable calldata plus wstETH.wrap / unwrap calldata against the canonical stETH (0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) and wstETH (0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0) contracts. The broker-router stake dispatch prepares ava.stake.order-draft.v1 from these encoders. Prepare-only: never signs or submits. | |
| ethereum | rocketpool | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Rocket Pool liquid staking on Ethereum mainnet. @ava/adapter-stake exists and builds real RocketDepositPool.deposit() payable calldata against 0xDD9bc35aE942eF0cFa76930954a156B3fF30a4E1, minting rETH. The deposit-pool-full revert is surfaced as a typed error rather than a crash. Prepare-only: never signs or submits. | |
| storage | sui | walrus | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Walrus decentralized storage adapter. Prepare-only for money-execution purposes: @ava/adapter-walrus exists and performs real HTTP publish/read against Walrus publisher and aggregator endpoints via an injected fetch, but it moves no funds and never signs or submits a financial transaction, so this descriptor carries no money-execution authority. |
| swap | arbitrum | cow | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | CoW Protocol registry authority remains disabled. apps/api-worker/src/lib/cow-live-submit.ts imports HttpCowTransport and can submit a signed order through the public copilot approval route, but that submit-only path does not use this registry entry and does not report a confirmed fill. |
| arbitrum | kyber | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | KyberSwap Aggregator adapter. @ava/adapter-kyber exists and wraps the two-step routes+build API, returning third-party calldata validated against an allowlist of verified MetaAggregationRouterV2 addresses per chain. Never fabricates a route or an amount. Prepare-only: never signs or submits. | |
| arbitrum | uniswap_v3 | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Uniswap v3 SwapRouter02 adapter. @ava/adapter-uniswap exists and builds real exactInputSingle / exactInput calldata with QuoterV2 pricing. Prepare-only: never signs or submits. Not yet wired into the broker-router swap dispatch, which still routes EVM swaps to CoW. | |
| avalanche | kyber | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | KyberSwap Aggregator adapter. @ava/adapter-kyber exists and wraps the two-step routes+build API, returning third-party calldata validated against an allowlist of verified MetaAggregationRouterV2 addresses per chain. Never fabricates a route or an amount. Prepare-only: never signs or submits. | |
| avalanche | uniswap_v3 | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Uniswap v3 SwapRouter02 adapter. @ava/adapter-uniswap exists and builds real exactInputSingle / exactInput calldata with QuoterV2 pricing. Prepare-only: never signs or submits. Not yet wired into the broker-router swap dispatch, which still routes EVM swaps to CoW. | |
| base | aerodrome | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Aerodrome DEX adapter, Base only. @ava/adapter-aerodrome exists, quotes via getAmountsOut and builds real swapExactTokensForTokens calldata with the Aerodrome Route[] struct. Fail-closed on any chain other than Base. Prepare-only: never signs or submits. | |
| base | cow | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | CoW Protocol registry authority remains disabled. apps/api-worker/src/lib/cow-live-submit.ts imports HttpCowTransport and can submit a signed order through the public copilot approval route, but that submit-only path does not use this registry entry and does not report a confirmed fill. | |
| base | kyber | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | KyberSwap Aggregator adapter. @ava/adapter-kyber exists and wraps the two-step routes+build API, returning third-party calldata validated against an allowlist of verified MetaAggregationRouterV2 addresses per chain. Never fabricates a route or an amount. Prepare-only: never signs or submits. | |
| base | uniswap_v3 | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Uniswap v3 SwapRouter02 adapter. @ava/adapter-uniswap exists and builds real exactInputSingle / exactInput calldata with QuoterV2 pricing. Prepare-only: never signs or submits. Not yet wired into the broker-router swap dispatch, which still routes EVM swaps to CoW. | |
| bnb | lifi | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Generic swap proof through the LI.FI aggregator on BNB Chain via @ava/executor-generic. A direct script settled 0.05 USDT to USDC in block 114518492 through SushiSwap. The API and execution worker do not wire GenericExecutor, so this route is not currently executable through Ava's public runtime. Prepare-only until that full path is wired and verified. | |
| bsc | kyber | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | KyberSwap Aggregator adapter. @ava/adapter-kyber exists and wraps the two-step routes+build API, returning third-party calldata validated against an allowlist of verified MetaAggregationRouterV2 addresses per chain. Never fabricates a route or an amount. Prepare-only: never signs or submits. | |
| ethereum | cow | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | CoW Protocol registry authority remains disabled. apps/api-worker/src/lib/cow-live-submit.ts imports HttpCowTransport and can submit a signed order through the public copilot approval route, but that submit-only path does not use this registry entry and does not report a confirmed fill. | |
| ethereum | kyber | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | KyberSwap Aggregator adapter. @ava/adapter-kyber exists and wraps the two-step routes+build API, returning third-party calldata validated against an allowlist of verified MetaAggregationRouterV2 addresses per chain. Never fabricates a route or an amount. Prepare-only: never signs or submits. | |
| gnosis | cow | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | CoW Protocol registry authority remains disabled. apps/api-worker/src/lib/cow-live-submit.ts imports HttpCowTransport and can submit a signed order through the public copilot approval route, but that submit-only path does not use this registry entry and does not report a confirmed fill. | |
| monad | uniswap | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Uniswap v3 swap on Monad via @ava/adapter-uniswap SwapRouter02. Prepare-only: emits ava.uniswap.swap.order-draft.v1 with amountOutMinimum=0 and requiresLiveQuote=true. A live quote via QuoterV2 must be fetched before signing to set a real slippage bound. Liquidity and routing verified only for USDC/WETH fee 3000 pool on Monad (0x25EF1a210fF55BcEe9F8fee979aAFf6bD1bE5Bf1). Unlisted pairs fail closed. Never signs or submits. | |
| optimism | kyber | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | KyberSwap Aggregator adapter. @ava/adapter-kyber exists and wraps the two-step routes+build API, returning third-party calldata validated against an allowlist of verified MetaAggregationRouterV2 addresses per chain. Never fabricates a route or an amount. Prepare-only: never signs or submits. | |
| polygon | kyber | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | KyberSwap Aggregator adapter. @ava/adapter-kyber exists and wraps the two-step routes+build API, returning third-party calldata validated against an allowlist of verified MetaAggregationRouterV2 addresses per chain. Never fabricates a route or an amount. Prepare-only: never signs or submits. | |
| sepolia | cow | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | CoW Protocol registry authority remains disabled. apps/api-worker/src/lib/cow-live-submit.ts imports HttpCowTransport and can submit a signed order through the public copilot approval route, but that submit-only path does not use this registry entry and does not report a confirmed fill. | |
| solana | jupiter | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Solana Jupiter registry authority remains disabled. apps/api-worker/src/lib/live-approve.ts imports submitLiveJupiterSwap and the public copilot approval route can sign and submit, but that submit-only path does not use this registry entry and does not report a confirmed fill. | |
| sui | cetus | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Sui Cetus concentrated-liquidity swap adapter. @ava/adapter-cetus exists and builds quotes plus unsigned Sui transaction payloads via injected transport. Never signs or submits. | |
| sui | deepbook | dry-run | dry-run only: builds a real quote and real calldata, stops before signing | Sui DeepBook order book swap adapter. @ava/adapter-deepbook exists and builds quotes plus unsigned Sui transaction payloads via injected transport. Never signs or submits. |
Refusal codes
Every failure returns { ok: false, error: { code, message, details? }, requestId }. The code is stable and worth branching on. The message is for humans and may change.
| code | status | meaning |
|---|---|---|
| AUTH_REQUIRED | 401 | No credential on a route that acts for a user. Create a session and send its token as Authorization: Bearer. |
| UNAUTHORIZED | 401 | A credential was presented and is not a valid session token or API key. Never silently downgraded to anonymous. |
| USER_ID_ASSERTION_MISMATCH | 403 | An x-ava-user-id header, a body userId, or an MCP userId argument named someone other than the authenticated caller. Refused, not overridden. |
| SESSION_CLAIM_REQUIRES_TOKEN | 403 | The user id you asked to resume is already held by a token. A user id is a name, not a password. |
| USER_NOT_FOUND | 404 | The credential resolves to an id with no session behind it. Create one first. |
| AGENT_VALIDATION_FAILED | 422 | Body was not { portalSlug, agentId }. |
| UNKNOWN_PORTAL | 422 | portalSlug is not one of the catalog slugs. |
| UNKNOWN_AGENT | 422 | agentId is not in the agent catalog. Arbitrary ids are refused, byo-external is the slot for your own agent. |
| WALLET_PROVIDER_LIMIT_REACHED | 503 | Each user provisions into its own Turnkey sub-organisation, so this now means the provider itself is at a real ceiling, not routine signup volume. |
| MANDATE_VALIDATION_FAILED | 422 | Body failed schema. objective must be one of the five enum values, and maxNotionalUsd must be a positive number rather than a string. |
| MANDATE_PARSE_FAILED | 422 | A natural-language message could not be parsed into a mandate and no structured capital was supplied. |
| MANDATE_CAPITAL_REQUIRED | 422 | Neither message nor capital produced a capital block. |
| MANDATE_NOT_FOUND | 404 | No mandate with that id belongs to this caller. Someone else's id answers the same way. |
| MANDATE_NOT_EVALUABLE | 409 | The mandate is cancelled or completed. |
| AGENT_RECORD_NOT_FOUND | 404 | The agent does not exist, or it does and its record is owner-only and you are not the owner. The two are deliberately indistinguishable. |
| VISIBILITY_VALIDATION_FAILED | 422 | Body was not { visibility: "owner" | "public" }. |
| INVALID_JSON | 422 | The request body was not valid JSON. |
| NOT_FOUND | 404 | No route matched. A missing path parameter usually causes this. |
Limits
Wallet provisioning is live: every agent wallet is created in its own Turnkey sub-organisation, so one user's growth cannot exhaust another's allowance, and provisioning returns a real address rather than a placeholder.
captured 2026-07-29
{
"ok": true,
"wallet": {
"walletId": "763e4194-55bc-43e5-9bdd-160de0763e0c",
"userId": "usr_c12fb8706494753a",
"family": "evm",
"address": "0xAF1e62e11329896cf75540D31ab86CE0490f6387",
"provider": "turnkey",
"status": "active",
"turnkeyWalletId": "63cdf133-2461-5a67-8c2a-df6c7fa918d7",
"turnkeyOrganizationId": "a198bc63-329d-45ac-b663-a36333b6d6a8",
"createdAt": "2026-07-29T13:57:49.906Z"
},
"nextStep": "Wallet is active and ready for policy-gated signing.",
"requestId": "77400897-3b68-458e-bcdb-8adaf8c33132"
}- The CoW broker runs in quote-only mode, so a swap returns a draft to inspect rather than a fill.
- Morpho lend on Base has signed and settled once on mainnet, and that single receipt is the whole of Ava's execution history. Every other route above stops at unsigned calldata.
- agentId on POST /v1/users/me/agents must exist in the catalog. An arbitrary id is refused with UNKNOWN_AGENT, so byo-external is the slot for an agent Ava does not host.
- Agent records are owner-only until the owner changes visibility, and a stranger's read is answered identically to a record that does not exist.
Ready to run it? The install guide makes every call on this page from your browser.