---
title: "Unsigned transaction on any chain: Chord first, a fallback only when it fails"
method: POST
path: "/meta/swap"
tags: ["Meta aggregator"]
---

# Unsigned transaction on any chain: Chord first, a fallback only when it fails

`POST /meta/swap`

## Request

- Base URL: `https://api.getchord.xyz/v1`
- URL: `https://api.getchord.xyz/v1/meta/swap`
- Auth: none declared

The same answer as `/meta/quote` with a `taker` required: an UNSIGNED transaction, simulated as the taker. Chord never signs and never holds funds.

Send the pair and amount, or the `quoteId` of a `/meta/quote` answer from the last 2 minutes. The policy runs again at the current state, so `provider` can differ from the quote's: compare before you sign.

**Before signing**
- `approval` not null: send `token.approve(spender, amount)` first. `spender` is `tx.to`. With `approval.resetFirst: true` (USDT on Ethereum, a left-over allowance) send `approve(spender, 0)` before it.
- When `provider` is not `chord`, `tx.to` is that aggregator's router. Show the user which provider it is.

**Policy**
1. Chord's own router is always asked first, with its normal budget.
2. A fallback is asked only when Chord fails: the engine timed out, errored or is down; there is no route; Chord refuses the route itself (value guard, taxed token, stale bonding-curve route); the chain's router is not live; or the built transaction fails simulation. A better price elsewhere is not a reason. A user error (unknown token, bad amount) never falls back.
3. Fallbacks are tried in the chain's order (`GET /meta/providers`); the first answer that passes every check wins. Per provider 4 s, whole request 9 s, a circuit breaker per provider and chain.

**Safety, on every fallback answer**
- The transaction target and the approval spender must be on Chord's allowlist for that provider and chain, and deployed there.
- `minAmountOut` comes from your slippage; a provider minimum below it is refused.
- The value guard of `/quote` applies: an output worth 30 % less than the input at Chord's prices is refused. An unpriced token gets a security scan (`safety`); a honeypot is refused.
- With `taker`, the transaction is simulated as that wallet and must deliver at least `minAmountOut`. A fallback transaction that cannot be simulated is never returned.
- Without `taker`, a fallback price is shown only after the provider BUILT the route and it was simulated for a placeholder wallet (`PRICE_CHECKED`); a provider that prices a size but will not build it is skipped. The placeholder's transaction is never returned.
- An input the simulation cannot supply (a balance that is not a plain storage mapping, e.g. rebasing shares) while the taker does not hold it: HTTP 422 `INPUT_NOT_SIMULATABLE` — hold the input and ask again.

## Request body

- object
  - `chain` string: Chain id or key.
  - `tokenIn` string: EVM address, hex, case-insensitive.
  - `tokenOut` string: EVM address, hex, case-insensitive.
  - `amount` string: Amount in the token's smallest unit, as a decimal string. Never a float.
  - `slippageBps` integer
  - `taker` string, required: The wallet that signs and receives.
  - `quoteId` string: Instead of chain / tokens / amount: the id of a `/meta/quote` answer (valid 2 minutes).

## Response `200`

Sign `tx` with any wallet library and submit it to the chain in `tx.chainId`.

- MetaQuote: The same shape whichever provider answered.
  - `chain` integer: Chain id.
  - `tokenIn` string: EVM address, hex, case-insensitive.
  - `tokenOut` string: EVM address, hex, case-insensitive.
  - `amountIn` string: Amount in the token's smallest unit, as a decimal string. Never a float.
  - `taker` string, nullable: The wallet the transaction was built and simulated for; null on a price-only quote.
  - `slippageBps` integer: The slippage `minAmountOut` reflects.
  - `provider` string, nullable: Who answered: `chord` (Chord's own router), `kyberswap`, `monorail`, `lifi`, `relay`. `null` = no provider has a route.
  - `fallbackReason` string, nullable: `null` when Chord answered. Otherwise why it did not: `no_route`, `insufficient_liquidity`, `engine_error`, `engine_timeout`, `engine_down`, `value_guard`, `taxed_token`, `route_stale`, `chain_not_live`, `build_failed`, `simulation_failed`.
  - `attempts` MetaAttempt[]: Every provider asked, in order.
    - `provider` string: `chord`, `kyberswap`, `monorail`, `lifi` or `relay`.
    - `status` string: `ok`, or why this provider did not produce the answer: `no_route`, `timeout`, `http_error`, `rate_limited`, `invalid_response`, `not_allowlisted`, `calldata_mismatch`, `min_out_too_low`, `value_gap`, `unsafe_token`, `simulation_failed`, `unverified`, `breaker_open`, `skipped`; for Chord also `engine_error`, `engine_timeout`, `engine_down`, `route_refused`, `build_failed`, `chain_not_live`.
    - `ms` integer: Time spent on this provider.
    - `error` string, nullable: Short reason from a fixed vocabulary, never provider text.
  - `amountOut` string: What the taker receives after every fee, smallest unit of `tokenOut`. (`/quote`'s `outputAmount` is before Chord's fee; this is after.)
  - `minAmountOut` string: The minimum the transaction enforces, from the slippage you asked for.
  - `priceImpactPct` string, nullable: Percent, when the provider states one.
  - `valueLossPct` number, nullable: Output value against input value at Chord's own prices, percent (negative = worth more). `null` when Chord has no reliable price for a side: see `safety` and `warnings`.
  - `feeBps` integer: Chord path: Chord's protocol fee on the output. Fallback: the integrator fee Chord added (0 today); the provider's own fees are already inside `amountOut`.
  - `feeRecipient` string, nullable: EVM address, hex, case-insensitive.
  - `route` object
    - `venues` string[]: Venue names.
  - `tx` object, nullable: The UNSIGNED transaction. `null` without `taker`. `to` is the Chord router when `provider` is `chord`, the provider's own router otherwise.
    - `to` string: EVM address, hex, case-insensitive.
    - `data` string: Calldata.
    - `value` string: Amount in the token's smallest unit, as a decimal string. Never a float.
    - `gas` string, nullable: Gas limit to sign with.
    - `chainId` integer
  - `approval` object, nullable: The approval to send first. `null` for a native input or when the taker's allowance already covers the amount.
    - `token` string: EVM address, hex, case-insensitive.
    - `spender` string: EVM address, hex, case-insensitive.
    - `amount` string: Amount in the token's smallest unit, as a decimal string. Never a float.
    - `resetFirst` boolean: Present only for a token that reverts `approve(x > 0)` while its allowance is non-zero (USDT on Ethereum). `true` = the taker has a left-over allowance: send `approve(spender, 0)` first, then this approval.
  - `simulation` object, nullable: The transaction run as the taker (with the input supplied when the taker does not hold it), measuring the taker's own balance change. `null` without `taker`.
    - `ok` boolean, nullable: `true` = ran and delivered at least `minAmountOut`; `null` = no verdict (only ever returned on the Chord path).
    - `amountOut` string: Amount in the token's smallest unit, as a decimal string. Never a float.
    - `gasUsed` integer
    - `revert` string
    - `reason` string
  - `safety` object, nullable: A security scan of the unknown token, present when the value check could not run on a fallback answer.
    - `source` string: `goplus`
    - `token` string: EVM address, hex, case-insensitive.
    - `worstFlag` string: `none`, `low`, `medium`, `high`.
    - `honeypot` boolean, nullable
    - `buyTaxPct` number, nullable
    - `sellTaxPct` number, nullable
    - `risks` string[]
  - `warnings` string[]: `INSUFFICIENT_BALANCE`, `UNPRICED`, `OUTPUT_WRAPPED`, `NO_ROUTE`, and on a price-only fallback `PRICE_CHECKED` (built and simulated for a placeholder wallet) or `UNVERIFIED_PRICE` (built, not simulatable) — each followed by a sentence.
  - `quoteId` string: Pass to `POST /meta/swap` within 2 minutes.
  - `expiresAt` integer: Unix time, seconds, UTC.

## Other responses

- `400`: Invalid parameters — `error` names the check that failed (`INVALID_PARAMS`, `BAD_ADDRESS`, `BAD_LIMIT`, `BAD_HOURS`, …).
- `404`: No provider has a route for this pair and size (`NO_ROUTE`); the body carries `fallbackReason` and `attempts`.
- `410`: Unknown or expired `quoteId` (`QUOTE_EXPIRED`). Send the full params.
- `422`: The request is valid but cannot be served as asked (`INPUT_NOT_SIMULATABLE` on `/meta/*`: a route exists, but the input token cannot be supplied in a simulation and the taker does not hold it — hold the input and ask again).
- `429`: Over the per-minute limit (`RATE_LIMIT_EXCEEDED`). Read `X-RateLimit-Reset` and back off.
- `503`: The lane behind this endpoint is not ready or not enabled here (`NOT_READY`, `CHAIN_NOT_ENABLED`, `NOT_ENABLED`, `*_UNAVAILABLE`). Retry later; do not treat it as an empty answer.

---

[API](https://skmtc.dev/getchord/apis/chord-api.md) · [All operations](https://skmtc.dev/getchord/apis/chord-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getchord/apis/chord-api/revisions/d3bc4640fea0?raw)
