---
title: "Foreign exchange"
description: "Choose the recipe for selling one currency for another with an OTC order: quote, order, settle to a wallet, an account or your own bank account."
---
Use these recipes to sell one currency for another with an **OTC order**. You get a quote for the amount you sell, then place the order with that quote before it expires.

**Money example.** Chidi has 1,600,000 NGN in his NGN account. You get a quote to sell 1,530,000 NGN for USDC at a rate of 0.00065, so he receives 994.5 USDC. He confirms, you place the order, and 1,530,000 NGN leaves his account at once. The order is `completed` when the 994.5 USDC reaches his USDC wallet.

## Two words used everywhere

- A **quote** is a firm price for selling an amount of one currency for another. It lasts until `expires_at`, about 5 minutes, and you can use it once.
- A **settlement instruction** is one of your business's own bank accounts or wallets. You save it once, and Gravv pays FX proceeds to it. An order uses it as `{"type": "external_account", "id": "<instruction id>"}`.

## What can be traded

| Sell, then buy | Status | Recipe |
| :-- | :-- | :-- |
| NGN to USDC, between balances a customer holds on Gravv | Available once Gravv sets an OTC rate for your business | [Convert naira to USDC](/recipes/fx/convert-naira-to-usdc) |
| USDC to NGN, paid to your own bank account | Available once Gravv sets an OTC rate for your business | [Sell USDC for naira into your bank account](/recipes/fx/sell-usdc-for-naira-into-your-bank-account) |
| USD to EUR, NGN to USD, GBP pairs and any other | Not yet available by default. Gravv sets up and prices a pair on request | [Check whether a pair can be traded](/recipes/fx/check-whether-a-pair-can-be-traded) |
| From an account whose money is held at a bank, with no wallet | Not supported | None |
| To a payee from `/v1/external-accounts` | Not supported. Proceeds go to a Gravv account or wallet, or to your own settlement instruction | None |
| "Buy exactly X", limit orders, target rates | Not supported. The quote amount is always the amount you sell | None |

## After the order

| Situation | Recipe |
| :-- | :-- |
| The quote expired, or the order call failed and you do not know if money moved | [Re-quote without selling twice](/recipes/fx/re-quote-without-selling-twice) |
| Add, switch or retire the bank accounts and wallets proceeds go to | [Keep your settlement instructions up to date](/recipes/fx/keep-your-settlement-instructions-up-to-date) |
| Follow, list or cancel orders | [Follow, list and cancel orders](/recipes/fx/follow-list-and-cancel-orders) |

Two operations are not FX orders. Moving USDT to USDC between two wallets of the same customer is a [conversion](/recipes/remittance-and-crypto/convert-usdt-and-usdc). Paying someone else in their currency is a payout, covered in [Bank payouts](/recipes/bank-payouts/overview).

## Rules every recipe relies on

- Send every request to one host, `https://api.gravv.xyz`, with the `Api-Key` header. A key that starts with `grvSec_sandbox_` reaches sandbox; any other key is live.
- Every POST needs its own new `Idempotency-Key`. A request with the same key and body replays the first response for 24 hours, **4xx responses included**. Use a new key after any 4xx and for every new quote. After a timeout or a 5xx, retry with the same key.
- Amounts in FX bodies are **strings** (`"1530000"`). A JSON number returns `invalid request encoding`.
- An order placed with an API key never waits for approval. The order is `pending`, and Gravv debits the source at once.
- FX and settlement-instruction errors are always HTTP 400 with the message in `error`, even for "not found" and outages.
- Gravv sends no webhooks for FX orders or settlement instructions, so poll the order.
- **The sandbox has no OTC rates today.** Every sandbox quote returns `quote currency pair is not supported` until support sets a rate for your sandbox business.
