---
title: "Exchange currencies"
description: "Create and track a foreign exchange order from funding through settlement."
---

# 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

```mermaid
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
```

<Steps>
<Step title="Create the order">

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

<RequestExample>
```bash title="Request"
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"
  }'
```
</RequestExample>

<ResponseExample>
```json title="Response"
{
  "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
}
```
</ResponseExample>

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.

</Step>

<Step title="Complete approval when required">

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.

</Step>

<Step title="Track execution and settlement">

Retrieve the order using its `order_id`.

<RequestExample>
```bash title="Request"
curl --request GET \
  --url https://api.gravv.xyz/v1/fx/orders/OTC-0VA89I \
  --header 'Api-Key: <API_KEY>'
```
</RequestExample>

<ResponseExample>
```json title="Response"
{
  "data": {
    "order_id": "OTC-0VA89I",
    "status": "completed",
    "amount": "1000.00000000",
    "converted_amount": "16242.50000000",
    "amount_currency": "USD",
    "converted_currency": "ZAR"
  },
  "error": null
}
```
</ResponseExample>

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

</Step>
</Steps>

See [Foreign Exchange](/platform/foreign-exchange/overview) for market and limit orders, supported pairs, cancellation, and status details.

