Skip to main content
Execute trades against the Strails FX orderbook using one of three execution modes: quote-based (recommended for price certainty), direct market (best available price), or targeted (against a specific liquidity provider’s order). All trades settle through an on-chain escrow: your cNGN is locked at the time of matching and released to the counterparty only after the blockchain transaction confirms. Use the status endpoints to track each trade through its full lifecycle.

Endpoints Overview


Get Quote

Generate a price quote by matching your requested cNGN amount against live orderbook liquidity. The response includes the matched order(s), the resulting stablecoin amount you will receive, and a quoteId you can pass directly to POST /fx/trade. The quote is valid for 15 minutes.
Quotes expire exactly 15 minutes after generation, and each quoteId can be executed only once. If you pass an expired or already-used quoteId to POST /fx/trade, the request is rejected with INVALID_QUOTE. Generate a fresh quote and retry.
If no single order can fill your amount, the quote combines several orders. See How orders are matched.
string
required
Trading pair in the format CNGN-TOKEN. Accepted values: "CNGN-USDC" or "CNGN-USDT".
string
required
Trade direction. Use "sell" to exchange cNGN for the quote currency, or "buy" to acquire cNGN by spending the quote currency.
string
The cNGN amount to trade, expressed as a human-readable decimal (e.g., "50000" means 50,000 cNGN). Provide this or tokenAmount, not both. Minimum is 1,000 cNGN.
string
The pair-currency amount to trade, expressed as a human-readable decimal (e.g., "37.04" for USDC or "37.04" for USDT). Use this when you want the quote denominated in the pair token instead of cNGN. Provide this or cngnAmount, not both.
boolean
default:"false"
When true, the quote may be partially filled across multiple orderbook orders if no single order can fully satisfy the requested amount. When false (default), the quote is only generated if the full amount can be matched.

Execute Trade

Execute an FX trade. Choose from three execution modes depending on how much price certainty and targeting you need.
Before calling this endpoint, ensure the cNGN you intend to trade is held in your Fintech Smart Wallet. You can retrieve your smart wallet address from GET /getfintechwallet.

Create Market Order

Submit an immediate market order that executes at the current best available price from the orderbook. Market orders skip the quote step and settle based on your auto-signing configuration.
Market orders execute against the best available limit orders at the time of submission, combining several orders if needed (see How orders are matched). If orderbook liquidity changes between your request and matching, the final fill price may differ slightly from what you observed in the orderbook stats. Use POST /fx/quote followed by POST /fx/trade (Mode 1) if exact price certainty is required.
string
required
Trading pair. Accepted values: "CNGN-USDC" or "CNGN-USDT".
string
required
Trade direction: "sell" to exchange cNGN for the quote currency, or "buy" to acquire cNGN.
string
required
Amount of cNGN to trade, in human-readable format (e.g., "50000" = 50,000 cNGN). Your wallet must hold at least this amount.
string
On-chain address to receive the output tokens. Defaults to your registered fintech wallet if omitted.
string
Client-generated unique key to prevent duplicate order execution on retry.
boolean
default:"false"
When true, the market order may be partially filled across multiple orderbook orders if no single order can fully satisfy the requested amount. When false (default), the order is only executed if the full amount can be matched.

Get Trade Status

Retrieve the full details and current status of a single trade. Poll this endpoint to track a trade from pending through to completed or failed.
string
required
The tradeId returned when the trade was created.

List Trades

List your fintech’s FX trades with optional filters. Results are paginated using a cursor-based approach with startAfter.
string
Filter by trading pair. Accepted values: "CNGN-USDC" or "CNGN-USDT".
string
Filter by trade direction. Accepted values: "buy" or "sell". Case-insensitive.
string
Filter by trade status. Case-insensitive. See the Trade Status Lifecycle section for all accepted values.
number
Maximum number of trades to return. Defaults to 50; maximum is 100.
string
The tradeId of the last trade in the previous page. Pass this value to fetch the next page of results.

How orders are matched

Quote-based (Mode 1), direct (Mode 2), and market-order trades are matched against the orderbook in the same way:
  • Strails considers the 10 best-priced active orders on the opposite side of your trade: the lowest-priced sell orders when you buy, and the highest-priced buy orders when you sell.
  • Your amount fills those orders in price order. If the best order cannot cover the full amount, the remainder spills into the next order, so one trade can fill across several liquidity providers. Each contributing order appears in the quote’s matchedOrders array, and price is the volume-weighted average.
  • An order is skipped if the amount still to fill is below its minAmount, if it has no remaining capacity, or if the liquidity provider’s wallet does not currently hold enough funds to honour it.
  • Quotes never include your own orders.
Targeted trades (Mode 3) always execute against exactly one order.

Insufficient liquidity

Orders are combined across liquidity providers whether or not you set allowPartialFill. The flag only decides what happens when all the matched orders together still cannot cover your amount:
  • allowPartialFill: false (default): the request is rejected with NO_LIQUIDITY and no trade is created.
  • allowPartialFill: true: Strails fills what the matched orders can cover and leaves the rest unfilled. For a quote, this is decided when the quote is generated, so set the flag on POST /fx/quote.
When a request is rejected, the message explains why, for example that your amount is below the smallest available minAmount, that the matching liquidity providers’ wallets are underfunded, or that only part of the amount could be matched. Reduce your amount, split it into smaller trades, or retry when more liquidity is available.

Multi-order settlement

When a trade fills across more than one order, each order is settled as its own slice:
  • Buy trades need no MPC signature, so they move straight from locked to settling.
  • Sell trades need each liquidity provider to sign the stablecoin transfer for their own slice, within the 15-minute price lock.
If every slice settles, the trade is completed. If some slices of a sell trade settle and others fail or expire (for example, because one liquidity provider did not sign in time), the trade ends as partial: the settled slices are final, the cNGN for the unsettled slices is refunded from escrow, and the trade is flagged for review by Strails. If no slice settles, the trade is failed.

Trade Status Lifecycle

Every trade passes through the following statuses as it progresses from submission to final settlement:

Status Flow

completed, partial, failed, and expired are final. When a trade expires or fails, the escrowed cNGN is automatically released back to your wallet.

Common Errors

FX error codes

When a quote or trade is rejected by the trading engine, the specific reason is returned in data.details.errorCode. Requests that fail schema validation return response_code 01 with the field errors in data.details.details and no errorCode.
Several client-side rejections (for example SELF_TRADING_NOT_ALLOWED and AMOUNT_BELOW_MINIMUM) are returned with response_code 05 and HTTP 500. Branch on data.details.errorCode, not on the HTTP status, and do not retry these blindly.

Additional Notes

All cNGN and stablecoin amounts in request bodies use human-readable decimal notation (e.g., "50000" = 50,000 cNGN). All amounts in orderbook fields (price, minAmount, maxAmount, availableLiquidity) use the smallest unit with 6 decimal places. Divide those values by 1,000,000 to convert to human-readable form.
Quotes and trade price locks both expire after exactly 15 minutes. Structure your integration to execute the trade immediately after generating a quote, and to poll GET /fx/trade/status for settlement confirmation rather than relying on synchronous responses.