---
title: "Create a firm FX quote"
method: POST
path: "/v1/fx/quotes"
tags: ["FX"]
---

# Create a firm FX quote

`POST /v1/fx/quotes`

Always firm — consumes one vendor quote and persists a Quote row. Pass an `Idempotency-Key` header to make retries safe (returns the original response on replay).

## Headers

- `X-API-Key` string
- `Idempotency-Key` string

## Request body

- CreateFxQuoteDto
  - `onBehalfOf` string, required — Customer this quote is for (the customer who will deposit).
  - `pair` FxPairDto, required
    - `sourceCurrency` string, required
    - `sourceNetwork` string — Network for crypto source (e.g. BASE, ETHEREUM). Required when source is a crypto asset; must be omitted when source is fiat.
    - `sourceRail` string — Rail for fiat source (SEPA, SEPA_INSTANT, FASTER_PAYMENTS). Required when source is fiat; must be omitted when source is crypto. For EUR onramp prefer `SEPA`; `SEPA_INSTANT` remains accepted. Slow EUR rails are not available today.
    - `destinationCurrency` string, required
    - `destinationNetwork` string — Network for crypto destination. Required when destination is a crypto asset; must be omitted when destination is fiat.
    - `destinationRail` string — Rail for fiat destination (SEPA, SWIFT, ACH). Required when destination is fiat; must be omitted when destination is crypto.
  - `amount` FxAmountDto, required
    - `value` string, required — Amount in minor units of whichever side is locked. Positive integer string.
    - `side` 'SOURCE' | 'DESTINATION', required — Which amount the customer locked. SOURCE = customer's gross spend; DESTINATION = customer's receivable.
  - `maxSlippageBps` number — Per-trade max slippage tolerance on the re-quote step, in basis points (1 bp = 0.01%). Minimum 5, maximum 1000. Optional for stable sources — if omitted, the organization-level default for the pair applies. REQUIRED for volatile sources (e.g. BTC): a quote whose source can move materially across the deposit-confirmation window is rejected when this is omitted, so the tolerance the customer accepts is always explicit.

## Response `201`

Firm quote created.

- FxQuoteResponseDto
  - `id` string, required
  - `pair` FxPairDto, required
    - `sourceCurrency` string, required
    - `sourceNetwork` string — Network for crypto source (e.g. BASE, ETHEREUM). Required when source is a crypto asset; must be omitted when source is fiat.
    - `sourceRail` string — Rail for fiat source (SEPA, SEPA_INSTANT, FASTER_PAYMENTS). Required when source is fiat; must be omitted when source is crypto. For EUR onramp prefer `SEPA`; `SEPA_INSTANT` remains accepted. Slow EUR rails are not available today.
    - `destinationCurrency` string, required
    - `destinationNetwork` string — Network for crypto destination. Required when destination is a crypto asset; must be omitted when destination is fiat.
    - `destinationRail` string — Rail for fiat destination (SEPA, SWIFT, ACH). Required when destination is fiat; must be omitted when destination is crypto.
  - `amount` FxAmountDto, required
    - `value` string, required — Amount in minor units of whichever side is locked. Positive integer string.
    - `side` 'SOURCE' | 'DESTINATION', required — Which amount the customer locked. SOURCE = customer's gross spend; DESTINATION = customer's receivable.
  - `grossSource` string, required — Source amount, fees included, in source-currency minor units.
  - `displayedDestination` string, required — Destination amount the customer will receive, in destination-currency minor units.
  - `rate` string, required — All-in customer rate (displayedDestination / grossSource).
  - `expiresAt` string, required — When the firm quote expires (ISO 8601).
  - `quoteBinding` 'PERSISTED' | 'AT_ACCEPT' — How this quote binds to a later trade. `PERSISTED`: the firm price is held and a trade created from this quote replays it, so accepting later (within `expiresAt`) is safe. `AT_ACCEPT`: pricing is finalized when the trade is created, so the price may move between quote and accept.
  - `expectedDepositDetails` FxQuoteExpectedDepositDetailsDto
    - `kind` 'CRYPTO_ADDRESS' | 'FIAT_INSTRUCTIONS_ON_ACCEPT', required — How the source side will be funded after accepting. CRYPTO_ADDRESS: the deposit address is known now (`address` + `network`). FIAT_INSTRUCTIONS_ON_ACCEPT: bank wire instructions are issued on the trade returned by accepting the quote.
    - `address` string — Deposit address the trade will use. Present only when `kind=CRYPTO_ADDRESS`. NOT active until the trade returned by accepting this quote carries deposit instructions — see `note`.
    - `network` string — Network of `address`. Present only when `kind=CRYPTO_ADDRESS`.
    - `note` string, required — Pre-accept warning to surface alongside the preview.

## Other responses

- `400` — Invalid pair shape (mismatched network/rail slots), or the fiat source rail is not yet supported (`fx_source_rail_not_supported` — send `SEPA` or `SEPA_INSTANT` for EUR onramp or `FASTER_PAYMENTS` for GBP; slow rails such as SWIFT are not available today).
- `401` — Unauthorized - Invalid or missing authentication credentials
- `403` — Fiat pairs are not enabled (`fx_fiat_pairs_not_enabled`).
- `409` — FX is not enabled for this organization on the given pair.
- `422` — No executable quote could be produced for the pair (`fx_quote_capacity_exhausted`, `fx_quote_amount_too_small`, `fx_quote_amount_too_large`, or `fx_quote_temporarily_unavailable`). For the amount buckets the body includes a `hint` and, when available, `limits` (`sourceCurrency`, `min`, `max` in minor units) plus the `requested` amount. Per-corridor amount limits are environment-specific — sandbox uses small test limits, so amounts that quote in production may be rejected in sandbox.

---

[API](https://skmtc.dev/infinite/apis/infinite-api.md) · [All operations](https://skmtc.dev/infinite/apis/infinite-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/infinite/infinite-api/revisions/5426f2706725/schema)
