> For the complete documentation index, see [llms.txt](https://docs.yifi.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.yifi.io/api-reference/swap.md).

# Swap

Create a swap order (`POST /v1/swap`)

## Create a swap order

> Creates an order with the chosen provider. \*\*No JWT is required.\*\* Optional header \`x-api-key-partner\` attributes the order to a partner; an invalid key returns \`401\` \`INVALID\_API\_KEY\`.\
> \
> NestJS default status for this \`POST\` is \*\*\`201 Created\`\*\*.\
> \
> \*\*\`engine\`\*\* (must be one of \`cex\` | \`dex\` | \`private\`):\
> \
> \- \`cex\` — off-chain. Send funds to \`depositAddress\` (memo: \`extraIdDeposit\` when present).\
> \- \`dex\` — on-chain. \`depositAddress\` is typically \`null\`; follow \`txCreateMeta\` / provider payload.\
> \- \`private\` — two-rail. \*\*No Bearer token is checked.\*\* Send funds to \`depositAddress\`. \`quoteId\` is passed to the private workflow as \`routeId\`.\
> \
> \*\*Rate snapshot (\`rateId\`)\*\* — UUID from \`GET /v1/quote\`. If sent:\
> \
> \- missing/expired snapshot → \`400\` \`RATE\_EXPIRED\` (TTL 5 minutes)\
> \- provider / coin UUIDs / amount do not match the snapshot → \`400\` \`RATE\_MISMATCH\` (\`error.details\` lists expected vs received)\
> \
> \`quoteId\` is the \*\*provider\*\* quote id (rate lock) and is independent of \`rateId\`.\
> \
> \`receiveAddress\` is validated against the destination token \`regexAddress\` when the catalog has one (\`INVALID\_RECEIVE\_ADDRESS\`). \`refundAddress\` is validated against the source token (\`INVALID\_REFUND\_ADDRESS\`).\
> \
> \`addressFrom\` is accepted for backward compatibility and \*\*ignored\*\*.\
> \
> \`transactionId\` in the response is the \*\*YiFi UUID\*\* stored in admin-service (use it on \`GET /v1/transactions/{id}\`). \`externalTransactionId\` is the provider id when it differs. \`accessToken\` is issued once — store it client-side.

```json
{"openapi":"3.0.0","info":{"title":"YiFi Swap Public API","version":"1.0"},"tags":[{"name":"Swap","description":"Create a swap order (`POST /v1/swap`)"}],"servers":[{"url":"https://api.yifi.io","description":"Production"},{"url":"https://api-dev.yifi.io","description":"Development / Staging"}],"security":[{"partnerApiKey":[]}],"components":{"securitySchemes":{"partnerApiKey":{"type":"apiKey","in":"header","name":"x-api-key-partner","description":"Optional partner API key on POST /v1/swap. Invalid key → 401. Omitted → anonymous swap."}},"schemas":{"CreateSwapDto":{"type":"object","properties":{"provider":{"type":"string","description":"Provider slug from the chosen quote's `provider` field"},"fromToken":{"type":"string","description":"Source token ticker"},"fromNetwork":{"type":"string","description":"Source chain slug — same as `GET /v1/chains` → `id` (case-insensitive)"},"toToken":{"type":"string","description":"Target token ticker"},"toNetwork":{"type":"string","description":"Target chain slug — same as `GET /v1/chains` → `id` (case-insensitive)"},"engine":{"type":"string","description":"Swap engine to use. Must match the engine from the selected quote.\n\n- `cex` – off-chain CEX swap\n\n- `dex` – on-chain DEX swap\n\n- `private` – two-rail private swap (no JWT required on this API)","enum":["cex","dex","private"]},"amount":{"type":"number","description":"Amount to exchange in source token units","minimum":1e-8},"quoteId":{"type":"string","description":"Quote ID from `/v1/quote`. Locks in the rate with providers that support it."},"rateId":{"type":"string","description":"Internal rate ID from `/v1/quote` response. Use to record the exact quote shown to the user in the transaction history. The snapshot expires 5 minutes after the quote was issued. If you pass an expired or unknown ID the request will be rejected with `RATE_EXPIRED`."},"receiveAddress":{"type":"string","description":"Destination wallet address for the output token"},"addressFrom":{"type":"string","description":"Legacy field. Accepted (not rejected by validation) but **not used** by `createSwap`."},"slippageTolerance":{"type":"string","description":"Slippage tolerance in percent. Default `\"0.5\"`. **Applies to DEX routes only** (`engine=dex`). Ignored for CEX and private.","default":"0.5"},"extraIdTo":{"type":"string","description":"Memo / tag for the destination address. Required for tokens that need it (e.g. XRP, XLM, TON)."},"refundAddress":{"type":"string","description":"Address to receive a refund if the swap fails. Recommended when the chosen provider or route requires it — follow provider documentation and support guidance. Strongly recommended for private swaps."},"refundExtraId":{"type":"string","description":"Memo / tag for the refund address, if required by the source network"},"ref":{"type":"string","description":"Partner affiliate referral code (alias of `refCode`). Optional: without a code the swap is created as usual. Attributes the swap to the partner via their affiliate referral node."},"refCode":{"type":"string","description":"Partner affiliate referral code. Attributes the swap to the partner via their affiliate referral node. Optional. If both `ref` and `refCode` are sent, `ref` wins. An unknown code is rejected with `REFERRAL_CODE_NOT_FOUND`."}},"required":["provider","fromToken","fromNetwork","toToken","toNetwork","engine","amount","receiveAddress"]},"SwapResponseDto":{"type":"object","properties":{"fromToken":{"type":"string","description":"Source token ticker"},"fromNetwork":{"type":"string","description":"Source chain slug as sent in the request"},"toToken":{"type":"string","description":"Target token ticker"},"toNetwork":{"type":"string","description":"Target chain slug as sent in the request"},"provider":{"type":"string","description":"Provider slug executing this swap"},"transactionId":{"type":"string","description":"YiFi transaction ID. Use this to poll `/v1/transactions/{id}` for status updates."},"accessToken":{"type":"string","description":"Issued once at swap creation. Store securely client-side (used for private flows and client-side transaction operations)."},"externalTransactionId":{"type":"string","description":"Provider-side transaction id when it differs from the YiFi UUID in `transactionId`."},"status":{"type":"string","description":"Initial swap status – always `pending` at the moment of creation","enum":["pending","processing","completed","failed"]},"statusList":{"type":"object","description":"Granular step-by-step status map. All values are `false` at creation. Keys vary by provider and engine."},"receiveAmount":{"type":"string","description":"Expected output amount the recipient will receive"},"expirationTime":{"type":"object","description":"Order expiration from provider response (JSON path configured as expirationTimePath on create-exchange endpoint)","nullable":true},"estimatedOutput":{"type":"string","description":"Legacy: expected output amount (prefer `receiveAmount`)"},"estimatedTime":{"type":"string","description":"Expected time to complete the swap"},"depositAddress":{"type":"object","description":"Address to send source funds to. Present for CEX and private routes. `null` for DEX routes where funds are pulled via smart contract.","nullable":true},"extraIdTo":{"type":"string","description":"Memo / tag for the destination address, if required"},"refundAddress":{"type":"string","description":"Address that will receive funds if the swap fails"},"refundExtraId":{"type":"string","description":"Memo / tag for the refund address, if required"},"txCreateMeta":{"type":"object","description":"Raw transaction metadata returned by the provider at creation (e.g. `hash`, `nonce`). May be a JSON string.","nullable":true},"extraIdDeposit":{"type":"string","description":"Memo / tag for the deposit address, if required by the source network"},"tokenForTx":{"type":"string","description":"Ticker of the token as returned by the provider's internal `createExchange` response. May differ from `fromToken` in multi-hop DEX routes."}},"required":["fromToken","fromNetwork","toToken","toNetwork","provider","transactionId","accessToken","status","receiveAmount","estimatedTime"]}}},"paths":{"/v1/swap":{"post":{"description":"Creates an order with the chosen provider. **No JWT is required.** Optional header `x-api-key-partner` attributes the order to a partner; an invalid key returns `401` `INVALID_API_KEY`.\n\nNestJS default status for this `POST` is **`201 Created`**.\n\n**`engine`** (must be one of `cex` | `dex` | `private`):\n\n- `cex` — off-chain. Send funds to `depositAddress` (memo: `extraIdDeposit` when present).\n- `dex` — on-chain. `depositAddress` is typically `null`; follow `txCreateMeta` / provider payload.\n- `private` — two-rail. **No Bearer token is checked.** Send funds to `depositAddress`. `quoteId` is passed to the private workflow as `routeId`.\n\n**Rate snapshot (`rateId`)** — UUID from `GET /v1/quote`. If sent:\n\n- missing/expired snapshot → `400` `RATE_EXPIRED` (TTL 5 minutes)\n- provider / coin UUIDs / amount do not match the snapshot → `400` `RATE_MISMATCH` (`error.details` lists expected vs received)\n\n`quoteId` is the **provider** quote id (rate lock) and is independent of `rateId`.\n\n`receiveAddress` is validated against the destination token `regexAddress` when the catalog has one (`INVALID_RECEIVE_ADDRESS`). `refundAddress` is validated against the source token (`INVALID_REFUND_ADDRESS`).\n\n`addressFrom` is accepted for backward compatibility and **ignored**.\n\n`transactionId` in the response is the **YiFi UUID** stored in admin-service (use it on `GET /v1/transactions/{id}`). `externalTransactionId` is the provider id when it differs. `accessToken` is issued once — store it client-side.","operationId":"createSwap","parameters":[],"requestBody":{"required":true,"description":"Create swap request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSwapDto"}}}},"responses":{"201":{"description":"Order created (YiFi UUID + access token + deposit details)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapResponseDto"}}}},"400":{"description":"Validation error, `RATE_EXPIRED`, `RATE_MISMATCH`, `INVALID_RECEIVE_ADDRESS`, or `INVALID_REFUND_ADDRESS`","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable code (`COIN_NOT_FOUND`, `RATE_EXPIRED`, `INVALID_API_KEY`, `HTTP_EXCEPTION` for class-validator, …)"},"message":{"description":"Human-readable message. Class-validator 400s may return a string **array** of constraint texts.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"details":{"description":"Present for some validation (4xx) errors when thrown with `details`.","oneOf":[{"type":"object","additionalProperties":true},{"type":"array"}]}},"required":["code","message","requestId","timestamp"]}}}}}},"401":{"description":"Header `x-api-key-partner` was sent but the key is invalid (`INVALID_API_KEY`)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable code (`COIN_NOT_FOUND`, `RATE_EXPIRED`, `INVALID_API_KEY`, `HTTP_EXCEPTION` for class-validator, …)"},"message":{"description":"Human-readable message. Class-validator 400s may return a string **array** of constraint texts.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"details":{"description":"Present for some validation (4xx) errors when thrown with `details`.","oneOf":[{"type":"object","additionalProperties":true},{"type":"array"}]}},"required":["code","message","requestId","timestamp"]}}}}}},"404":{"description":"`COIN_NOT_FOUND` or `PROVIDER_NOT_FOUND` (CEX/DEX only)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable code (`COIN_NOT_FOUND`, `RATE_EXPIRED`, `INVALID_API_KEY`, `HTTP_EXCEPTION` for class-validator, …)"},"message":{"description":"Human-readable message. Class-validator 400s may return a string **array** of constraint texts.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"details":{"description":"Present for some validation (4xx) errors when thrown with `details`.","oneOf":[{"type":"object","additionalProperties":true},{"type":"array"}]}},"required":["code","message","requestId","timestamp"]}}}}}},"408":{"description":"`WORKFLOW_TIMEOUT` (50s create-swap workflow)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable code (`COIN_NOT_FOUND`, `RATE_EXPIRED`, `INVALID_API_KEY`, `HTTP_EXCEPTION` for class-validator, …)"},"message":{"description":"Human-readable message. Class-validator 400s may return a string **array** of constraint texts.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"details":{"description":"Present for some validation (4xx) errors when thrown with `details`.","oneOf":[{"type":"object","additionalProperties":true},{"type":"array"}]}},"required":["code","message","requestId","timestamp"]}}}}}},"500":{"description":"`SWAP_ERROR`, `CREATE_TRANSACTION_FAILED`, or missing DEX/private task queue","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable code (`COIN_NOT_FOUND`, `RATE_EXPIRED`, `INVALID_API_KEY`, `HTTP_EXCEPTION` for class-validator, …)"},"message":{"description":"Human-readable message. Class-validator 400s may return a string **array** of constraint texts.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"details":{"description":"Present for some validation (4xx) errors when thrown with `details`.","oneOf":[{"type":"object","additionalProperties":true},{"type":"array"}]}},"required":["code","message","requestId","timestamp"]}}}}}}},"summary":"Create a swap order","tags":["Aggregator","Swap"]}}}}
```
