Exchange currencies

This recipe creates an OTC foreign exchange order, handles the permission-dependent approval path, and tracks the order until it settles or reaches another terminal state.

Before you start, you need a customer ID, a funded source account or wallet, and a destination account, wallet, or settlement instruction. Every POST request requires an Idempotency-Key; reuse a key only for an identical retry.

Flow overview

sequenceDiagram
    autonumber
    actor Client
    participant API as Gravv API
    actor Approver as Dashboard approver
    participant Settlement as Settlement rail

    Client->>API: Create OTC order
    alt Caller can approve FX orders
        API-->>Client: pending
    else Approval is required
        API-->>Client: waiting_approval
        Approver->>API: Approve or reject in dashboard
        alt Order approved
            API-->>Client: pending
        else Order rejected
            API-->>Client: rejected
        end
    end
    API->>Settlement: Execute and settle approved order
    loop Until terminal status
        Client->>API: Retrieve order
        API-->>Client: pending, active, completed, rejected, cancelled, or expired
    end

Create a market order to exchange the base currency into the quote currency. A limit order also requires target_rate.

curl --request POST \
--url https://api.gravv.xyz/v1/fx/orders \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: fx-order-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": "85034797-3ea1-4ad8-a952-becd67d74acc",
"order_type": "market",
"pair": {
"base": "USD",
"quote": "ZAR"
},
"amount": "1000",
"source_type": "account",
"source_id": "74653c7b-ae84-45ab-8085-2d2493f86d81",
"destination_type": "ssi",
"destination_id": "b23ffd0e-d0fc-432e-acc9-c396194121b3"
}'
{
"data": {
"id": "4994339f-4a19-47ef-a86b-1005eff0a4d0",
"order_id": "OTC-0VA89I",
"order_type": "market",
"pair": {
"base": "USD",
"quote": "ZAR"
},
"amount": "1000.00000000",
"converted_amount": "16242.50000000",
"rate": "16.24250000",
"status": "waiting_approval"
},
"error": null
}

The initial status is permission-dependent. A caller with FX approval authority can receive pending; otherwise the order waits in waiting_approval for a dashboard decision.

When the order is waiting_approval, an authorized user reviews it in the Gravv dashboard. Approval and rejection are human dashboard actions and are not API-key operations. Approval moves the order to pending; rejection moves it to the terminal rejected state.

Retrieve the order using its order_id.

curl --request GET \
--url https://api.gravv.xyz/v1/fx/orders/OTC-0VA89I \
--header 'Api-Key: <API_KEY>'
{
"data": {
"order_id": "OTC-0VA89I",
"status": "completed",
"amount": "1000.00000000",
"converted_amount": "16242.50000000",
"amount_currency": "USD",
"converted_currency": "ZAR"
},
"error": null
}

Poll only when your integration needs an immediate update. Otherwise, reconcile using the order ID and the status returned by the API.

See Foreign Exchange for market and limit orders, supported pairs, cancellation, and status details.

Was this page helpful?