Place an OTC order
An OTC order is a currency exchange that needs approval before it executes. Unlike a spot trade, which executes the moment you submit it, an order is created in a pending_approval state and a second user approves or rejects it before any funds move. Use orders for larger or controlled exchanges that need a second pair of eyes.
Order vs spot tradeUse a spot trade for an instant conversion at the current rate. Use an order when the exchange needs approval first, when you want to set a target rate (a limit order), or when the proceeds settle to a bank account or wallet you've saved as a settlement instruction.
Order types
- Market order: executes at the current rate once approved.
- Limit order: waits until the rate reaches your
target_rate, then executes once approved.
Create an order
Use the Create order endpoint. Each order requires the following fields:
| Field | Type | Description |
|---|---|---|
order_type | string | market or limit |
pair | object | the currency pair, as { "base": "USD", "quote": "NGN" } |
amount | string | amount of the base currency to exchange, greater than zero |
target_rate | string | required for limit orders; the rate at which the order should execute |
side | string | optional, buy or sell. Defaults to sell |
source_type | string | optional, account or wallet: where the funds come from |
source_id | string | optional, the account or wallet ID |
destination_type | string | optional, account, wallet, or ssi: where the proceeds go |
destination_id | string | optional, the account, wallet, or settlement instruction ID |
Use Get all currency pair rates to confirm a pair is supported before you create an order.
Market order
curl --request POST \
--url https://api.gravv.xyz/v1/fx/orders \
--header 'Api-Key: <Api Key>' \
--header 'Idempotency-Key: 979879887678789_attempt_1' \
--header 'content-type: application/json' \
--data '
{
"order_type": "market",
"pair": { "base": "USD", "quote": "NGN" },
"amount": "1000",
"source_type": "account",
"source_id": "5d9e677f-f071-4881-a861-7cdcebacd9d5",
"destination_type": "ssi",
"destination_id": "a14ec356-f33d-4384-ba68-4d9bfa19765b"
}
'The order is created in pending_approval and returns its order_id:
{
"data": {
"id": "8f1d2c0e-2b7a-4f3e-9c1a-7d2b6e4f9a01",
"order_id": "OTC-1737031200-AB12",
"order_type": "market",
"side": "sell",
"pair": { "base": "USD", "quote": "NGN" },
"amount": "1000",
"amount_currency": "USD",
"converted_amount": "1550000",
"converted_currency": "NGN",
"rate": "1550",
"status": "pending_approval",
"destination_type": "ssi",
"destination_id": "a14ec356-f33d-4384-ba68-4d9bfa19765b",
"created_at": "2026-06-20T10:29:16.000Z",
"expires_at": "2026-06-20T10:44:16.000Z"
},
"error": null
}Limit order
A limit order adds a target_rate. It executes once the rate reaches that value, after approval:
curl --request POST \
--url https://api.gravv.xyz/v1/fx/orders \
--header 'Api-Key: <Api Key>' \
--header 'Idempotency-Key: 979879887678789_attempt_2' \
--header 'content-type: application/json' \
--data '
{
"order_type": "limit",
"pair": { "base": "USD", "quote": "NGN" },
"amount": "1000",
"target_rate": "1600",
"source_type": "account",
"source_id": "5d9e677f-f071-4881-a861-7cdcebacd9d5",
"destination_type": "account",
"destination_id": "c2f9f2d0-1234-4abc-9def-0123456789ab"
}
'Where proceeds settle
Set destination_type to control where the converted funds go:
account: an internal account, bydestination_id.wallet: a crypto wallet, bydestination_id.ssi: a saved settlement instruction, by its ID. Use this to settle to an external bank account or wallet you've saved ahead of time.
Order status
An order moves through these statuses:
| Status | Description |
|---|---|
pending_approval | created and waiting for a second user to approve or reject |
active | approved; the funds are charged and the order is executing |
completed | executed and settled |
rejected | a reviewer rejected the order |
cancelled | cancelled before execution |
expired | not approved before it expired |
Track an order
Check a single order with Get order using its order_id:
curl --request GET \
--url https://api.gravv.xyz/v1/fx/orders/OTC-1737031200-AB12 \
--header 'Api-Key: <Api Key>'List your orders with List orders. You can filter with status, order_type, side, search, start_date, and end_date, and page with page and items_per_page (default 20, max 100):
curl --request GET \
--url 'https://api.gravv.xyz/v1/fx/orders?status=pending_approval&items_per_page=20' \
--header 'Api-Key: <Api Key>'Cancel an order
Cancel an order that hasn't executed with Cancel order:
curl --request POST \
--url https://api.gravv.xyz/v1/fx/orders/OTC-1737031200-AB12/cancel \
--header 'Api-Key: <Api Key>'Next step
A new order sits in pending_approval until someone approves it. See Approve or reject OTC orders.
Updated 3 months ago
