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:

  • source_amount: The amount in NGN to swap. The minimum amount you can swap for each currency pair is 100 of the target currency.
  • target_currency: The destination currency (USD, GBP, or EUR).
  • source_reference: A unique string you assign to identify this swap on your system.
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
{
  "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 -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
{
    "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:

  • status - show only swaps with a specific state (completed, pending, or failed).
  • currency - show only swaps that converted to a particular destination currency (USD, GBP, or EUR).
  • from_date - the start of the date range to filter by (format: YYYY-MM-DD).
  • to_date - the end of the date range to filter by (format: YYYY-MM-DD).
  • page - the page number to retrieve (default: 1).
  • limit - the number of results per page (default: 10).

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

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
{
    "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. This way you can generate unified statements or match records against your internal ledger without making separate calls to different endpoints.

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
{
  "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 -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
{
    "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"
        }
    ]
}

How is this guide?

Last updated on

On this page