# Introduction

ZERT is cross-chain swap infrastructure for wallets, exchanges, neobanks, and dApps. One integration gives your product access to 70+ chains, 900+ DEXs, and 6M+ liquidity pools through ZERT's routing and execution infrastructure.

ZERT scans live liquidity, ranks routes by real-world execution quality, stress-tests under simulated conditions, and executes privately — no MEV exposure, no front-running. Gas is covered by ZERT. Your users pay nothing extra.

ZERT is fully non-custodial. No KYC. No user accounts.

### Base URL

```
https://api.gateway.zert.com/api/v1
```

All public endpoints live under `/api/v1`. Authentication uses `Authorization: Bearer YOUR_API_KEY` on every endpoint except `GET /health`.

### Public Flow

The public swap flow is `Quote -> Build -> Sign -> Submit`:

1. `POST /quote`
2. `POST /swap/build`
3. `GET /swap/{swapId}/signing` until a signable payload is ready
4. Sign externally in the wallet or signer you control
5. `POST /swap/{swapId}/signing` with the signed artifact

Postman and GitBook help you inspect payloads and submit signed artifacts. They do not sign transactions.

### What's Next

| If you want to...                            | Go to                                                                                                     |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Get your API key and start calling endpoints | [Authentication & API Keys](/getting-started/authentication-and-api-keys)                                 |
| Do your first swap in 5 minutes              | [Quick Start — Your First Swap](/getting-started/quick-start-your-first-swap)                             |
| Walk through the full swap lifecycle         | [Guides → Full swap flow](/integration-example/full-swap-flow)                                            |
| Discover chains, tokens, and pairs           | [Guides → Discovery: chains, tokens, and pairs](/integration-example/discovery-and-quote)                 |
| Set up limit orders                          | [Guides → Limit orders](/integration-example/limit-orders)                                                |
| Track a transaction                          | [Guides → Check transaction status](/integration-example/check-transaction-status)                        |
| Explore all endpoints                        | [Zert API Docs](https://api.gateway.zert.com/docs)                                                        |
| See all data types                           | [ZERT APIs → Models](/models)                                                                             |
| Integrate with TypeScript                    | [Routing Engine SDK → Installation and Configuration](/routing-engine-sdk/installation-and-configuration) |
| Handle SDK and API errors                    | [Routing Engine SDK → Error Handling for APIs](/routing-engine-sdk/error-handling-for-apis)               |


# Authentication & API Keys

All endpoints except `GET /health` require an API key.

### Headers

Use bearer auth by default on every public endpoint:

```
Authorization: Bearer YOUR_API_KEY
```

### Example Request

```bash
curl https://api.gateway.zert.com/api/v1/chains \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Getting an API Key

API keys can be generated and managed directly from the Developer Dashboard - <https://dashboard.zert.com/>

Or [contact us](https://www.zert.com/contact-us) to request API access.

### Unauthenticated Endpoint

The health check does not require authentication:

```bash
curl https://api.gateway.zert.com/api/v1/health
```

Returns service status and uptime. Useful for monitoring.


# Quick Start - Your First Swap

Quote -> Build -> Sign -> Submit in four steps. Takes under 5 minutes if you have an API key and a signer.

### Step 1: Get a Quote

```bash
curl -X POST https://api.gateway.zert.com/api/v1/quote \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fromChain": "solana",
    "toChain": "avax",
    "tokenIn": "SOL",
    "tokenOut": "WETH",
    "network": "mainnet",
    "amount": "100.00",
    "slippageBps": 75,
    "recipient": "0x1234567890123456789012345678901234567890"
  }'
```

The response includes a `routes` array. Each route has:

* `routeId` — pass this to Step 2
* `expectedAmountOut` — projected output for that route

### Step 2: Build the Signing Step

Swagger does not auto-fill `quoteId` and `routeId` from Step 1. Paste the fresh values from the previous quote response when testing in the docs UI.

```bash
curl -X POST https://api.gateway.zert.com/api/v1/swap/build \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteId": "REPLACE_WITH_QUOTE_ID_FROM_POST_QUOTE",
    "routeId": "REPLACE_WITH_ROUTE_ID_FROM_SELECTED_ROUTE",
    "recipient": "0x1234567890123456789012345678901234567890",
    "waitForTxRequestMs": 300
  }'
```

Leave `Idempotency-Key` blank for one-off Swagger or curl calls. If you need retry-safe dedupe on direct HTTP calls, send your own UUIDv4 or ULID only when retrying the exact same payload. SDK and Postman flows can generate one automatically.

The response includes:

* `swapId` — use this for the rest of the flow
* `mode` — `sync` if the signable payload is ready now, `async` if you should poll
* `signing` — the signable payload when `mode` is `sync`

If the signing surface for the active step is not configured, the API returns `503` instead of a placeholder transaction payload.

### Step 3: Poll Until a Signable Payload Is Ready

If Step 2 returns `mode: "async"`, poll:

```bash
curl "https://api.gateway.zert.com/api/v1/swap/your-swap-id/signing" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

When the response returns `mode: "sync"`, hand the payload to the external wallet or signer. Postman does not sign transactions.

### Step 4: Submit the Signed Artifact

```bash
curl -X POST "https://api.gateway.zert.com/api/v1/swap/your-swap-id/signing" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rawTx": "0x_signed_tx_from_external_wallet"
  }'
```

If the execution has more than one signing step, keep polling the same endpoint until the next payload is ready.

### Track On-Chain Status (Optional)

```bash
curl "https://api.gateway.zert.com/api/v1/status/transaction/0x123...abc?chainRef=polygon-mainnet" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

| Status      | Meaning                      |
| ----------- | ---------------------------- |
| `pending`   | Submitted, not yet confirmed |
| `confirmed` | Confirmed on chain           |
| `failed`    | Transaction failed           |

### What's Next

| Next step                                              | Go to                                                                                     |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| Understand the full swap lifecycle in detail           | [Guides → Full swap flow](/integration-example/full-swap-flow)                            |
| Discover which chains, tokens, and pairs are available | [Guides → Discovery: chains, tokens, and pairs](/integration-example/discovery-and-quote) |
| Set up limit orders                                    | [Guides → Limit orders](/integration-example/limit-orders)                                |


# Support

For integration help, technical questions, or account issues, contact the ZERT team directly.

**Email:** <support@zert.com>

We typically respond within one business day.


# Discovery

Use the discovery endpoints to fetch supported chains, tokens, and tradeable pairs before requesting a quote.

The public discovery surface is:

* `GET /api/v1/chains`
* `GET /api/v1/tokens`
* `GET /api/v1/pairs`

Use `id` values from `/chains` when building quote requests. Token and pair responses are discovery helpers for search, validation, and UI selectors.


# Get Supported Chains

Returns a list of all enabled chains/networks with their IDs and names. Use this to discover supported chains for the routing engine.

**Endpoint:** `GET /api/v1/chains`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`). For an unauthenticated health check, see [Health Check](/health-check).

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/chains' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "chains": [
      {
        "id": "eth-mainnet",
        "name": "Ethereum Mainnet"
      },
      {
        "id": "polygon-mainnet",
        "name": "Polygon"
      }
    ]
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Responses

| Code | Description                               |
| ---- | ----------------------------------------- |
| 200  | List of available chains                  |
| 401  | Unauthorized (missing or invalid API key) |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Chains
{% endembed %}


# Get Available Tokens

Returns supported tokens across all chains or filtered by chain. Use this to discover tokens available for swaps.

**Endpoint:** `GET /api/v1/tokens`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Query parameters

| Parameter       | Type    | Required | Description                                       | Example             |
| --------------- | ------- | -------- | ------------------------------------------------- | ------------------- |
| `chainRef`      | string  | No       | Filter by chain                                   | `eth-mainnet`       |
| `q`             | string  | No       | Search by symbol or name                          | `USDC`              |
| `limit`         | integer | No       | Max tokens to return (1–50000, default: 500)      | `50`                |
| `cursor`        | string  | No       | Opaque cursor for pagination                      | `next_cursor_value` |
| `supportedOnly` | string  | No       | `1` or `true` — only tokens supported for trading | `1`                 |
| `requireLogo`   | string  | No       | `1` or `true` — only tokens with a logo URL       | `true`              |
| `sort`          | string  | No       | `top`, `marketcap`, `trending`, or `asc`          | `top`               |
| `debug`         | string  | No       | `1` or `true` — include debug info in response    | `1`                 |

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/tokens?chainRef=eth-mainnet&limit=10' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "tokens": [
      {
        "chain": "eth-mainnet",
        "symbol": "USDC",
        "name": "USD Coin",
        "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "decimals": 6,
        "logoUrl": "https://cdn.example.com/usdc.png"
      }
    ],
    "nextCursor": null
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Token fields

| Field        | Type           | Description                          |
| ------------ | -------------- | ------------------------------------ |
| `chain`      | string         | Chain identifier for the token       |
| `symbol`     | string         | Canonical token symbol               |
| `name`       | string         | Human-readable token name            |
| `address`    | string         | EVM contract address when applicable |
| `decimals`   | integer        | Token decimals                       |
| `mint`       | string         | Solana mint address when applicable  |
| `logoUrl`    | string         | Token logo URL when available        |
| `nextCursor` | string or null | Cursor for the next page             |

## Responses

| Code | Description                               |
| ---- | ----------------------------------------- |
| 200  | List of tokens                            |
| 400  | Invalid request (e.g. invalid params)     |
| 401  | Unauthorized (missing or invalid API key) |
| 502  | Quote-service token discovery unavailable |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Tokens
{% endembed %}


# Get Trading Pairs

Returns tradeable token pairs. Pairs can be filtered by chain and token symbols.

**Endpoint:** `GET /api/v1/pairs`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Query parameters

| Parameter       | Type    | Required | Description                                                    | Example        |
| --------------- | ------- | -------- | -------------------------------------------------------------- | -------------- |
| `chainRef`      | string  | No       | Filter by chain. When set, only same-chain pairs are returned. | `eth-mainnet`  |
| `tokenIn`       | string  | No       | Filter by input token symbol                                   | `USDC`         |
| `tokenOut`      | string  | No       | Filter by output token symbol                                  | `USDT`         |
| `limit`         | integer | No       | Max pairs to return (1–10000, default: 1000)                   | `20`           |
| `cursor`        | string  | No       | Cursor for pagination (opaque)                                 | (opaque value) |
| `supportedOnly` | string  | No       | `1` or `true` — only pairs with supported tokens               | `1`            |
| `debug`         | string  | No       | `1` or `true` — include debug info in response                 | `1`            |

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/pairs?chainRef=eth-mainnet&tokenIn=USDC&limit=20' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "pairs": [
      {
        "fromChain": "eth-mainnet",
        "toChain": "eth-mainnet",
        "tokenIn": "USDC",
        "tokenOut": "USDT"
      }
    ],
    "nextCursor": null
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Pair fields

| Field        | Type           | Description                  |
| ------------ | -------------- | ---------------------------- |
| `fromChain`  | string         | Source chain identifier      |
| `toChain`    | string         | Destination chain identifier |
| `tokenIn`    | string         | Input token symbol           |
| `tokenOut`   | string         | Output token symbol          |
| `nextCursor` | string or null | Cursor for the next page     |

## Responses

| Code | Description                               |
| ---- | ----------------------------------------- |
| 200  | List of trading pairs                     |
| 400  | Invalid request                           |
| 401  | Unauthorized (missing or invalid API key) |
| 502  | Quote-service token discovery unavailable |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Pairs
{% endembed %}


# Swap

The public swap flow is `Quote -> Build -> Sign -> Submit`:

1. `POST /quote`
2. `POST /swap/build`
3. `GET /swap/{swapId}/signing` until a signable payload is ready
4. Sign externally in the wallet or signer you control
5. `POST /swap/{swapId}/signing` with the signed artifact

Postman and GitBook help you inspect payloads, but they do not sign transactions.


# Request Quote

Get routing quotes for swaps. The public response is intentionally slim: `quoteId`, `expiresAtMs`, and route summaries.

For same-chain routes, `provider` is a convenience alias when the route resolves to one public provider label. For cross-chain or other multi-step routes, prefer `providers`, `bridgeProvider`, and `stepCount`.

If no viable route exists for the requested pair, the endpoint still returns `200` with `routes: []`.

**Endpoint:** `POST /api/v1/quote`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Request body

| Field                  | Type    | Required | Description                     | Example                    |
| ---------------------- | ------- | -------- | ------------------------------- | -------------------------- |
| `fromChain`            | string  | Yes      | Source chain                    | `solana`                   |
| `toChain`              | string  | Yes      | Destination chain               | `avax`                     |
| `tokenIn`              | string  | Yes      | Input token symbol              | `SOL`                      |
| `tokenOut`             | string  | Yes      | Output token symbol             | `WETH`                     |
| `network`              | string  | No       | `mainnet` or `testnet`          | `mainnet`                  |
| `tokenRefFromOverride` | string  | No       | Canonical input token override  | `eip155:42161:erc20:0x...` |
| `tokenRefToOverride`   | string  | No       | Canonical output token override | `eip155:43114:erc20:0x...` |
| `amount`               | string  | Yes      | Human-readable amount           | `100.00`                   |
| `slippageBps`          | integer | No       | Slippage in basis points        | `75`                       |
| `recipient`            | string  | No       | Destination wallet address      | `0x1234...`                |

## Example

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/quote' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "fromChain": "solana",
    "toChain": "avax",
    "tokenIn": "SOL",
    "tokenOut": "WETH",
    "network": "mainnet",
    "amount": "100.00",
    "slippageBps": 75,
    "recipient": "0x1234567890123456789012345678901234567890"
  }'
```

### Example response

```json
{
  "data": {
    "quoteId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "expiresAtMs": 1776977796667,
    "routes": [
      {
        "routeId": "route_01hzy3m7v0xj7x4mqs79b0z9st",
        "providers": ["raydium", "circle-cctp", "traderjoe"],
        "bridgeProvider": "circle-cctp",
        "family": "cctp",
        "stepCount": 3,
        "expectedAmountOut": "4.17000742",
        "estimatedDurationSeconds": 180
      }
    ]
  },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-04-23T20:56:04.937Z"
  }
}
```

## Responses

| Code | Description                                                         |
| ---- | ------------------------------------------------------------------- |
| 200  | Quote completed                                                     |
| 400  | Invalid request                                                     |
| 401  | Unauthorized                                                        |
| 502  | Quote-service unavailable or returned a non-client upstream failure |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Quote
{% endembed %}


# Build Swap

Build the current signable step for a swap. The API persists the swap submission, publishes the execution intent, and returns either a ready-to-sign payload or an async polling handle. Use a fresh `quoteId` and `routeId` from `POST /quote`; stale quote bindings return `410 QUOTE_EXPIRED`. Swagger does not auto-fill those fields from the previous quote response, so you must paste the fresh values when testing in the docs UI. The gateway does not emit placeholder wallet payloads. If the execution surface for the active step is not configured, the endpoint returns `503`.

**Endpoint:** `POST /api/v1/swap/build`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Headers

| Header            | Required | Description                                                                                                                                                                                                                                                                                                             |
| ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | No       | Optional opaque dedupe token. If omitted, the gateway generates one automatically. Leave it blank for one-off Swagger or curl calls. Send your own UUIDv4 or ULID only when retrying the exact same payload and you need retry-safe dedupe. Allowed characters: `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `-`. Max 200 chars. |

## Request body

| Field                    | Type    | Required | Description                                              |
| ------------------------ | ------- | -------- | -------------------------------------------------------- |
| `quoteId`                | string  | Yes      | Quote ID from `POST /quote`                              |
| `routeId`                | string  | Yes      | Route ID from the selected quote route                   |
| `recipient`              | string  | No       | Destination wallet address                               |
| `userAddress`            | string  | No       | Optional wallet address used by the signing surface      |
| `permitType`             | string  | No       | `eip2612`, `permit2`, or `approve`                       |
| `permitDeadline`         | integer | No       | Permit deadline in ms                                    |
| `permitSignature`        | object  | No       | Expanded `v`, `r`, `s` permit signature                  |
| `permitSignatureCompact` | object  | No       | Compact `r`, `vs` permit signature                       |
| `permit2`                | object  | No       | Permit2 payload                                          |
| `waitForTxRequestMs`     | integer | No       | Bounded wait window before falling back to async polling |

## Example

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/swap/build' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{
    "quoteId": "REPLACE_WITH_QUOTE_ID_FROM_POST_QUOTE",
    "routeId": "REPLACE_WITH_ROUTE_ID_FROM_SELECTED_ROUTE",
    "recipient": "0x1234567890123456789012345678901234567890",
    "waitForTxRequestMs": 300
  }'
```

Leave `Idempotency-Key` blank for one-off Swagger or curl calls. Paste a fresh `quoteId` and `routeId` from the previous `POST /quote` response. If you need retry-safe dedupe, send your own key only when retrying the exact same payload. The SDK and Postman collection can generate one automatically.

### Example sync response

```json
{
  "data": {
    "swapId": "92714c81-38ec-4782-8624-0f56f526a9fd",
    "mode": "sync",
    "nextAction": "sign",
    "signing": {
      "step": { "current": 1, "total": 3 },
      "expiresAtMs": 1776977916667,
      "readyAtMs": 1776977796877,
      "clientHints": {
        "recommendedWallet": "phantom",
        "network": "solana",
        "action": "swap"
      },
      "payload": {
        "type": "solana",
        "transaction": "AQAAAA..."
      }
    }
  },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-04-23T20:56:04.937Z"
  }
}
```

### Example async response

```json
{
  "data": {
    "swapId": "8933a4cd-a08d-43b7-a63f-d7c1a53fdbe8",
    "mode": "async",
    "nextAction": "poll",
    "pollAfterMs": 250,
    "signing": {
      "step": null,
      "expiresAtMs": null,
      "readyAtMs": null
    }
  },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-04-23T20:56:04.937Z"
  }
}
```

## Responses

| Code | Description                                                                              |
| ---- | ---------------------------------------------------------------------------------------- |
| 200  | A signable payload is ready immediately                                                  |
| 202  | Accepted, but the first signable step is still building                                  |
| 400  | Invalid request                                                                          |
| 401  | Unauthorized                                                                             |
| 409  | Current tx request is invalid for signing                                                |
| 410  | Quote expired; request a new quote and retry                                             |
| 503  | PG or Kafka unavailable, execution plan not yet readable, or signing surface unavailable |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Build Swap
{% endembed %}


# Get Signing Status

Get the current signable step for a swap. Use this after a `mode: "async"` build response, after a `tx_request_expired` error, or when resuming a multi-step execution.

**Endpoint:** `GET /api/v1/swap/{swapId}/signing`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Path parameters

| Field    | Type   | Required | Description                            |
| -------- | ------ | -------- | -------------------------------------- |
| `swapId` | string | Yes      | Swap ID returned by `POST /swap/build` |

## Example

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/swap/8933a4cd-a08d-43b7-a63f-d7c1a53fdbe8/signing' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Responses

| Code | Description                                                         |
| ---- | ------------------------------------------------------------------- |
| 200  | A signable payload is ready                                         |
| 202  | The swap is still building or waiting for the valid signing window  |
| 401  | Unauthorized                                                        |
| 404  | Swap not found                                                      |
| 409  | Execution is terminal or the tx request is expired or not yet valid |
| 503  | Execution plan not yet readable                                     |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Get Signing Status
{% endembed %}


# Submit Signed Artifact

Submit the externally signed artifact for the current swap step.

**Endpoint:** `POST /api/v1/swap/{swapId}/signing`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Headers

| Header            | Required | Description                                                                                                                                                                                                                                                                                                             |
| ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | No       | Optional opaque dedupe token. If omitted, the gateway generates one automatically. Leave it blank for one-off Swagger or curl calls. Send your own UUIDv4 or ULID only when retrying the exact same payload and you need retry-safe dedupe. Allowed characters: `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `-`. Max 200 chars. |

## Path parameters

| Field    | Type   | Required | Description                            |
| -------- | ------ | -------- | -------------------------------------- |
| `swapId` | string | Yes      | Swap ID returned by `POST /swap/build` |

## Request body

| Field   | Type   | Required | Description                                                           |
| ------- | ------ | -------- | --------------------------------------------------------------------- |
| `rawTx` | string | Yes      | Signed transaction artifact returned by the external wallet or signer |

## Example

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/swap/92714c81-38ec-4782-8624-0f56f526a9fd/signing' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{
    "rawTx": "0x_signed_tx_from_external_wallet"
  }'
```

Leave `Idempotency-Key` blank for one-off Swagger or curl calls. If you need retry-safe dedupe, send your own key only when retrying the exact same payload. The SDK and Postman collection can generate one automatically.

## Responses

| Code | Description                                                                 |
| ---- | --------------------------------------------------------------------------- |
| 200  | Signed artifact accepted. Poll the same signing surface for the next step   |
| 400  | Invalid signed transaction                                                  |
| 401  | Unauthorized                                                                |
| 404  | Swap not found                                                              |
| 409  | Current tx request is expired, not yet valid, or does not match the payload |
| 503  | PG or Kafka unavailable                                                     |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Submit Signed Artifact
{% endembed %}


# Limit Orders

Public off-chain limit orders for authenticated API owners.

The public limit-order surface is:

* `POST /api/v1/limit-orders/limit`
* `GET /api/v1/limit-orders/limit/{limitOrderId}`
* `DELETE /api/v1/limit-orders/limit/{limitOrderId}`
* `POST /api/v1/limit-orders/limit/{limitOrderId}/replace`
* `GET /api/v1/limit-orders/limits`

Create and replace requests mirror the public quote shape (`fromChain`, `toChain`, `tokenIn`, `tokenOut`, `amount`) and add limit-order terms like `minAmountOut`, `timeInForce`, and expiry controls.


# Create Limit Order

Creates a durable off-chain limit order. The public request shape mirrors `POST /quote` and adds limit-order terms.

**Endpoint:** `POST /api/v1/limit-orders/limit`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Headers

| Header            | Type   | Description                                                                                                                   | Example                                |
| ----------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| `Idempotency-Key` | string | Required opaque dedupe token. Use UUIDv4 or ULID. Allowed characters: `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `-`. Max 200 chars. | `123e4567-e89b-12d3-a456-426614174000` |

## Request body

| Field           | Type    | Required | Description                       | Example         |
| --------------- | ------- | -------- | --------------------------------- | --------------- |
| `fromChain`     | string  | Yes      | Source chain identifier           | `"arbitrum"`    |
| `toChain`       | string  | Yes      | Destination chain identifier      | `"avax"`        |
| `tokenIn`       | string  | Yes      | Input token identifier            | `"TBTC"`        |
| `tokenOut`      | string  | Yes      | Output token identifier           | `"WETH"`        |
| `amount`        | string  | Yes      | Human amount string               | `"100.00"`      |
| `minAmountOut`  | string  | Yes      | Minimum output amount             | `"1"`           |
| `timeInForce`   | string  | No       | `GTC`, `GTD`, `IOC`, or `FOK`     | `"GTC"`         |
| `expiresAtMs`   | integer | No       | Absolute expiry in ms since epoch | `1745403600000` |
| `expireAfterMs` | integer | No       | Relative expiry in ms             | `86400000`      |
| `network`       | string  | No       | `mainnet` or `testnet`            | `"mainnet"`     |
| `slippageBps`   | integer | No       | Quote-time slippage hint          | `75`            |
| `recipient`     | string  | No       | Recipient address                 | `"0x1234..."`   |
| `route`         | object  | No       | Optional route snapshot           | `{}`            |
| `routeKey`      | string  | No       | Optional route identifier         | `"route_123"`   |

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/limit-orders/limit' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: limit-create-001' \
  -d '{
    "timeInForce": "GTC",
    "minAmountOut": "1",
    "fromChain": "arbitrum",
    "toChain": "avax",
    "tokenIn": "TBTC",
    "tokenOut": "WETH",
    "network": "mainnet",
    "amount": "100.00",
    "slippageBps": 75,
    "recipient": "0x1234567890123456789012345678901234567890"
  }'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "limitOrderId": "lim_abc123",
    "status": "open",
    "expiresAtMs": 1745403600000,
    "minAmountOut": "1"
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Responses

| Code | Description                               |
| ---- | ----------------------------------------- |
| 200  | Created                                   |
| 400  | Invalid request                           |
| 401  | Unauthorized (missing or invalid API key) |
| 503  | PG unavailable                            |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Create Limit Order
{% endembed %}


# List Limit Orders

List limit orders for the authenticated owner with optional filters.

**Endpoint:** `GET /api/v1/limit-orders/limits`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Query parameters

| Parameter | Type    | Required | Description                              | Example |
| --------- | ------- | -------- | ---------------------------------------- | ------- |
| `limit`   | integer | No       | Max orders to return (1–200, default 50) | `20`    |
| `status`  | string  | No       | Filter by status                         | `open`  |

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/limit-orders/limits?limit=20&status=open' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "limitOrders": [
      {
        "limitOrderId": "lim_abc123",
        "status": "open",
        "timeInForce": "GTC",
        "createdAtMs": 1745396400000,
        "updatedAtMs": 1745396400000,
        "expiresAtMs": 1745403600000,
        "minAmountOut": "1"
      }
    ]
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Returned row fields

| Field          | Type    | Description              |
| -------------- | ------- | ------------------------ |
| `limitOrderId` | string  | Limit-order identifier   |
| `status`       | string  | Current order status     |
| `timeInForce`  | string  | Expiry mode              |
| `createdAtMs`  | integer | Creation timestamp       |
| `updatedAtMs`  | integer | Last update timestamp    |
| `expiresAtMs`  | integer | Expiry timestamp         |
| `minAmountOut` | string  | Minimum output threshold |

## Responses

| Code | Description                               |
| ---- | ----------------------------------------- |
| 200  | OK                                        |
| 401  | Unauthorized (missing or invalid API key) |
| 503  | PG unavailable                            |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — List Limit Orders
{% endembed %}


# Get or Cancel Limit Order

Retrieve a limit order by ID or cancel it. Only the authenticated owner can access or cancel.

**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

***

## Get limit order by id

**Endpoint:** `GET /api/v1/limit-orders/limit/{limitOrderId}`

### Path parameters

| Parameter      | Type   | Required | Description    | Example      |
| -------------- | ------ | -------- | -------------- | ------------ |
| `limitOrderId` | string | Yes      | Limit order ID | `lim_abc123` |

### Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/limit-orders/limit/e896c19b-fccd-466c-9b6d-eca6d7a6cd5a' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "limitOrderId": "lim_abc123",
    "status": "open",
    "timeInForce": "GTC",
    "createdAtMs": 1745396400000,
    "updatedAtMs": 1745396400000,
    "expiresAtMs": 1745403600000,
    "minAmountOut": "1"
  },
  "meta": { "requestId": "...", "timestamp": "..." }
}
```

{% endtab %}
{% endtabs %}

***

## Cancel limit order by id

**Endpoint:** `DELETE /api/v1/limit-orders/limit/{limitOrderId}`

### Headers

| Header            | Type   | Required | Description                                                                                                                   |
| ----------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | string | Yes      | Required opaque dedupe token. Use UUIDv4 or ULID. Allowed characters: `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `-`. Max 200 chars. |

### Path parameters

| Parameter      | Type   | Required | Description    | Example      |
| -------------- | ------ | -------- | -------------- | ------------ |
| `limitOrderId` | string | Yes      | Limit order ID | `lim_abc123` |

### Request body (optional)

| Field    | Type   | Description            | Example        |
| -------- | ------ | ---------------------- | -------------- |
| `reason` | string | Optional cancel reason | `user_request` |

### Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X DELETE 'https://api.gateway.zert.com/api/v1/limit-orders/limit/e896c19b-fccd-466c-9b6d-eca6d7a6cd5a' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: limit-cancel-001' \
  -H 'Content-Type: application/json' \
  -d '{"reason": "user_request"}'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "limitOrderId": "lim_abc123",
    "status": "canceled"
  },
  "meta": { "requestId": "...", "timestamp": "..." }
}
```

{% endtab %}
{% endtabs %}

## Responses

| Code | Description                               |
| ---- | ----------------------------------------- |
| 200  | OK                                        |
| 401  | Unauthorized (missing or invalid API key) |
| 403  | Forbidden (not owner)                     |
| 404  | Not found                                 |
| 503  | PG unavailable                            |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Limit Order Get/Cancel
{% endembed %}


# Replace Limit Order

Atomically creates a replacement order and cancels the referenced open order. The replacement receives a new `limitOrderId`.

**Endpoint:** `POST /api/v1/limit-orders/limit/{limitOrderId}/replace`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Headers

| Header            | Type   | Required | Description                                                                                                                   |
| ----------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | string | Yes      | Required opaque dedupe token. Use UUIDv4 or ULID. Allowed characters: `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `-`. Max 200 chars. |

## Path parameters

| Parameter      | Type   | Required | Description                    | Example      |
| -------------- | ------ | -------- | ------------------------------ | ------------ |
| `limitOrderId` | string | Yes      | Existing open order to replace | `lim_abc123` |

## Request body

Request shape matches `POST /api/v1/limit-orders/limit`.

| Field           | Type    | Required | Description                       | Example         |
| --------------- | ------- | -------- | --------------------------------- | --------------- |
| `fromChain`     | string  | Yes      | Source chain identifier           | `"arbitrum"`    |
| `toChain`       | string  | Yes      | Destination chain identifier      | `"avax"`        |
| `tokenIn`       | string  | Yes      | Input token identifier            | `"TBTC"`        |
| `tokenOut`      | string  | Yes      | Output token identifier           | `"WETH"`        |
| `amount`        | string  | Yes      | Human amount string               | `"100.00"`      |
| `minAmountOut`  | string  | Yes      | Minimum output amount             | `"1"`           |
| `timeInForce`   | string  | No       | `GTC`, `GTD`, `IOC`, or `FOK`     | `"GTC"`         |
| `expiresAtMs`   | integer | No       | Absolute expiry in ms since epoch | `1745407200000` |
| `expireAfterMs` | integer | No       | Relative expiry in ms             | `86400000`      |
| `network`       | string  | No       | `mainnet` or `testnet`            | `"mainnet"`     |
| `slippageBps`   | integer | No       | Quote-time slippage hint          | `75`            |
| `recipient`     | string  | No       | Recipient address                 | `"0x1234..."`   |

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/limit-orders/limit/lim_abc123/replace' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: limit-replace-001' \
  -d '{
    "timeInForce": "GTC",
    "minAmountOut": "2",
    "fromChain": "arbitrum",
    "toChain": "avax",
    "tokenIn": "TBTC",
    "tokenOut": "WETH",
    "network": "mainnet",
    "amount": "100.00",
    "slippageBps": 75,
    "recipient": "0x1234567890123456789012345678901234567890"
  }'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "limitOrderId": "lim_replacement_456",
    "status": "open",
    "expiresAtMs": 1745407200000,
    "minAmountOut": "2",
    "replacesLimitOrderId": "lim_abc123"
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Responses

| Code | Description                                   |
| ---- | --------------------------------------------- |
| 200  | Replaced                                      |
| 400  | Invalid request                               |
| 401  | Unauthorized                                  |
| 403  | Forbidden                                     |
| 404  | Not found                                     |
| 409  | Order not replaceable or idempotency conflict |
| 503  | PG unavailable                                |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Replace Limit Order
{% endembed %}


# Status & Tracking

Track on-chain transaction status by hash and chain. Supports EVM, Solana, and Bitcoin.

The public status surface is `GET /api/v1/status/transaction/{txHash}`.


# Get Transaction Status

Retrieve the status of a transaction by chain and transaction hash. Supports EVM, Solana, and Bitcoin.

**Endpoint:** `GET /api/v1/status/transaction/{txHash}`\
**Auth:** Required (`Authorization: Bearer YOUR_API_KEY`)

## Path parameters

| Parameter | Type   | Required | Description      |
| --------- | ------ | -------- | ---------------- |
| `txHash`  | string | Yes      | Transaction hash |

## Query parameters

| Parameter  | Type   | Required | Description                                 | Example       |
| ---------- | ------ | -------- | ------------------------------------------- | ------------- |
| `chainRef` | string | Yes      | Chain identifier. Required query parameter. | `eth-mainnet` |

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/status/transaction/0x123...?chainRef=eth-mainnet' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "txHash": "0x123...",
    "chain": "eth-mainnet",
    "status": "confirmed",
    "confirmedAtMs": 1745400000000
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Transaction status values

| Status      | Description                              |
| ----------- | ---------------------------------------- |
| `pending`   | Transaction submitted, not yet confirmed |
| `confirmed` | Transaction confirmed on chain           |
| `failed`    | Transaction failed                       |

## Responses

| Code | Description                               |
| ---- | ----------------------------------------- |
| 200  | Transaction status found                  |
| 400  | Missing or invalid `chainRef`             |
| 401  | Unauthorized (missing or invalid API key) |
| 404  | Transaction not found                     |
| 502  | Upstream error                            |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Transaction Status
{% endembed %}


# Health Check

Returns basic service health. This endpoint does **not** require authentication.

**Endpoint:** `GET /api/v1/health`\
**Auth:** None

## Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/health'
```

{% endtab %}

{% tab title="Example Response" %}

```json
{
  "data": {
    "status": "ok"
  },
  "meta": {
    "requestId": "req_xyz",
    "timestamp": "2026-04-23T12:00:00.000Z"
  }
}
```

{% endtab %}
{% endtabs %}

## Response fields

| Field    | Type   | Description           | Example |
| -------- | ------ | --------------------- | ------- |
| `status` | string | Service health status | `ok`    |

## Responses

| Code | Description |
| ---- | ----------- |
| 200  | OK          |

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Health
{% endembed %}


# Models

Reference for the public swap request and response shapes used by the B2B API Gateway.

This page intentionally stays compact. The Swagger UI models list is the exhaustive schema inventory and remains the authoritative source for every field and union member.

## Common

### ApiMeta

Present on successful responses.

| Field       | Type   | Description            |
| ----------- | ------ | ---------------------- |
| `requestId` | string | Request correlation ID |
| `timestamp` | string | ISO 8601 timestamp     |

### ApiError

Structured error body.

| Field           | Type    | Description                 |
| --------------- | ------- | --------------------------- |
| `error.code`    | string  | Stable error code           |
| `error.message` | string  | Human-readable explanation  |
| `error.details` | object  | Optional structured context |
| `meta`          | ApiMeta | Request metadata            |

***

## Quote And Permit

### QuoteRequest

| Field                                         | Type    | Required | Description                        |
| --------------------------------------------- | ------- | -------- | ---------------------------------- |
| `fromChain`                                   | string  | Yes      | Source chain identifier            |
| `toChain`                                     | string  | Yes      | Destination chain identifier       |
| `tokenIn`                                     | string  | Yes      | Input token identifier             |
| `tokenOut`                                    | string  | Yes      | Output token identifier            |
| `amount`                                      | string  | Yes      | Human amount string                |
| `network`                                     | string  | No       | `mainnet` or `testnet`             |
| `slippageBps`                                 | integer | No       | Quote-time slippage hint           |
| `recipient`                                   | string  | No       | Recipient address                  |
| `tokenRefFromOverride` / `tokenRefToOverride` | string  | No       | Explicit token-reference overrides |

### QuoteRouteSummary

| Field                      | Type      | Description                                                                 |
| -------------------------- | --------- | --------------------------------------------------------------------------- |
| `routeId`                  | string    | Route identifier used in `POST /swap/build`                                 |
| `provider`                 | string    | Convenience alias when the route resolves to a single public provider label |
| `providers`                | string\[] | Ordered provider list across the route steps                                |
| `bridgeProvider`           | string    | Bridge provider when the route includes a bridge step                       |
| `family`                   | enum      | Route family when available                                                 |
| `stepCount`                | integer   | Number of execution legs represented by the route                           |
| `expectedAmountOut`        | string    | Expected output amount                                                      |
| `estimatedDurationSeconds` | integer   | Estimated completion time                                                   |
| `warnings`                 | string\[] | Optional route warnings                                                     |

For same-chain routes, `provider` is often enough. For cross-chain or other multi-step routes, prefer `providers`, `bridgeProvider`, and `stepCount`.

### QuoteResponse

| Field              | Type                 | Description                                                         |
| ------------------ | -------------------- | ------------------------------------------------------------------- |
| `data.quoteId`     | string               | Quote identifier                                                    |
| `data.expiresAtMs` | integer              | Quote expiry timestamp                                              |
| `data.routes`      | QuoteRouteSummary\[] | Available route summaries; may be empty when no viable route exists |
| `meta`             | ApiMeta              | Request metadata                                                    |

***

## Swap

### SwapBuildRequest

| Field                                                    | Type    | Required | Description                        |
| -------------------------------------------------------- | ------- | -------- | ---------------------------------- |
| `quoteId`                                                | string  | Yes      | Quote identifier                   |
| `routeId`                                                | string  | Yes      | Route identifier from the quote    |
| `recipient`                                              | string  | No       | Recipient address                  |
| `userAddress`                                            | string  | No       | Wallet owner address               |
| `permitType`                                             | enum    | No       | `eip2612`, `permit2`, or `approve` |
| `permitDeadline`                                         | integer | No       | Permit deadline                    |
| `permitSignature` / `permitSignatureCompact` / `permit2` | object  | No       | Optional permit artifacts          |
| `waitForTxRequestMs`                                     | integer | No       | Bounded wait before async fallback |

### WalletSigningStep

| Field     | Type    | Description                   |
| --------- | ------- | ----------------------------- |
| `current` | integer | Current signing step number   |
| `total`   | integer | Total number of signing steps |

### WalletSigningEnvelope

| Field            | Type                      | Description                               |
| ---------------- | ------------------------- | ----------------------------------------- |
| `step`           | WalletSigningStep or null | Current step descriptor                   |
| `expiresAtMs`    | integer or null           | Current payload expiry                    |
| `readyAtMs`      | integer or null           | Time when payload is expected to be ready |
| `payload`        | union                     | Chain-family-specific signing payload     |
| `executionHints` | object                    | Optional gas and execution hints          |
| `clientHints`    | object                    | Optional wallet UX hints                  |

`WalletSigningPayload` is a union across EVM, Solana, Bitcoin, and Cosmos payload shapes. Use Swagger Models for the exact family-specific fields.

### WalletSwapSigningReadyResponse

| Field             | Type                  | Description            |
| ----------------- | --------------------- | ---------------------- |
| `data.swapId`     | string                | Swap identifier        |
| `data.mode`       | string                | `sync`                 |
| `data.nextAction` | string                | `sign`                 |
| `data.signing`    | WalletSigningEnvelope | Ready-to-sign envelope |

### WalletSwapSigningAsyncResponse

| Field              | Type                  | Description                |
| ------------------ | --------------------- | -------------------------- |
| `data.swapId`      | string                | Swap identifier            |
| `data.mode`        | string                | `async`                    |
| `data.nextAction`  | string                | `poll`                     |
| `data.pollAfterMs` | integer               | Suggested polling interval |
| `data.signing`     | WalletSigningEnvelope | Pending signing envelope   |

### WalletSignedArtifactRequest

| Field   | Type   | Required | Description                          |
| ------- | ------ | -------- | ------------------------------------ |
| `rawTx` | string | Yes      | Externally signed artifact to submit |

***

## Discovery

### Chain

| Field  | Type   | Description               |
| ------ | ------ | ------------------------- |
| `id`   | string | Chain identifier          |
| `name` | string | Human-readable chain name |

### Token

| Field      | Type    | Description                          |
| ---------- | ------- | ------------------------------------ |
| `chain`    | string  | Chain identifier                     |
| `symbol`   | string  | Canonical token symbol               |
| `name`     | string  | Human-readable token name            |
| `address`  | string  | EVM contract address when applicable |
| `decimals` | integer | Token decimals                       |
| `mint`     | string  | Solana mint address when applicable  |
| `logoUrl`  | string  | Token logo URL when available        |

### Pair

| Field       | Type   | Description                  |
| ----------- | ------ | ---------------------------- |
| `fromChain` | string | Source chain identifier      |
| `toChain`   | string | Destination chain identifier |
| `tokenIn`   | string | Input token symbol           |
| `tokenOut`  | string | Output token symbol          |

### Response wrappers

| Schema           | Fields                                            |
| ---------------- | ------------------------------------------------- |
| `ChainsResponse` | `data.chains`, `meta`                             |
| `TokensResponse` | `data.tokens`, optional `data.nextCursor`, `meta` |
| `PairsResponse`  | `data.pairs`, optional `data.nextCursor`, `meta`  |

***

## Limit Orders

### LimitOrderPublicRequest

Public create and replace requests mirror `QuoteRequest` and add limit-order terms:

| Field                                                   | Type            | Required | Description                             |
| ------------------------------------------------------- | --------------- | -------- | --------------------------------------- |
| `fromChain`, `toChain`, `tokenIn`, `tokenOut`, `amount` | mixed           | Yes      | Same core quote inputs as `POST /quote` |
| `minAmountOut`                                          | string          | Yes      | Minimum output threshold                |
| `timeInForce`                                           | enum            | No       | `GTC`, `GTD`, `IOC`, or `FOK`           |
| `expiresAtMs`                                           | integer         | No       | Absolute expiry                         |
| `expireAfterMs`                                         | integer         | No       | Relative expiry                         |
| `network`, `slippageBps`, `recipient`                   | mixed           | No       | Optional quote-style helpers            |
| `route`, `routeKey`                                     | object / string | No       | Optional route metadata                 |

### Limit-order responses

| Schema                      | Fields                                                                                               |
| --------------------------- | ---------------------------------------------------------------------------------------------------- |
| `LimitOrderCreateResponse`  | `limitOrderId`, `status`, `expiresAtMs`, `minAmountOut`                                              |
| `LimitOrderReplaceResponse` | `limitOrderId`, `status`, `expiresAtMs`, `minAmountOut`, `replacesLimitOrderId`                      |
| `LimitOrderCancelResponse`  | `limitOrderId`, `status`                                                                             |
| `LimitOrderRow`             | `limitOrderId`, `status`, `timeInForce`, `createdAtMs`, `updatedAtMs`, `expiresAtMs`, `minAmountOut` |
| `LimitOrdersListResponse`   | `data.limitOrders`, `meta`                                                                           |

***

## Status

### TransactionStatusResponse

| Field                | Type    | Description                           |
| -------------------- | ------- | ------------------------------------- |
| `data.txHash`        | string  | Transaction hash                      |
| `data.chain`         | string  | Chain identifier                      |
| `data.status`        | enum    | `pending`, `confirmed`, or `failed`   |
| `data.confirmedAtMs` | integer | Confirmation timestamp when available |
| `data.error`         | string  | Failure reason when available         |
| `meta`               | ApiMeta | Request metadata                      |

***

{% embed url="<https://api.gateway.zert.com/docs>" %}
Zert API — Schemas
{% endembed %}


# Guides

* [Full swap flow](/integration-example/full-swap-flow): quote, build the signing step, poll, sign externally, and submit the signed artifact
* [Discovery: chains, tokens, and pairs](/integration-example/discovery-and-quote): fetch metadata and request quotes
* [Limit orders](/integration-example/limit-orders): create, list, and cancel limit orders
* [Check transaction status](/integration-example/check-transaction-status): verify on-chain status by tx hash


# Full swap flow

This guide walks through the public swap flow: quote, build, poll when needed, sign externally, submit the signed artifact, and optionally track the resulting transaction.

## Step 1: Request a quote

Call `POST /quote` with the source chain, destination chain, input token, output token, and amount.

## Step 2: Build the current signing step

Call `POST /swap/build` with the `quoteId` and selected `routeId`. Swagger does not auto-fill those fields from the Step 1 response, so paste the fresh values yourself when testing in the docs UI.

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/swap/build' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "quoteId": "REPLACE_WITH_QUOTE_ID_FROM_POST_QUOTE",
    "routeId": "REPLACE_WITH_ROUTE_ID_FROM_SELECTED_ROUTE",
    "recipient": "0x1234567890123456789012345678901234567890",
    "waitForTxRequestMs": 300
  }'
```

If you want retry-safe dedupe on direct HTTP calls, send your own `Idempotency-Key`. Swagger does not carry the prior quote response into this request. SDK and Postman flows can generate the idempotency key automatically, and Postman also captures the live `quoteId` and `routeId` for you.

The response is either:

* `mode: "sync"` with a ready-to-sign payload in `data.signing`
* `mode: "async"` with `pollAfterMs` telling you when to poll `GET /swap/{swapId}/signing`

If the signing surface for the active step is not configured, `POST /swap/build` returns `503` instead of a placeholder transaction.

Postman does not sign transactions. Hand the payload to the external wallet or signer you control.

## Step 3: Poll the signing endpoint

If the build response is async, poll until the signing payload is ready:

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/swap/your-swap-id/signing' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Step 4: Submit the signed artifact

After the wallet signs the payload, submit the signed artifact:

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/swap/your-swap-id/signing' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "rawTx": "0x_signed_tx_from_external_wallet"
  }'
```

If the flow has multiple signing steps, keep polling the same endpoint until the next payload is ready.

## Step 5: Track the transaction

Once you have a transaction hash, use `GET /status/transaction/{txHash}?chainRef=...` to verify on-chain progress.

| Step | Endpoint                           | Purpose                         |
| ---- | ---------------------------------- | ------------------------------- |
| 1    | `POST /quote`                      | Request a quote                 |
| 2    | `POST /swap/build`                 | Build the current signing step  |
| 3    | `GET /swap/{swapId}/signing`       | Poll when signing is async      |
| 4    | `POST /swap/{swapId}/signing`      | Submit the signed artifact      |
| 5    | `GET /status/transaction/{txHash}` | Verify the on-chain transaction |

See [Request Quote](/api-reference/swap/request-quote), [Build Swap](/api-reference/swap/submit-swap), [Get Signing Status](/api-reference/swap/get-wallet-signing-status), [Submit Signed Artifact](/api-reference/swap/submit-wallet-signed-artifact), and [Get Transaction Status](/api-reference/status-and-tracking/get-transaction-status) for full request and response details.


# Discovery: chains, tokens, and pairs

Use the discovery endpoints to fetch supported chains, tokens, and tradeable pairs before building quote requests.

## Step 0: Health check (optional)

Verify the gateway is up with `GET /health` (no auth required). See [Health Check](/health-check).

## Step 1: List chains

Get all enabled chains to build chain selectors or validate `fromChain` / `toChain`.

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/chains' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

Response:

```json
{
  "data": {
    "chains": [
      { "id": "eth-mainnet", "name": "Ethereum Mainnet" },
      { "id": "polygon-mainnet", "name": "Polygon" }
    ]
  },
  "meta": { "requestId": "...", "timestamp": "..." }
}
```

Use `id` values as chain identifiers in quote and status calls.

## Step 2: List tokens (optional)

Filter tokens by chain or search by symbol/name. Use results to build token selectors and validate token availability.

```bash
# All tokens on Ethereum, limit 20
curl -X GET 'https://api.gateway.zert.com/api/v1/tokens?chainRef=eth-mainnet&limit=20' \
  -H 'Authorization: Bearer YOUR_API_KEY'

# Search by symbol
curl -X GET 'https://api.gateway.zert.com/api/v1/tokens?q=USDC&limit=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

Response includes `data.tokens` with fields like `chain`, `symbol`, `name`, `address`, `decimals`, and an optional `nextCursor`.

## Step 3: List pairs (optional)

Get tradeable pairs for a chain or token to validate or suggest quote inputs.

```bash
# Pairs on Ethereum with USDC as input
curl -X GET 'https://api.gateway.zert.com/api/v1/pairs?chainRef=eth-mainnet&tokenIn=USDC&limit=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

Response includes `data.pairs` with `fromChain`, `toChain`, `tokenIn`, `tokenOut`, and an optional `nextCursor`.

## Step 4: Request a quote

Use discovered chain identifiers and token identifiers in `POST /quote`:

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/quote' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{
    "fromChain": "solana",
    "toChain": "avax",
    "tokenIn": "SOL",
    "tokenOut": "WETH",
    "network": "mainnet",
    "amount": "100.00"
  }'
```

## Summary

| Step | Endpoint      | Purpose                                  |
| ---- | ------------- | ---------------------------------------- |
| 0    | `GET /health` | Optional health check (no auth)          |
| 1    | `GET /chains` | Get supported chain identifiers          |
| 2    | `GET /tokens` | Get tokens by chain or search            |
| 3    | `GET /pairs`  | Get tradeable pairs for validation or UX |
| 4    | `POST /quote` | Get route summaries for a swap idea      |

See [Health Check](/health-check), [List Chains](/api-reference/discovery/list-chains), [List Tokens](/api-reference/discovery/list-tokens), [List Pairs](/api-reference/discovery/list-pairs), and [Request Quote](/api-reference/swap/request-quote) for full parameters and responses.


# Limit orders

Create, list, get, replace, and cancel off-chain limit orders. Public create and replace requests mirror the quote shape and add `minAmountOut` plus expiry controls.

## Prerequisites

* API key (header `Authorization: Bearer YOUR_API_KEY`)
* Base URL: `https://api.gateway.zert.com/api/v1`

## Step 1: Create a limit order

Call `POST /limit-orders/limit` with the quote-style fields `fromChain`, `toChain`, `tokenIn`, `tokenOut`, and `amount`, plus `minAmountOut`. Create requests require an `Idempotency-Key`.

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/limit-orders/limit' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: limit-create-001' \
  -d '{
    "timeInForce": "GTC",
    "minAmountOut": "1",
    "fromChain": "arbitrum",
    "toChain": "avax",
    "tokenIn": "TBTC",
    "tokenOut": "WETH",
    "network": "mainnet",
    "amount": "100.00",
    "slippageBps": 75,
    "recipient": "0x1234567890123456789012345678901234567890"
  }'
```

Response includes `data.limitOrderId`, `data.status`, `data.expiresAtMs`, and `data.minAmountOut`.

## Step 2: List your limit orders

Call `GET /limit-orders/limits` to list orders for the authenticated owner. Use query `limit` (1–200) and optional `status` (e.g. `open`, `canceled`).

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/limit-orders/limits?limit=20&status=open' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Step 3: Get or cancel a single order

* **Get:** `GET /limit-orders/limit/{limitOrderId}` — returns full order details.
* **Cancel:** `DELETE /limit-orders/limit/{limitOrderId}` — requires `Idempotency-Key`; optional body `{"reason": "..."}`.

```bash
# Get
curl -X GET 'https://api.gateway.zert.com/api/v1/limit-orders/limit/lim_abc123' \
  -H 'Authorization: Bearer YOUR_API_KEY'

# Cancel
curl -X DELETE 'https://api.gateway.zert.com/api/v1/limit-orders/limit/lim_abc123' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: limit-cancel-001' \
  -H 'Content-Type: application/json' \
  -d '{"reason": "user_request"}'
```

## Step 4: Replace an open order

Call `POST /limit-orders/limit/{limitOrderId}/replace` with the same request shape as create. Replacement requests also require `Idempotency-Key`.

```bash
curl -X POST 'https://api.gateway.zert.com/api/v1/limit-orders/limit/lim_abc123/replace' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: limit-replace-001' \
  -d '{
    "timeInForce": "GTC",
    "minAmountOut": "2",
    "fromChain": "arbitrum",
    "toChain": "avax",
    "tokenIn": "TBTC",
    "tokenOut": "WETH",
    "network": "mainnet",
    "amount": "100.00"
  }'
```

## Summary

| Step | Endpoint                                | Purpose                   |
| ---- | --------------------------------------- | ------------------------- |
| 1    | `POST /limit-orders/limit`              | Create limit order        |
| 2    | `GET /limit-orders/limits`              | List owner's limit orders |
| 3    | `GET /limit-orders/limit/{id}`          | Get one order             |
| 3    | `DELETE /limit-orders/limit/{id}`       | Cancel one order          |
| 4    | `POST /limit-orders/limit/{id}/replace` | Replace an open order     |

See [Create Limit Order](/api-reference/limit-orders/limit-order-create), [List Limit Orders](/api-reference/limit-orders/limit-orders-list), [Get or Cancel Limit Order](/api-reference/limit-orders/limit-order-get-cancel), and [Replace Limit Order](/api-reference/limit-orders/limit-order-replace) for full parameters and responses.


# Check transaction status

After submitting a wallet-signed artifact, you can verify on-chain status using the transaction status endpoint. Supports EVM, Solana, and Bitcoin.

## When to use

* After `POST /swap/{swapId}/signing` leads to a broadcast transaction hash in your flow.
* To poll until the transaction is `confirmed` or `failed`.
* To show users real-time status (e.g. pending → confirmed).

## Request

**Endpoint:** `GET /status/transaction/{txHash}`\
**Required query:** `chainRef` — chain where the transaction was submitted.

```bash
curl -X GET 'https://api.gateway.zert.com/api/v1/status/transaction/0x123...abc?chainRef=eth-mainnet' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

Use the correct `chainRef` for the chain of the tx (e.g. `eth-mainnet`, `polygon-mainnet`, `solana-mainnet`, `bitcoin`).

## Response

```json
{
  "data": {
    "txHash": "0x123...abc",
    "chain": "eth-mainnet",
    "status": "confirmed",
    "confirmedAtMs": 1745400000000
  },
  "meta": { "requestId": "...", "timestamp": "..." }
}
```

## Status values

| Status      | Description                  |
| ----------- | ---------------------------- |
| `pending`   | Submitted, not yet confirmed |
| `confirmed` | Confirmed on chain           |
| `failed`    | Transaction failed           |

## Polling example

Poll every few seconds until `status` is `confirmed` or `failed`, or until a timeout:

1. Call `GET /status/transaction/{txHash}?chainRef=...`.
2. If `data.status` is `confirmed` or `failed`, stop.
3. If `pending`, wait (e.g. 3–5 seconds) and repeat.
4. Treat `404` as not indexed or not found yet, and retry only if that matches your expected flow.

See [Get Transaction Status](/api-reference/status-and-tracking/get-transaction-status) for full parameters, response fields, and error codes.


# API Reference

ZERT public APIs for discovery, swaps, limit orders, status tracking, and health checks.

**Base URL:** `https://api.gateway.zert.com/api/v1`\
**Authentication:** API key in header `Authorization: Bearer YOUR_API_KEY` (required for all endpoints except `GET /health`).

* [Health Check](/health-check)
* [Get Supported Chains](/api-reference/discovery/list-chains)
* [List Tokens](/api-reference/discovery/list-tokens)
* [List Pairs](/api-reference/discovery/list-pairs)
* [Request Quote](/api-reference/swap/request-quote)
* [Build Swap](/api-reference/swap/submit-swap)
* [Get Signing Status](/api-reference/swap/get-wallet-signing-status)
* [Submit Signed Artifact](/api-reference/swap/submit-wallet-signed-artifact)
* [Get Transaction Status](/api-reference/status-and-tracking/get-transaction-status)
* [Create Limit Order](/api-reference/limit-orders/limit-order-create)
* [Get or Cancel Limit Order](/api-reference/limit-orders/limit-order-get-cancel)
* [Replace Limit Order](/api-reference/limit-orders/limit-order-replace)
* [List Limit Orders](/api-reference/limit-orders/limit-orders-list)
* [Models](/models)

***

{% embed url="<https://api.gateway.zert.com/docs>" %}
**Zert API UI** — Interactive API reference
{% endembed %}


# SDK Overview

Routing Engine SDK is a TypeScript/JavaScript client for **Zert's routing API**. Use it in front-end or back-end apps to discover chains/tokens/pairs, request quotes, run the swap signing flow, manage limit orders, and check transaction/system status.

The SDK talks only to Zert's backend at a fixed URL (`DEFAULT_API_BASE_URL` from the package; no runtime `baseURL` override). Pass **`api.apiKey`** so every request includes the **`x-api-key`** header. You can also set timeout, `baseHeaders`, and `onError`.  It exposes a layer-based API (`sdk.swap.build()`) and flat methods (`sdk.buildSwap()`).

## Documentation

* [SDK Overview & Configuration](/routing-engine-sdk/installation-and-configuration) — Install, configure, and get started.
* [Get Supported Chains](/routing-engine-sdk/get-supported-chains) — Fetch supported chains.
* [Token Management](/routing-engine-sdk/get-available-tokens) — Fetch available tokens.
* [Get Trading Pairs](/routing-engine-sdk/get-trading-pairs) — Fetch trading pairs.
* [Request a Quote](/routing-engine-sdk/request-a-quote) — Request a quote for a swap.
* [Submit a Swap](/routing-engine-sdk/submit-a-swap) — Submit a swap.
* [Limit Orders](/routing-engine-sdk/limit-orders) — Create, get, cancel, and list limit orders.
* [Status](/routing-engine-sdk/sdk-overview) — Get transaction status.
* [Health](/routing-engine-sdk/check-service-health) — Check service health.
* [Error Handling](/routing-engine-sdk/error-handling-for-apis) — Handle API and SDK errors.

## Routing Engine SDK features include

* **Discovery endpoints** - Fetch supported tokens, chains, and tradeable pairs.
* **Quote and swap flow** - `POST /quote`, then build/sign/submit via 3 swap endpoints.
* **Limit orders** - Create, get, cancel, replace, and list limit orders.
* **Status and health** - Check transaction status and service health.
* **Optional configuration** - `timeout`, `baseHeaders`, and global `onError` (fixed backend host; exports include `DEFAULT_API_BASE_URL` and `API_KEY_HEADER`).
* **Two API styles** - Layer-based methods (`sdk.swap.build()`) or flat methods (`sdk.buildSwap()`).
* **Typed and promise-based** — Full TypeScript types and consistent Promise-based async API.


# Installation and Configuration

Install and configure the Routing Engine SDK for Zert's routing API.

> TypeScript/JavaScript client for Zert's routing API — tokens, chains, pairs, quotes, swaps, execution, and limit orders.

The Routing Engine SDK calls **Zert's backend only**. The base URL is fixed in the package as **`DEFAULT_API_BASE_URL`** (see `routing-engine-sdk` exports); you cannot override it via config.

**All API requests require an API key.** Pass `api.apiKey` when creating the SDK; it is sent as the **`x-api-key`** header on every request (header name is also available as **`API_KEY_HEADER`**).

## How to integrate the SDK

**Step 1 — Install the SDK**

```bash
npm install routing-engine-sdk
```

Or with yarn, pnpm, or bun:

```bash
yarn add routing-engine-sdk
# or: pnpm add routing-engine-sdk
# or: bun add routing-engine-sdk
```

**Step 2 — Create and configure the SDK**

```typescript
import { RoutingEngineSDK, DEFAULT_API_BASE_URL, API_KEY_HEADER } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: {
    apiKey: 'your-api-key',
  },
});
```

Optional: timeout and headers:

```typescript
const sdk = new RoutingEngineSDK({
  api: {
    apiKey: 'your-api-key',
    timeout: 15000,
    baseHeaders: {
      headers: { 'X-Custom': 'value' },
    },
  },
});
```

**Step 3 — Call the API**

Use layers (e.g. `sdk.tokens`, `sdk.quote`) or flat methods (`getTokens`, `requestQuote`, `createLimit`):

```typescript
const tokens = await sdk.getTokens();
const quote = await sdk.requestQuote({
  fromChain: 'arbitrum',
  toChain: 'avax',
  tokenIn: 'TBTC',
  tokenOut: 'WETH',
  network: 'mainnet',
  amount: '100.00',
  slippageBps: 75,
  recipient: '0x1234567890123456789012345678901234567890',
});
const build = await sdk.buildSwap({
  quoteId: quote.data.quoteId,
  routeId: quote.data.routes[0].routeId,
  recipient: '0x1234567890123456789012345678901234567890',
});
const signing = await sdk.getSwapSigning(build.data.swapId);
await sdk.submitSwapSigning(build.data.swapId, { rawTx: '0xsigned_raw_tx' });

const order = await sdk.createLimit({
  timeInForce: 'GTC',
  minAmountOut: '1',
  fromChain: 'arbitrum',
  toChain: 'avax',
  tokenIn: 'TBTC',
  tokenOut: 'WETH',
  network: 'mainnet',
  amount: '100.00',
  slippageBps: 75,
  recipient: '0x1234567890123456789012345678901234567890',
});
const status = await sdk.getTransactionStatus('0xhash', { chainRef: 'eth-mainnet' });
const health = await sdk.getHealth();
```

**Step 4 — Handle errors**

See Error Handling. API failures throw `RoutingEngineAPIError`. Invalid or unsupported config (e.g. wrong types, or passing `baseURL`) can throw `ConfigTypeError` in development.

### API surface

| Layer / access | Doc              |
| -------------- | ---------------- |
| `sdk.tokens`   | Token Management |
| `sdk.chains`   | Chains           |
| `sdk.pairs`    | Pairs            |
| `sdk.quote`    | Quote            |
| `sdk.swap`     | Swap             |
| `sdk.limit`    | Limit orders     |
| `sdk.status`   | Status           |
| `sdk.health`   | Health           |

`sdk.api` exposes the same layers for backward compatibility.

### Flat API

| Flat method                                                                    | Delegates to                                                |
| ------------------------------------------------------------------------------ | ----------------------------------------------------------- |
| `getTokens(params?)`, `getChains()`, `getPairs(params?)`                       | `tokens.get(params?)`, `chains.get()`, `pairs.get(params?)` |
| `requestQuote(body)`                                                           | `quote.request(body)`                                       |
| `buildSwap(body)`, `getSwapSigning(swapId)`, `submitSwapSigning(swapId, body)` | matching `swap.*` methods                                   |
| `createLimit`, `getLimit`, `cancelLimit`, `replaceLimit`, `listLimits`         | matching `limit.*` methods                                  |
| `getTransactionStatus(txHash, params)`, `getHealth()`                          | `status.getTransaction(...)`, `health.get()`                |

## Configuration reference (`api` option)

| Field         | Description                                                                                     |
| ------------- | ----------------------------------------------------------------------------------------------- |
| `apiKey`      | Required for authenticated Zert API calls; sent as `x-api-key`.                                 |
| `timeout`     | Request timeout in ms (default `10000`).                                                        |
| `baseHeaders` | Optional `{ known?, headers?, only? }` maps merged into default headers (after `Content-Type`). |
| `onError`     | Optional callback invoked before throwing `RoutingEngineAPIError`.                              |

Unsupported: **`baseURL`** — the SDK always uses `DEFAULT_API_BASE_URL`.


# Get Supported Chains

Fetch the list of chains supported by the routing engine

> Fetch supported chains from Zert's backend.

### `get`

Retrieves the list of supported chains.

**Parameters**

* None.

**Returns**

A Promise that resolves to `ChainsResponse`.

* `data.chains` (Chain\[]): List of chains.
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

Each **Chain** has: `id` (e.g. `eth-mainnet`) and `name`.

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

try {
  const response = await sdk.chains.get();
  console.log(response.data.chains);
} catch (error) {
  console.error(error);
}
```


# Get Available Tokens

Fetch all available tokens across supported chains.

> Fetch all available tokens from Zert's backend across supported chains.

### `get`

Retrieves a list of all available tokens.

**Parameters**

* `params` (TokensQueryParams, optional)
  * `chainRef` (string)
  * `q` (string)
  * `limit` (number)
  * `cursor` (string)
  * `supportedOnly` (string | boolean)
  * `requireLogo` (string | boolean)
  * `sort` (string)
  * `debug` (string | boolean)

**Returns**

A Promise that resolves to `TokensResponse`.

* `data.tokens` (Token\[]): List of tokens.
* `data.nextCursor` (string, optional): Cursor for next page.
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

**Token** fields include: `chain`, `symbol`, `name`, `address` (optional), `decimals`, `mint` (optional), and `logoUrl` (optional).

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

try {
  const response = await sdk.tokens.get({
    chainRef: 'eth-mainnet',
    supportedOnly: true,
    limit: 100,
  });
  console.log(response.data.tokens);
  console.log('Next cursor:', response.data.nextCursor);
} catch (error) {
  console.error(error);
}
```


# Get Trading Pairs

Fetch trading pairs (tokenIn / tokenOut) available for swapping.

> Fetch trading pairs from Zert's backend.

### `get`

Retrieves the list of trading pairs.

**Parameters**

* `params` (PairsQueryParams, optional)
  * `chainRef` (string)
  * `tokenIn` (string)
  * `tokenOut` (string)
  * `limit` (number)
  * `cursor` (string)
  * `supportedOnly` (string | boolean)
  * `debug` (string | boolean)

**Returns**

A Promise that resolves to `PairsResponse`.

* `data.pairs` (Pair\[]): List of pairs.
* `data.nextCursor` (string, optional): Cursor for next page.
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

Each **Pair** has: `fromChain`, `toChain`, `tokenIn`, and `tokenOut`.

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

try {
  const response = await sdk.pairs.get({
    chainRef: 'eth-mainnet',
    tokenIn: 'USDC',
    tokenOut: 'USDT',
    limit: 50,
  });
  console.log(response.data.pairs);
  console.log('Next cursor:', response.data.nextCursor);
} catch (error) {
  console.error(error);
}
```


# Request a Quote

Request a quote for a swap (amount, route, and pricing).

> Request a quote for a swap from Zert's backend.

### `request`

Fetches a quote for a swap given source chain, destination chain, tokens, amount, slippage, and recipient.

**Parameters**

* `body` (QuoteRequest, required): Quote request.
  * `fromChain` (string): Source chain (e.g. `arbitrum`).
  * `toChain` (string): Destination chain.
  * `tokenIn` (string): Input token symbol or ref (e.g. `USDC`).
  * `tokenOut` (string): Output token symbol or ref (e.g. `USDT`).
  * `network` (string): Network (e.g. `mainnet`).
  * `amount` (string): Amount as string (e.g. `"100.00"`).
  * `slippageBps` (number): Slippage tolerance in basis points (e.g. `75` = 0.75%).
  * `recipient` (string): Recipient address.

**Returns**

A Promise that resolves to `QuoteResponse`.

* `data.quoteId` (string): Quote identifier.
* `data.expiresAtMs` (number): Quote expiry in milliseconds.
* `data.routes` (QuoteRoute\[]): Route options (`routeId`, `provider(s)`, `expectedAmountOut`, etc.).
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

try {
  const response = await sdk.quote.request({
    fromChain: 'arbitrum',
    toChain: 'avax',
    tokenIn: 'TBTC',
    tokenOut: 'WETH',
    network: 'mainnet',
    amount: '100.00',
    slippageBps: 75,
    recipient: '0x1234567890123456789012345678901234567890',
  });
  console.log('Quote ID:', response.data.quoteId);
  console.log('First route:', response.data.routes[0]?.routeId);
} catch (error) {
  console.error(error);
}
```


# Submit a Swap

Build and execute the public swap flow: Build -> Get signing -> Submit signed artifact.

Use this flow after `POST /quote`. The SDK exposes these methods:

* Layer-based: `sdk.swap.build`, `sdk.swap.getSigning`, `sdk.swap.submitSigning`
* Flat: `sdk.buildSwap`, `sdk.getSwapSigning`, `sdk.submitSwapSigning`

***

### Build swap

#### `build`

Calls `POST /swap/build`.

**Parameters**

* `body` (SwapBuildRequest, required)
  * `quoteId` (string, required)
  * `routeId` (string, required)
  * `recipient` (string, required)
  * `waitForTxRequestMs` (number, optional)

**Returns**

A Promise that resolves to `SwapBuildResponse`.

* `data.swapId` (string)
* `data.mode` (`"sync"` or `"async"`)
* `data.nextAction` (`"sign"` or `"poll"`)
* `data.pollAfterMs` (number, optional)
* `data.signing` (signing payload + hints)
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

***

### Get signing status

#### `getSigning`

Calls `GET /swap/{swapId}/signing`.

**Parameters**

* `swapId` (string, required)

**Returns**

A Promise that resolves to `SwapSigningResponse` with the same top-level shape as build (`swapId`, `mode`, `nextAction`, `pollAfterMs`, `signing`).

***

### Submit signed artifact

#### `submitSigning`

Calls `POST /swap/{swapId}/signing`.

**Parameters**

* `swapId` (string, required)
* `body` (SwapSubmitSigningRequest, required)
  * `rawTx` (string, required): Signed transaction artifact.

**Returns**

A Promise that resolves to `SwapSigningResponse` (typically `nextAction: "poll"` while next step is preparing).

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

try {
  const quote = await sdk.quote.request({
    fromChain: 'arbitrum',
    toChain: 'avax',
    tokenIn: 'TBTC',
    tokenOut: 'WETH',
    network: 'mainnet',
    amount: '100.00',
    slippageBps: 75,
    recipient: '0x1234567890123456789012345678901234567890',
  });

  const build = await sdk.swap.build({
    quoteId: quote.data.quoteId,
    routeId: quote.data.routes[0].routeId,
    recipient: '0x1234567890123456789012345678901234567890',
  });

  const signing = await sdk.swap.getSigning(build.data.swapId);
  if (signing.data.nextAction === 'sign') {
    // Sign with wallet/signer, then submit the signed raw transaction.
    await sdk.swap.submitSigning(build.data.swapId, { rawTx: '0xsigned_raw_tx' });
  }
} catch (error) {
  console.error(error);
}
```


# Limit Orders

Create, fetch, cancel, and list limit orders.

The Limit layer exposes:

* `POST /limit-orders/limit`
* `GET /limit-orders/limit/{limitOrderId}`
* `DELETE /limit-orders/limit/{limitOrderId}`
* `POST /limit-orders/limit/{limitOrderId}/replace`
* `GET /limit-orders/limits`

***

### Create limit order

#### `create`

Creates a limit order. You can pass an idempotency key via `requestConfig.headers` to make the request idempotent.

**Parameters**

* `body` (LimitOrderRequest, required): Limit order payload.
  * `timeInForce` (string, required): e.g. `GTC`
  * `minAmountOut` (string, required)
  * `fromChain` (string, required)
  * `toChain` (string, required)
  * `tokenIn` (string, required)
  * `tokenOut` (string, required)
  * `network` (string, required)
  * `amount` (string, required)
  * `slippageBps` (number, required)
  * `recipient` (string, required)
* `requestConfig` (object, optional): Request options.
  * `headers` (Record\<string, string>, optional): e.g. `{ 'Idempotency-Key': 'unique-key' }`.

**Returns**

A Promise that resolves to `LimitOrderCreateResponse`.

* `data.limitOrderId` (string): Limit order identifier.
* `data.status` (string): Order status.
* `data.expiresAtMs` (number): Expiry timestamp.
* `data.minAmountOut` (string): Minimum amount out.
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

try {
  const response = await sdk.limit.create(
    {
      timeInForce: 'GTC',
      minAmountOut: '1',
      fromChain: 'arbitrum',
      toChain: 'avax',
      tokenIn: 'TBTC',
      tokenOut: 'WETH',
      network: 'mainnet',
      amount: '100.00',
      slippageBps: 75,
      recipient: '0x1234567890123456789012345678901234567890',
    },
    { headers: { 'Idempotency-Key': 'my-unique-key-123' } }
  );
  console.log('Limit order ID:', response.data.limitOrderId);
  console.log('Status:', response.data.status);
} catch (error) {
  console.error(error);
}
```

***

### Get limit order

#### `get`

Fetches a single limit order by ID.

**Parameters**

* `limitOrderId` (string, required): Limit order identifier.

**Returns**

A Promise that resolves to `LimitOrderResponse`.

* `data.limitOrderId` (string): Limit order ID.
* `data.status` (string): Order status.
* `data.timeInForce` (string): Time in force.
* `data.createdAtMs`, `data.updatedAtMs`, `data.expiresAtMs` (number): Timestamps.
* `data.minAmountOut` (string): Minimum amount out.
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

```typescript
const order = await sdk.limit.get('limit-order-123');
console.log(order.data.status, order.data.minAmountOut);
```

***

### Cancel limit order

#### `cancel`

Cancels a limit order. Optionally send a body with a cancellation reason.

**Parameters**

* `limitOrderId` (string, required): Limit order identifier.
* `body` (LimitOrderCancelBody, optional): Optional payload.
  * `reason` (string, optional): Cancellation reason.
* Note: Idempotency is handled via headers on the HTTP request path in the API spec.

**Returns**

A Promise that resolves to `LimitOrderCancelResponse`.

* `data.limitOrderId` (string): Limit order ID.
* `data.status` (string): `"canceled"`.
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

```typescript
await sdk.limit.cancel('limit-order-123');
// With reason:
await sdk.limit.cancel('limit-order-123', { reason: 'User requested cancel' });
```

***

### Replace limit order

#### `replace`

Replaces an open order (`POST /limit-orders/limit/{limitOrderId}/replace`).

**Parameters**

* `limitOrderId` (string, required)
* `body` (LimitOrderReplaceRequest, required): Same shape as `LimitOrderRequest`.
* `requestConfig` (object, optional): e.g. idempotency headers.

**Returns**

A Promise that resolves to `LimitOrderReplaceResponse`.

* `data.limitOrderId` (string): New order ID.
* `data.status` (string): Usually `open`.
* `data.expiresAtMs` (number)
* `data.minAmountOut` (string)
* `data.replacesLimitOrderId` (string): Original order ID.

```typescript
const replaced = await sdk.limit.replace(
  'limit-order-123',
  {
    timeInForce: 'GTC',
    minAmountOut: '1',
    fromChain: 'arbitrum',
    toChain: 'avax',
    tokenIn: 'TBTC',
    tokenOut: 'WETH',
    network: 'mainnet',
    amount: '100.00',
    slippageBps: 75,
    recipient: '0x1234567890123456789012345678901234567890',
  },
  { headers: { 'Idempotency-Key': 'replace-key-1' } }
);
console.log(replaced.data.replacesLimitOrderId, '->', replaced.data.limitOrderId);
```

***

### List limit orders

#### `list`

Lists limit orders for the authenticated owner with optional filters and pagination.

**Parameters**

* `params` (LimitOrdersListParams, optional): Query parameters.
  * `limit` (number, optional): Max number of orders to return.
  * `status` (string, optional): Filter by status.

**Returns**

A Promise that resolves to `LimitOrdersListResponse`.

* `data.limitOrders` (LimitOrderData\[]): Array of limit order objects (same shape as `get`).
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Response timestamp.

```typescript
const list = await sdk.limit.list({ limit: 20, status: 'open' });
list.data.limitOrders.forEach((order) => {
  console.log(order.limitOrderId, order.status);
});
```

***

On HTTP or API-level errors (e.g. 400, 401), the client throws `RoutingEngineAPIError`. See Error Handling.


# Get Transaction Status

Track transaction status via GET /status/transaction/{txHash}.

#### `getTransaction`

Calls `GET /status/transaction/{txHash}` with required `chainRef` query param.

**Parameters**

* `txHash` (string, required): Transaction hash.
* `params` (TransactionStatusParams, required)
  * `chainRef` (string, required): e.g. `eth-mainnet`, `solana-mainnet`, `bitcoin`.

**Returns**

A Promise that resolves to `TransactionStatusResponse`.

* `data.txHash` (string)
* `data.chain` (string)
* `data.status` (string): e.g. `pending`, `confirmed`, `failed`
* `data.confirmedAtMs` (number, optional)
* `data.error` (string, optional)
* `meta.requestId` (string)
* `meta.timestamp` (string)

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

const status = await sdk.status.getTransaction('0xhash', { chainRef: 'eth-mainnet' });
console.log(status.data.status);
```


# Check Service Health

Check service health via GET /health.

#### `get`

Calls `GET /health`.

**Parameters**

* None.

**Returns**

A Promise that resolves to `HealthResponse`.

* `data` (Record\<string, unknown>): Health payload from backend.
* `meta.requestId` (string)
* `meta.timestamp` (string)

```typescript
import { RoutingEngineSDK } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

const health = await sdk.health.get();
console.log(health.data);
```


# Error Handling for APIs

Handle API and SDK errors when using the Routing Engine SDK.

All API methods (`tokens.get`, `chains.get`, `pairs.get`, `quote.request`, `swap.build`, `swap.getSigning`, `swap.submitSigning`, `limit.*`, `status.getTransaction`, `health.get`) return Promises. On HTTP errors (4xx, 5xx) or when the API returns a body with an `error` property, the SDK throws a `RoutingEngineAPIError` instead of resolving.

### Error types

#### `RoutingEngineSDKError`

Base class for SDK errors. Use it when you want to catch any SDK-originated error.

* `message` (string): Error message.
* `cause` (unknown, optional): Original error or exception.

#### `ConfigTypeError`

Thrown in **development** when SDK config has invalid types (e.g. `timeout` not a number, `apiKey` not a string), or when unsupported options are used (e.g. `baseURL` — the SDK only calls Zert’s backend). In production, invalid values are logged and defaults are used instead. Extends `RoutingEngineSDKError`.

* `message` (string): Description of the invalid config.
* `field` (string, optional): Config key that failed validation.

#### `RoutingEngineAPIError`

Thrown when an API request fails (network error, HTTP error status, or API error payload). Extends `RoutingEngineSDKError`.

* `message` (string): Error message (from API when available).
* `statusCode` (number, optional): HTTP status code (e.g. 400, 401, 503).
* `body` (unknown, optional): Response body. When the API returns `{ error, meta }`, you can cast to `ApiErrorResponse` to read `error.code`, `error.message`, `error.details`, and `meta`.
* `cause` (unknown, optional): Underlying error (e.g. Axios error).

### API error response shape

When the server responds with an error payload, `body` matches `ApiErrorResponse`:

* `error.code` (string): Error code.
* `error.message` (string): Human-readable message.
* `error.details` (Record\<string, unknown>, optional): Additional details.
* `meta.requestId` (string): Request identifier.
* `meta.timestamp` (string): Timestamp.

### Handling errors

Check for `RoutingEngineAPIError` and use `statusCode` and `body` to handle specific cases (e.g. 401 Unauthorized, 400 Invalid request).

```typescript
import { RoutingEngineSDK, RoutingEngineAPIError, ApiErrorResponse } from 'routing-engine-sdk';

const sdk = new RoutingEngineSDK({
  api: { apiKey: 'your-api-key' },
});

try {
  const response = await sdk.quote.request({
    fromChain: 'arbitrum',
    toChain: 'avax',
    tokenIn: 'TBTC',
    tokenOut: 'WETH',
    network: 'mainnet',
    amount: '100.00',
    slippageBps: 75,
    recipient: '0x1234567890123456789012345678901234567890',
  });
  console.log(response);
} catch (e) {
  if (e instanceof RoutingEngineAPIError) {
    console.error('Status:', e.statusCode);
    console.error('Message:', e.message);
    if (e.body && typeof e.body === 'object' && 'error' in e.body) {
      const errBody = e.body as ApiErrorResponse;
      console.error('Code:', errBody.error.code);
      console.error('Details:', errBody.error.details);
      console.error('Request ID:', errBody.meta.requestId);
    }
  } else {
    throw e;
  }
}
```

### Global error callback

You can pass an `onError` callback when creating the SDK. It is invoked whenever an API error is about to be thrown.

```typescript
const sdk = new RoutingEngineSDK({
  api: {
    apiKey: 'your-api-key',
    onError: (error) => {
      if (error instanceof RoutingEngineAPIError && error.statusCode === 401) {
        // Handle unauthorized
      }
    },
  },
});
```


# Privacy policy

{% @zert-iubenda-gitbook/zert-iubenda-gitbook %}


# Terms and Conditions

{% @zert-iubenda-gitbook/zert-iubenda-terms-gitbook %}


