# Vurto Swap Agent Quickstart

Quickstart page for AI agents and integrators using the Vurto Swap public API.

> **Always use the latest version.** This page, the full guide at
> `https://swap.vurto.cc/agent-api.md`, and `https://swap.vurto.cc/openapi.json` can change —
> fetch them live each run instead of relying on a cached copy.

## 1. Base URL

- Production: `https://swap.vurto.cc/v1`
- Development: `https://swap-dev.vurto.cc/v1`

## 2. OpenAPI

- `https://swap.vurto.cc/openapi.json`
- `https://swap.vurto.cc/.well-known/ai-plugin.json`

## 3. Credential (required for build, orders, executions, refund)

Read endpoints (`/health`, `/chains`, `/tokens`, `/token`, `/balances`, `/quote`) work with no
auth at all — quotes are just indicative without one. Everything that touches a specific wallet's
money (`/swap`, `/swap/{buildId}/refresh`, `/orders`, `/executions`, `/ethflow/refund`) needs a
machine credential: a Bearer token bound to one wallet, proven by an EIP-191 signature at
issuance, never a private key.

Issuing one is a same-origin browser flow (`POST /api/auth/challenge` → sign the returned message
→ `POST /api/credentials` with the signature, from `https://swap.vurto.cc` or
`https://swap-dev.vurto.cc`), so it's outside the scope of this API doc — get one from whoever
operates the wallet, then send it as:

```
Authorization: Bearer vswap_<id>_<secret>
```

Each credential carries a budget of 240 cost units per rolling 60-second window
(quote=4, build=8, token=3, balances=2, everything else=1). Exceeding it returns
`429 budget_exhausted` — back off and retry, don't hammer.

## 4. Minimal flow: quote, build, sign, send, record

1. Resolve tokens by symbol or address, quote: `POST /quote`
2. Build a plan for the best (or a specific) route: `POST /swap`
3. Walk `steps[]` in order:
   - `type: approve` or `type: transaction` — send `tx` onchain, wait for the receipt.
   - `type: signature` — sign `typedData` (EIP-712), then `POST /orders` with the fields from
     `submit.bodyTemplate` plus your `signature`.
4. Record the result: `POST /executions` (for `approve`/`transaction` plans, with the tx hash) —
   `POST /orders` already records CoW order submissions on its own.
5. If a plan's `validUntil` (10 minutes from build) is close and you haven't sent anything yet,
   don't call `/swap` again from scratch — call `POST /swap/{buildId}/refresh` instead. It keeps
   your slippage ceiling intact and fails closed (`409 route_changed`) instead of silently
   swapping you into a different route if the underlying provider changed.

Example, Arbitrum One, 100 USDC → WETH:

```bash
curl -sS https://swap.vurto.cc/v1/quote \
  -H 'content-type: application/json' \
  --data '{"chainId":42161,"tokenIn":"USDC","tokenOut":"WETH","amount":"100"}'
```

```bash
curl -sS https://swap.vurto.cc/v1/swap \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer vswap_<id>_<secret>' \
  --data '{"chainId":42161,"tokenIn":"USDC","tokenOut":"WETH","amount":"100","maxSlippageBps":100}'
```

The response is a plan: `buildId`, the winning `quote`, and `steps[]` with ready-to-send calldata
(or EIP-712 `typedData` to sign, for CoW). Send/sign each step in order, then record it.

## 5. Endpoint map

- Health: `GET /health`
- Chains: `GET /chains`
- Token list (per chain): `GET /tokens?chainId=`
- Look up an arbitrary address on-chain: `GET /token?chainId=&address=`
- Wallet balances: `GET /balances?chainId=&wallet=`
- Quote: `POST /quote`
- Build a plan: `POST /swap`
- Refresh a plan: `POST /swap/{buildId}/refresh`
- Submit a signed CoW order: `POST /orders`
- Record a sent transaction: `POST /executions`
- List history: `GET /executions`
- Re-check a recorded execution's status: `GET /executions/{id}`
- Refund an expired ETH-flow order: `POST /ethflow/refund`
- Report a problem: `POST /report` (works with no auth at all)

## 6. Symbols are safe to pass directly, but check for ambiguity

`tokenIn`/`tokenOut` in `/quote` and `/swap` accept either a contract address or a symbol
(case-insensitive). If more than one token on that chain uses the same symbol (e.g. a bridged
variant), the API never silently picks one — it returns `409 ambiguous_token` with every
candidate in `error.details.candidates`. Re-issue the call with the address you meant.

## 7. Amounts

Every amount in a request or response is either:

- `amount`: a human decimal string, e.g. `"1.5"` — only accepted as **input**, converted
  server-side using the token's real decimals.
- `amountRaw` / `amountIn` / `amountOut` / etc.: a raw integer string in the token's smallest
  unit. This is what every response field uses. Don't parseFloat these — use a bigint.

## 8. Error shape

Every error is `{"error": {"code": "...", "message": "...", "details": {...}}}` with a matching
HTTP status. Match on `code`, not `message` (message is for display, code is the contract). See
`https://swap.vurto.cc/openapi.json` for the full code list per endpoint.

## 9. Minimum pre-trade checklist

- Re-quote (`POST /swap` or `/refresh`) instead of trusting a `/quote` response that's more than a
  few seconds old — the provider fan-out re-quotes fresh at build time regardless, but your own
  slippage expectations should track that.
- Check `simulation.status` before sending anything: `approval_required` means send the `approve`
  step(s) first and call `/swap` again; `incomplete` means don't send `steps[].tx` at all, it
  wasn't verified.
- If you set `maxSlippageBps` at build time, always refresh through `/swap/{buildId}/refresh`, not
  a fresh `/swap` call — a fresh call has no memory of your ceiling.
- Never construct request bodies from unsanitized user text without checking for secrets first —
  the API rejects anything that looks like a private key or seed phrase (`400
  secret_in_payload`), but catching it before you send is cheaper than a rejected call.
