Place an OTC order

An OTC order exchanges one currency for another and settles the proceeds to an account, wallet, or saved settlement instruction. Its initial status depends on the caller's permissions: pending when the caller can approve FX orders, or waiting_approval when a dashboard decision is required.

📘 Order vs spot trade

Use a market order for the current available rate. Use a limit order when execution should wait for a target rate. Either order can settle to an account, wallet, or saved settlement instruction.

Order types

  • Market order: executes at the current rate after any required approval.
  • Limit order: waits until the rate reaches your target_rate and any required approval is complete.

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 returns its order_id and its permission-dependent initial status:

{
  "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": "waiting_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, by destination_id.
  • wallet: a crypto wallet, by destination_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
waiting_approval waiting for an authorized dashboard user to approve or reject
pending accepted and queued for execution
active 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=waiting_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>' \
     --header 'Idempotency-Key: fx-cancel-<UUID>'

Next step

If the order returns waiting_approval, see Approve or reject OTC orders. Otherwise, track it from pending through execution and settlement.

Was this page helpful?