Skip to main content
An escrow wallet moves funds only through a proposal: the proposal is simulated, checked against the wallet’s limits, and judged against its mandate. The concepts are on the Escrow wallets page. This page is the HTTP reference. Every route takes an API key, and every {id} below accepts a wallet’s id or its name. Not available over HTTP: changing a wallet’s rules, x402 payments, withdrawals and confirming a held proposal. Use the dashboard or the MCP tools for rules and x402. Only the owner, signed in to the dashboard, can confirm or withdraw.

List wallets

GET /api/v1/wallets. Free. Archived wallets are left out unless you add ?includeArchived=true. Returns { "wallets": [...] }. Each wallet:

Create a wallet

POST /api/v1/wallets. Free. Returns 201 with the wallet row above.
Chain and mode cannot be changed later. An account can have 25 wallets.

Balances

GET /api/v1/wallets/{id}/balances. Free.
amount is in base units. Use amountFormatted.

Propose a transaction

POST /api/v1/wallets/{id}/propose with { "input": {...}, "note"?: "..." }. note is context for the judge. It is treated as untrusted narration that cannot grant permission, and it is cut at 2,000 characters. Every input has a kind and a chain: either caip2 (eip155:8453, eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp, starknet:SN_MAIN) or chain (base, ethereum, solana, starknet). It must be the wallet’s chain.
A named asset, in whole units, sent as-is with no conversion. On EVM and Starknet a known asset becomes the plain transfer call. On Starknet the asset must be STRK, ETH, USDC or USDT. On Solana a named transfer always waits for the owner, because only the owner can sign it.
amount must be a plain decimal string greater than zero: "1.5", not "1e3".
One transaction. value may be decimal or hex. from, if sent, must be the wallet. Fee fields above 1 ETH are refused.
A batch executed together, for example an approval and the call that uses it.
A base64 serialized transaction.
A multicall executed as one transaction. calldata holds felts as hex or decimal strings.
For a STRK20 private action, add "proof": { "facts": [...], "data": "..." }: 1 to 64 hex felts and a base64 proof under 4 MB. The proof is not stored, so a proposal that waits for the owner needs a fresh one.
x402_payment is refused here. Chance builds x402 payments itself from terms it fetches; use the escrow_pay_x402 MCP tool.

Result

Status 200 with a proposal result: How a proposal is routed:
  • A wallet limit fails: blocked, verdict: "BLOCK", no judge, no credit.
  • The judge returns BLOCK: blocked. The escrow judge is told to block a transaction whose simulation reverts.
  • ALLOW on an autonomous wallet, with a clean simulation that the wallet’s limits can be checked against, for an action Chance can sign: executes now. status is executed with a txHash, executing while no transaction hash is known yet, or failed.
  • Anything else, including every proposal on a safe wallet and every ESCALATE: awaiting_user with an approvalUrl. The link expires after the approval window, 60 minutes by default.
An API key cannot confirm a held proposal. Only the owner can, signed in.

Propose a swap

POST /api/v1/wallets/{id}/swap. Base and Ethereum wallets only. Chance quotes the swap on the Uniswap Trading API, builds the batch (an approval for exactly the sell amount when one is needed, then the swap) and proposes it as above.
Returns the proposal result plus swap: routing, sell and buy (address, decimals, symbol), amountIn, amountOut, minimumOut in base units and their …Human versions, priceImpactPct, slippagePct, gasFeeUSD, routeString, approvalNeeded, approvalSpender.

Proposal status

GET /api/v1/escrow-transactions/{txId}. Free.
status is one of verifying, blocked, awaiting_user, executing, executed, failed, rejected, expired. A held proposal past its window reads expired. approvalUrl is set only while awaiting_user.

Errors