Swapping between Currency Pairs

Swapping between Currency Pairs

Learn how to convert funds between supported currency pairs using the Atlas Swaps API.

A swap converts funds from one currency to another at the current exchange rate and credits the destination currency to your Atlas wallet. Use swaps to move funds across borders, settle international payments, or maintain balances in multiple currencies.

Before initiating a swap, check the current rate using the Exchange Rates guide to display a live preview to your users.

Note
Currently, all swaps must originate from NGN. You cannot initiate a swap from USD, GBP, or EUR.

Initiating a swap

To initiate a swap, make a POST request to the Initiate FX Swap endpoint with the following required fields:

cURL Example

curl -X POST "https://atlas.tryduplo.com/api/v1/fx/swap" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "source_amount": 500000,
    "target_currency": "USD",
    "source_reference": "SWP_SRC_001"
  }'

IMPORTANT
Swap requests can only be initiated between 8AM and 5PM WAT (UTC+01:00). Any requests made outside the allowed window will not be processed.

On a successful request, you should receive a response similar to the one below:

JSON Example

{
  "requestId": "48ae6a61-a4e4-40af-bb53-e0ac03ed2929",
  "requestTimestamp": "2026-04-16 22:33:23.097",
  "message": "FX swap initiated successfully.",
  "statusCode": 201,
  "data": {
    "id": "019d986d-7353-725e-bbf0-9060fb2cf3c4",
    "destinationCurrency": "USD",
    "sourceCurrency": "NGN",
    "debitedAmount": {
      "value": 500000,
      "currency": "NGN",
      "formatted": "NGN500000"
    },
    "creditedAmount": {
      "value": 16666.666666666668,
      "currency": "USD",
      "formatted": "USD16666.666666667"
    },
    "status": "pending",
    "sourceReference": "SWP_SRC_001",
    "negotiatedRate": null,
    "createdAt": "2026-04-16T23:33:23+01:00"
  }
}

The debitedAmount is the amount taken from your NGN wallet. The creditedAmount is the equivalent amount credited to your destination currency wallet.

Lifecycle of an FX swap

Once a swap is initiated, Atlas will send a FX_SWAP_CREATED webhook to notify your system that the request has been received and is now sitting in the PENDING state. This gives you an early signal to update your internal records or reflect the pending state to your users.

The swap request will remain in the pending state until the admin approves the swap request. Then, it will move to the COMPLETED state, and you will receive a FX_SWAP_COMPLETED webhook event. If the swap request is rejected, which can be due to compliance reasons or market volatility, you will get a FX_SWAP_FAILED event instead.

You can view the structure of the payload and learn how to handle webhooks from Atlas in our webhook guide.

Checking swap status

After initiating a swap, use the Get FX Swap endpoint to poll for its current status and details. Pass the id returned in the data.id field of the initiation response.

cURL Example

curl -X GET "https://atlas.tryduplo.com/api/v1/fx/swap/c42fd5b5-c3cf-4c95-aa66-3e3155f62968" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Accept: application/json"

On a successful request, you should receive a response similar to the one below:

JSON Example

{
    "requestId": "31c33e10-0088-4ebb-8686-1893ee7bee21",
    "requestTimestamp": "2026-04-16 22:44:35.413",
    "message": "FX swap fetched successfully.",
    "statusCode": 200,
    "data": {
        "id": "019d986d-7353-725e-bbf0-9060fb2cf3c4",
        "destinationCurrency": "USD",
        "sourceCurrency": "NGN",
        "debitedAmount": {
            "value": 500000,
            "currency": "NGN",
            "formatted": "NGN500000"
        },
        "creditedAmount": {
            "value": 16666.666666667,
            "currency": "USD",
            "formatted": "USD16666.666666667"
        },
        "status": "pending",
        "sourceReference": "SWP_SRC_001",
        "negotiatedRate": null,
        "createdAt": "2026-04-16T23:33:23+01:00"
    }
}

Listing your swap history

Once you've been running swaps for a while, you'll need a way to review past activity: whether to audit recent conversions, surface transaction history for your users, or investigate a specific swap. The List FX Swaps endpoint returns a paginated list of all swap requests on your account.

You can narrow results using the following query parameters:

All parameters are optional. Omitting them returns all swaps in reverse chronological order.

cURL Example

curl -X GET "https://atlas.tryduplo.com/api/v1/fx/swaps?status=completed&currency=USD&from_date=2026-02-01&to_date=2026-02-28&page=1&limit=10" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Accept: application/json"

On a successful request, you should receive a response similar to the one below:

JSON Example

{
    "requestId": "45e55bb7-72d4-4e0c-95ed-e58773bc6d22",
    "requestTimestamp": "2026-04-16 22:52:40.874",
    "message": "FX swaps fetched successfully.",
    "statusCode": 200,
    "data": [
        {
            "id": "019d986d-7353-725e-bbf0-9060fb2cf3c4",
            "destinationCurrency": "USD",
            "sourceCurrency": "NGN",
            "debitedAmount": {
                "value": 500000,
                "currency": "NGN",
                "formatted": "NGN500000"
            },
            "creditedAmount": {
                "value": 16666.666666667,
                "currency": "USD",
                "formatted": "USD16666.666666667"
            },
            "status": "pending",
            "sourceReference": "SWP_SRC_001",
            "negotiatedRate": null,
            "createdAt": "2026-04-16T23:33:23+01:00"
        },
        {
            "id": "019d9872-0498-72c6-b234-995e53b91690",
            "destinationCurrency": "GBP",
            "sourceCurrency": "NGN",
            "debitedAmount": {
                "value": 400000,
                "currency": "NGN",
                "formatted": "NGN400000"
            },
            "creditedAmount": {
                "value": 8000,
                "currency": "GBP",
                "formatted": "GBP8000"
            },
            "status": "pending",
            "sourceReference": "SWP_SRC_001",
            "negotiatedRate": null,
            "createdAt": "2026-04-16T23:38:22+01:00"
        },
        {
            "id": "019d9872-a593-7171-a7a2-0a599d64e118",
            "destinationCurrency": "EUR",
            "sourceCurrency": "NGN",
            "debitedAmount": {
                "value": 300000,
                "currency": "NGN",
                "formatted": "NGN300000"
            },
            "creditedAmount": {
                "value": 6666.6666666667,
                "currency": "EUR",
                "formatted": "EUR6666.6666666667"
            },
            "status": "pending",
            "sourceReference": "SWP_SRC_001",
            "negotiatedRate": null,
            "createdAt": "2026-04-16T23:39:03+01:00"
        },
        {
            "id": "019d9874-262a-7184-9a7b-f976ed84e18e",
            "destinationCurrency": "EUR",
            "sourceCurrency": "NGN",
            "debitedAmount": {
                "value": 30,
                "currency": "NGN",
                "formatted": "NGN30"
            },
            "creditedAmount": {
                "value": 0.66666666666667,
                "currency": "EUR",
                "formatted": "EUR0.66666666666667"
            },
            "status": "pending",
            "sourceReference": "SWP_SRC_001",
            "negotiatedRate": null,
            "createdAt": "2026-04-16T23:40:42+01:00"
        }
    ],
    "links": {
        "first": "http://atlas.tryduplo.com/api/v1/fx/swaps?page=1",
        "last": "http://atlas.tryduplo.com/api/v1/fx/swaps?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "page": null,
                "active": false
            },
            {
                "url": "http://atlas.tryduplo.com/api/v1/fx/swaps?page=1",
                "label": "1",
                "page": 1,
                "active": true
            },
            {
                "url": null,
                "label": "Next &raquo;",
                "page": null,
                "active": false
            }
        ],
        "path": "http://atlas.tryduplo.com/api/v1/fx/swaps",
        "per_page": 10,
        "to": 4,
        "total": 4
    }
}

Reconciling swap transactions

If you're building a reconciliation flow that needs to cover multiple transaction types (payouts, collections, and swaps) in a single view, the List Transactions endpoint with channel=swap lets you pull swap activity in the same format as your other transactions.

cURL Example

curl -X GET "https://atlas.tryduplo.com/api/v1/transaction?channel=swap&page=1&limit=10" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Accept: application/json"

On a successful request, you should receive a response similar to the one below:

JSON Example

{
  "requestId": "e6f7a8b9-c0d1-2345-abcd-ef6789012345",
  "requestTimestamp": "2026-02-28 14:30:00.000",
  "message": "Transactions retrieved successfully.",
  "statusCode": 200,
  "data": {
    "items": [
      {
        "id": "TXN_abc123def456",
        "channel": "swap",
        "reference": "d52ge6c6-d4dg-5d06-bb77-4f4266g73079",
        "sourceReference": "SWP_SRC_002",
        "fromCurrency": "NGN",
        "toCurrency": "USD",
        "amount": 1000000,
        "convertedAmount": 648.5,
        "status": "completed",
        "createdAt": "2026-04-14T09:45:12+00:00"
      },
      {
        "id": "TXN_789xyz012abc",
        "channel": "swap",
        "reference": "c42fd5b5-c3cf-4c95-aa66-3e3155f62968",
        "sourceReference": "SWP_SRC_001",
        "fromCurrency": "NGN",
        "toCurrency": "USD",
        "amount": 500000,
        "convertedAmount": 324.25,
        "status": "completed",
        "createdAt": "2026-04-10T10:15:30+00:00"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 10,
      "total": 2,
      "totalPages": 1
    }
  }
}

Each item includes a reference field that maps directly to the swap id from the FX swap endpoints, so you can cross-reference records between the two.

Fetching wallet balances

After a swap has been successfully processed, the swapped amount settles in the wallet with the corresponding destination currency. You can view the wallet balances of all available currencies using the FX Wallet Balances endpoint.

cURL Example

curl -X GET "https://atlas.tryduplo.com/api/v1/fx/wallet/balances" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Accept: application/json"

On a successful request, you should receive a response similar to the one below:

JSON Example

{
    "requestId": "34ec2faa-81c0-41e1-b5fd-f11a5a417cfe",
    "requestTimestamp": "2026-04-16 23:06:50.663",
    "message": "FX wallet balances fetched successfully.",
    "statusCode": 200,
    "data": [
        {
            "currency": "NGN",
            "ledgerBalance": {
                "value": 25199720,
                "currency": "NGN",
                "formatted": "NGN25199720"
            },
            "availableBalance": {
                "value": 25199720,
                "currency": "NGN",
                "formatted": "NGN25199720"
            },
            "businessState": "live"
        },
        {
            "currency": "EUR",
            "ledgerBalance": {
                "value": 0,
                "currency": "EUR",
                "formatted": "EUR0"
            },
            "availableBalance": {
                "value": 0,
                "currency": "EUR",
                "formatted": "EUR0"
            },
            "businessState": "live"
        },
        {
            "currency": "USD",
            "ledgerBalance": {
                "value": 0,
                "currency": "USD",
                "formatted": "USD0"
            },
            "availableBalance": {
                "value": 0,
                "currency": "USD",
                "formatted": "USD0"
            },
            "businessState": "live"
        },
        {
            "currency": "GBP",
            "ledgerBalance": {
                "value": 0,
                "currency": "GBP",
                "formatted": "GBP0"
            },
            "availableBalance": {
                "value": 0,
                "currency": "GBP",
                "formatted": "GBP0"
            },
            "businessState": "live"
        }
    ]
}