# 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zert.com/api-reference/swap/submit-swap.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
