How to Withdraw Money from Atlas
Step-by-step guide to sending a bank transfer payout from Atlas, including fetching bank codes, resolving account names, and initiating the transfer via the API.
Prerequisites
To follow this guide, you need the following:
- A verified Atlas account. If you don't have one, you can sign up for free and follow this guide to verify your Atlas account.
- An Atlas API Key. You can fetch your API Key from your Atlas dashboard .
- Configured and setup webhooks on Atlas. See our guide on Configuring and Verifying Atlas webhooks.
Initiating a payout
You can initiate a payout using the Create a Bank Transfer Payout endpoint. However, this endpoint requires parameters that you need to fetch from other endpoints. The parameters required by the Create a Bank Transfer Payout endpoint are shown below:
{
"account_number": string,
"bank_code": string,
"bank_name": string,
"account_name": string,
"amount": number,
"narration": string,
"type": enum,
"currency": enum,
"source_reference": string
}The bank_code and account_name need to be provided by the List Bank per Currency endpoint and the Account Name Enquiry endpoint respectively.
The source_reference is a unique identifier you assign to each payout so you can track it on your own system. You can also use it to
fetch the transaction via the Get Payout by Source Reference endpoint.
Fetching account details
To fetch the account details of the recipient account, first make a GET request to banking/banks/${currency} to get the bank code. This endpoint will return all the available banks you can withdraw to.
Then filter for your recipient's bank.
For example:
import axios from "axios";
const api = axios.create({
baseURL: process.env.API_BASE_URL || "https://atlas.tryduplo.com/api/v1/",
timeout: 10_000,
});
export async function findBankByName(currency, search) {
try {
const res = await api.get(`/banking/bank/${currency}`, {
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
});
const banks = res.data?.data ?? [];
return banks.filter((bank) =>
bank.bankName.toLowerCase().includes(search.toLowerCase()),
);
} catch {
throw new Error("Unable to fetch bank list");
}
}With the bank code, you can fetch a validated account name by making a POST request to banking/name-enquiry with the recipient's account number and the bank code you fetched.
For example:
async function resolveAccountName({ accountNumber, bankCode }) {
try {
const res = await api.post(
"/banking/name-enquiry",
{
accountNumber,
bankCode,
},
{
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
},
);
const data = res.data?.data;
return {
accountName: data.accountName,
bankCode: data.bankCode,
};
} catch {
throw new Error("Unable to resolve account name");
}
}With all the details, make a POST request to payout/bank-transfer to initiate the payout. For example:
async function initiateBankTransfer({
accountNumber,
bankName,
amount,
narration,
currency = "NGN",
sourceReference,
}) {
try {
// 1. Find bank code
const banks = await findBankByName(currency, bankName);
if (!banks.length) {
throw new Error("Bank not found");
}
const { bankCode, bankName: resolvedBankName } = banks[0];
// 2. Resolve account name
const { accountName } = await resolveAccountName({
accountNumber,
bankCode,
});
// 3. Initiate transfer
const res = await api.post(
"/payout/bank-transfer",
{
account_number: accountNumber,
bank_code: bankCode,
bank_name: resolvedBankName,
account_name: accountName,
amount,
narration,
type: "bank_transfer",
currency,
source_reference: sourceReference,
},
{
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
"Content-Type": "application/json",
},
},
);
return res.data;
} catch {
throw new Error("Unable to complete bank transfer");
}
}On a successful payout, you will receive a response similar to the one below:
{
"requestId": "96d34fbb-0589-492f-b621-f8fb8b649f8c",
"requestTimestamp": "2026-03-21 11:13:56.980",
"message": "Payout initiated successfully.",
"statusCode": 200,
"data": {
"id": "019d1019-f9b3-70d2-be9f-cef3ec5b4566",
"recipientAccountName": "Test user",
"recipientAccountNumber": "2018054387",
"recipientBankName": "Kuda Microfinance Bank",
"sourceReference": "7f436fb1-f04e-43e7-b376-13f62d129805",
"paymentChannel": "Bank Transfer",
"sessionId": null,
"parentTransactionReference": null,
"reference": "TRN_QELKDOVYYCFV",
"amount": {
"value": 100,
"currency": "NGN",
"formatted": "NGN100"
},
"balance": {
"value": 316,
"currency": "NGN",
"formatted": "NGN316"
},
"status": "Pending",
"narration": "Test Payout",
"createdAt": "2026-03-21 12:13:51",
"fee": [
{
"id": "019d1019-ff2c-70fb-af1c-6004b6d67dcb",
"recipientAccountName": "-",
"recipientAccountNumber": "-",
"recipientBankName": "Master Wallet",
"sourceReference": "7f436fb1-f04e-43e7-b376-13f62d129805",
"paymentChannel": "Fees",
"sessionId": null,
"parentTransactionReference": "TRN_QELKDOVYYCFV",
"reference": "TRN_KLXCE8TGZ0QS",
"amount": {
"value": 10,
"currency": "NGN",
"formatted": "NGN10"
},
"balance": {
"value": 306,
"currency": "NGN",
"formatted": "NGN306"
},
"status": "Pending",
"narration": "Test Payout",
"createdAt": "2026-03-21 12:13:53"
}
]
}
}You will also receive a webhook with an OUT_FLOW_PENDING_EVENT event type to confirm
that the transaction has been initiated and on completion an OUT_FLOW_SUCCESS_EVENT event to confirm that the transaction has been completed on our end. If the payout fails due to unforeseen circumstances, you will receive an OUT_FLOW_FAILED_EVENT event.
Related guides
How is this guide?
Last updated on
How to Collect Payments with Atlas
Step-by-step guide to collecting payments by initiating a checkout session via the Atlas API, redirecting customers to the hosted checkout page, and confirming payments via webhooks.
Create Virtual Accounts with Atlas
Step-by-step guide to creating a customer with a dedicated virtual account.