> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trustware.io/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /route

> Generate the transaction payload for signing. Creates a Trustware intent record that can be tracked through to completion.

`POST https://api.trustware.io/api/v1/routes/route`

Builds the full transaction payload for signing and broadcasting. Call this after the user confirms the quote. Trustware creates an intent record internally so the transaction can be tracked from receipt submission through final settlement.

Pass a `metadata` object with any fields you need echoed back in all subsequent status responses, such as user ID, withdrawal ID, fiat destination, or any other reference data.

## Request

```json theme={null}
{
  "fromChain": "43114",
  "toChain": "137",
  "fromToken": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
  "toToken": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
  "fromAmount": "215124226346059100",
  "fromAmountUSD": "2",
  "fromAddress": "0xeb212fe20f26243ec4d4df2dd49c8aa9fa75a201",
  "toAddress": "0xeb212fe20f26243ec4d4df2dd49c8aa9fa75a201",
  "slippage": 1,
  "slippageBps": 100,
  "metadata": {
    "userId": "usr_abc123",
    "withdrawalId": "wdl_xyz789"
  }
}
```

| Field | Required | Description |
| - | - | - |
| `fromChain` | Yes | Source chain ID as a string. |
| `toChain` | Yes | Destination chain ID as a string (e.g. `"8453"` for Base, `"cataclysm-1"` for Nibiru). |
| `fromToken` | Yes | Source token identifier: contract address on EVM, mint address on Solana, native denom on Cosmos. Use `"0xeeee...eeee"` for the native token on EVM chains. |
| `toToken` | Yes | Destination token identifier: contract address on EVM, mint address on Solana, native denom on Cosmos (e.g. `"unibi"`). |
| `fromAmount` | Yes | Amount in the smallest unit of the source token. |
| `fromAmountUSD` | No | USD equivalent of `fromAmount`. Used for display and analytics. |
| `fromAddress` | Yes | Sender wallet address. |
| `toAddress` | Yes | Recipient wallet address. |
| `slippage` | No | Slippage tolerance as a percentage. Defaults to `1`. |
| `slippageBps` | No | Slippage in basis points. Equivalent to `slippage * 100`. |
| `metadata` | No | Arbitrary key-value object echoed in all status responses. Use for user attribution. |
| `hooks` | No | Optional destination call. `hooks.postHook` executes a contract call on the destination chain once funds arrive. See [destination calls](#destination-calls-with-hooks-posthook) and the [vault destinations guide](/guides/vault-destinations). Omit `hooks` for a normal route. |

## Destination calls with hooks.postHook

Pass `hooks.postHook` to execute a contract call on the destination chain once the routed funds arrive, instead of paying the destination token to a recipient address. Destination calls are EVM only and provider dependent.

This example routes the native asset on Avalanche into a contract on Base, funding the call with the destination native asset. `callData` is the ABI encoding of `depositNativeFor(address recipient)` for the recipient in `toAddress`.

```json theme={null}
{
  "fromChain": "43114",
  "toChain": "8453",
  "fromToken": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
  "toToken": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
  "fromAmount": "900000000000000000",
  "fromAddress": "0xeb212fe20f26243ec4d4df2dd49c8aa9fa75a201",
  "toAddress": "0xeb212fe20f26243ec4d4df2dd49c8aa9fa75a201",
  "hooks": {
    "postHook": {
      "target": "0xYourVaultAddress",
      "callData": "0xYourEncodedCallData",
      "value": "2000000000000000",
      "fundToken": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
      "fundAmount": "2000000000000000",
      "estimatedGas": "150000"
    }
  }
}
```

| Field | Required | Description |
| - | - | - |
| `target` | Yes | Contract address to call on the destination chain. Must be a valid EVM address. |
| `callData` | Yes | ABI-encoded calldata for the call. |
| `value` | Native-value calls only | Native value to send with the call, in destination base units. |
| `fundToken` | No | Token the call acts on. Defaults to the route's `toToken`. |
| `fundAmount` | Yes unless `fullAmount` is set | Predetermined destination-token amount the call is funded with, in destination base units. |
| `estimatedGas` | Recommended | Gas limit hint for the destination call. Some providers require it, so omitting it narrows which providers can serve the request. |
| `toApprovalAddress` | ERC-20 destination calls | Address allowed to pull `fundToken` when the destination contract uses `transferFrom`, normally the contract itself. Must not be set for native-value calls. How the allowance is granted depends on the resolving provider: some prepend an explicit `approve()` call, others pass the address to their own execution engine. |
| `fullAmount` | No | **Provider dependent.** Patch the amount argument in `callData` rather than using a predetermined `fundAmount`. Requires `amountInputPos`. See [amount patching](/guides/destination-call-options#dynamic-landed-balance-mode). |
| `amountInputPos` | With `fullAmount` | **Provider dependent**, and required whenever `fullAmount` is set. The zero-based index of the ABI argument to patch, which must be a static type such as `uint256`. Index `0` is valid. |
| `toFallbackAddress` | No | **Provider dependent.** Recipient if the destination call fails, when the resolving provider implements a fallback. Not every provider does, so this is not a guarantee. |
| `description` | No | Optional free-text label for the call. Not forwarded by every provider, so do not depend on it appearing downstream. |

Requests are rejected before routing when `target` or `callData` is missing, when `fullAmount` is `true` without `amountInputPos`, or when neither `fundAmount` nor `fullAmount` is supplied. A posthook the API rejects returns `400` with a message describing the problem.

`hooks.postHook` is also accepted on `POST /api/v1/routes/deposit-address`. Deposit-address routing is provider dependent, and so are deposit-address routes with a posthook, so that path is not available on every route.

<Note>
  The route status reflects the provider's aggregate result. There is no separate
  field reporting the destination call on its own, so verify destination state by
  reading your own contract.
</Note>

## Response

```json theme={null}
{
  "data": {
    "intentId": "32c423a8-0531-453e-80b6-c318625cba4d",
    "route": {
      "estimate": {
        "fromAmount": "215124226346059100",
        "toAmount": "1994795",
        "toAmountMin": "1974049",
        "toAmountUsd": "1.99",
        "totalFeesUsd": "0.050017",
        "fees": [...]
      },
      "execution": {
        "transaction": {
          "to": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666",
          "data": "0x84…ed56",
          "value": "219426580165239030",
          "gasLimit": "682400",
          "gasPrice": "12900585",
          "maxFeePerGas": "26170938",
          "maxPriorityFeePerGas": "1000",
          "type": "ON_CHAIN_EXECUTION"
        }
      },
      "provider": "squid",
      "requestId": "b17a041c3f5633e3d46e9eb44041ed56",
      "reliabilityScore": 1
    }
  }
}
```

| Field | Description |
| - | - |
| `intentId` | Unique identifier for this intent. Pass this to `/receipt` and `/status`. |
| `route.estimate` | Fee and amount estimates (same structure as the `/quote` response). |
| `route.execution.transaction` | The transaction object to sign and broadcast. Pass this to your signing infrastructure. |
| `route.execution.transaction.to` | The provider contract this transaction is sent to. Changes when the selected provider changes between quotes. This is not necessarily the ERC-20 spender; see [Token approvals](#token-approvals). |
| `route.execution.transaction.data` | Encoded calldata for the route transaction. |
| `route.execution.transaction.value` | Native token value to send with the transaction (in wei). |
| `route.execution.approvals` | Present when the route requires ERC-20 allowances before the route transaction can succeed. Each entry is authoritative for one allowance. |
| `route.execution.approvals[].tokenAddress` | The ERC-20 token to approve. |
| `route.execution.approvals[].spender` | The address to approve the token for. Always a provider contract, never a Trustware contract. |
| `route.execution.approvals[].amount` | The exact amount to approve, in the token's base units. |
| `route.execution.approvals[].chainId` | The chain the approval belongs on. |
| `route.provider` | The routing provider selected for this route, as a runtime value such as `"squid"` or `"lifi"`. |
| `route.requestId` | The routing provider's own request ID, not Trustware's. It is also returned in the `X-Provider-Request-Id` header and reaches the status payload as `provider_request_id`. Trustware's own ID for the call is in `X-Request-Id`. |
| `route.reliabilityScore` | Provider reliability score for this route, from 0 to 1. |

Every field on an approval entry is optional, so skip any entry that does not carry a token, a spender, and a non-zero amount.

## Error responses

When no provider can build the route, the endpoint returns a judged result rather than one opaque message. A top-level `code` gives the verdict for the request as a whole, and `providers` carries one entry per provider consulted saying why it is out.

| Status | `code` | Meaning |
| - | - | - |
| `404` | `no_route_available` | No provider could route the request. The pair, the amount or the liquidity is the problem, so the same inputs will not route on retry. |
| `502` | `providers_failed` | At least one provider did not answer properly, so the request cannot be called unroutable. The pair may still route; retry after a moment. |

```json theme={null}
{
  "error": "no route available for this pair",
  "code": "no_route_available",
  "providers": [
    {
      "name": "squid",
      "outcome": "declined",
      "code": "amount_too_low",
      "message": "amount below provider minimum"
    },
    {
      "name": "relay",
      "outcome": "declined",
      "code": "no_routes",
      "message": "provider returned no routes"
    }
  ]
}
```

| Field | Description |
| - | - |
| `error` | A short human summary. Its exact wording is not stable, so branch on `code` and `providers` instead of parsing it. |
| `code` | The verdict for the request: `no_route_available` or `providers_failed`. |
| `providers[].name` | The provider, as a runtime value. |
| `providers[].outcome` | `declined` when the provider answered and cannot serve the request, `failed` when it did not answer properly. Treat any other value as a failure. |
| `providers[].code` | A decline code or a failure code, from the tables below. These are stable strings; branch on them. |
| `providers[].message` | A one-line reason, sanitized. Raw provider payloads are deliberately not returned; they stay in the server logs against `X-Request-Id`. |

A provider missing from `providers` was not consulted, so absence is not a decline.

### Decline codes

Returned with `outcome: "declined"`. The provider answered and said no, which is a negative result rather than a fault.

| Code | Meaning |
| - | - |
| `chain_unsupported` | The provider does not serve one of the two chains. |
| `pair_unsupported` | The provider does not serve this token pair. |
| `action_unsupported` | The provider cannot perform the requested action, such as a destination posthook. |
| `no_routes` | The provider found no path for this pair and amount. |
| `amount_too_low` | The amount is below the provider's minimum. |
| `insufficient_liquidity` | Not enough liquidity for this amount. |
| `destination_call_failed` | The provider simulated your `hooks.postHook` and it reverted. Check `callData`, the selector, and whether `target` can accept the transfer. This is about your call, not about liquidity. |
| `route_unsupported` | The provider declined for a reason it did not classify further. |

### Failure codes

Returned with `outcome: "failed"`. The provider did not produce a usable answer.

| Code | Meaning |
| - | - |
| `timeout` | The provider did not respond in time. |
| `provider_error` | The provider returned an error. |
| `not_configured` | The provider is not configured on this deployment. |

Because these are faults rather than refusals, a response containing one arrives as `502`, and the pair may still be routable on a later attempt.

<Note>
  Through `@trustware/sdk` 1.1.13 the SDK throws this as a plain `Error`
  carrying only the `error` summary, so `code` and `providers` are available to
  direct API callers rather than through the SDK. See [handling
  errors](/events-errors/error-handling).
</Note>

## Token approvals

If the source token is an ERC-20, the required allowances must be granted before the route transaction is broadcast. The spender is always a provider contract, never a Trustware contract.

When `route.execution.approvals` is present, each entry is the authoritative source for one allowance: approve `amount` of `tokenAddress` for `spender` on `chainId`. Grant each one, wait for it to confirm, then broadcast `route.execution.transaction`.

An ERC-20 source route returns them inside `route.execution`:

```json theme={null}
{
  "data": {
    "route": {
      "execution": {
        "approvals": [
          {
            "chainId": "43114",
            "tokenAddress": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
            "spender": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666",
            "amount": "2000000"
          }
        ]
      }
    }
  }
}
```

<Warning>
  Do not assume the spender is `route.execution.transaction.to`. It often is, but
  a route can require an allowance for a different address, and it can require
  more than one. When `route.execution.approvals` is present, use it.
</Warning>

Re-quoting can change the selected provider, which changes the required allowances. Rebuild the route and re-read `approvals` rather than reusing an earlier response. You can also check a specific allowance with [`GET /allowance`](/api-reference/allowance) before broadcasting.

This is a source-chain allowance and is unrelated to `hooks.postHook.toApprovalAddress`, which controls a destination-chain allowance for a destination contract call.

The `@trustware/sdk` headless core handles the source allowances automatically on EVM routes: `Trustware.sendRouteTransaction()` reads the current allowance, submits an approval for the exact amount when one is missing, waits for confirmation, and then sends the route transaction.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.