Direct Transfer Flow
Make payouts using our direct transfer flow
Unlike the general transfer flow, which requires you to create recipient and sender entities separately, the direct transfer flow lets you set up both in a single request.
This flow has two key steps:
- Initiate the transfer on the transfer type.
- Verify the transfer status.
Requirements
Before you start, go through our quickstart section. It covers essential setup steps for using our APIs.
You need the following setup to follow this guide:
- API credentials (client ID and client secret) for authenticating your requests.
- A webhook URL to receive payment status updates.
- Whitelist your IP addresses
- Ensure your balance has sufficient funds. You can fund your balance using one of the following methods:
- Directly funding via collections.
- Indirect funding by converting funds from a different currency balance (i.e., wallet-to-wallet transfers).
Step 1: Initiate the Transfer
To initiate a direct transfer, send a transfer request to the create direct transfer endpointwith the following parameters:
action
: Specifies how the transfer should be processed. Accepted values are:instant
,deferred
, orscheduled
.type
: Specifies the transfer method. Available methods arebank
,mobile_moeny
, andwallet
.payment_instruction
: An object containing details of the payment, including:amount
,source_currency
,recipient
object,sender
object (optional)
Below are sample requests based on the possible transfer action
:
Instant Transfers
To transfer the funds immediately, send a request to the Create a direct transfer endpoint.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/direct-transfers' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"action": "instant",
"payment_instruction": {
"source_currency": "NGN",
"amount": {
"applies_to": "destination_currency",
"value": 50000
},
"recipient": {
"bank": {
"account_number": "0122333334",
"code": "044"
}
},
"destination_currency": "NGN"
},
"type": "bank",
"reference": "574874568ufdgjhvbjhcdbchb"
}'
You'll get a response similar to this:
{
"status": "success",
"message": "Transfer created",
"data": {
"id": "trf_pGoKPGH7rEgY4v",
"type": "bank",
"reference": "574874568ufdgjhvbjhcdbchb",
"status": "NEW",
"source_currency": "NGN",
"destination_currency": "NGN",
"amount": {
"value": 50000,
"applies_to": "destination_currency"
},
"recipient": {
"type": "bank",
"bank": {
"account_number": "0122333334",
"code": "044"
}
},
"meta": {},
"created_datetime": "2024-12-05T12:36:20.069512926Z"
}
}
Deferred Transfers
To initiate a direct transfer that will be processed later, send a request similar to the instant transfer, but set the action parameter to deferred
.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/direct-transfers' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"action": "deferred",
"payment_instruction": {
"source_currency": "NGN",
"amount": {
"applies_to": "destination_currency",
"value": 50000
},
"recipient": {
"bank": {
"account_number": "0122333334",
"code": "044"
}
},
"destination_currency": "NGN"
},
"type": "bank",
"reference": "57487456fff8ufdgjihvbjhcdbchb"
}'
You'll get a response similar to this:
{
"status":"success",
"message":"Transfer created",
"data":{
"id":"trf_Pd6KueVSrrUiBF",
"type":"bank",
"action":"deferred",
"reference":"57487456fff8ufdgjihvbjhcdbchb",
"status":"NEW",
"source_currency":"NGN",
"destination_currency":"NGN",
"amount":{
"value":50000,
"applies_to":"destination_currency"
},
"recipient":{
"type":"bank",
"id":"rcb_1IkjUDnrt1",
"name":{
"first":"Ajadi",
"last":"Jackson"
},
"currency":"NGN",
"bank":{
"account_number":"0122333334",
"code":"044"
}
},
"meta":{},
"created_datetime":"2025-06-02T10:32:22.457Z"
}
}
To complete a deferred transfer, call the update transfer endpoint, pass the transfer id
, and update the action parameter by setting action
to instant
to process the transfer or set it to close
to cancel it.
Scheduled Transfers
To schedule a transfer for a future date, set the action parameter to scheduled
and include a disburse_option
with the desired datetime and timezone.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/direct-transfers' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
--data '{
"action": "scheduled",
"disburse_option": {
"date_time": "2025-10-25 13:52:30",
"timezone": "UTC"
},
"payment_instruction": {
"source_currency": "NGN",
"amount": {
"applies_to": "destination_currency",
"value": 50000
},
"recipient": {
"bank": {
"account_number": "0122333334",
"code": "044"
}
},
"destination_currency": "NGN"
},
"type": "bank",
"reference": "57487456fffi8ufdgjhvbjhcdbchbfff"
}'
You'll get a response similar to this:
{
"status":"success",
"message":"Transfer created",
"data":{
"id":"trf_PfQSOvcuyET5Di",
"type":"bank",
"action":"scheduled",
"reference":"57487456fffi8ufdgjhvbjhcdbchbfff",
"status":"NEW",
"source_currency":"NGN",
"destination_currency":"NGN",
"amount":{
"value":50000,
"applies_to":"destination_currency"
},
"disburse_option":{
"date_time":"2025-10-25 13:52:30",
"timezone":"UTC"
},
"recipient":{
"type":"bank",
"id":"rcb_1IkjUDnrt1",
"name":{
"first":"Ajadi",
"last":"Jackson"
},
"currency":"NGN",
"bank":{
"account_number":"0122333334",
"code":"044"
}
},
"meta":{},
"created_datetime":"2025-06-02T10:51:02.222Z"
}
}
The data.status
field in the response will always return NEW
upon initiating a transfer. This indicates that the transfer has been successfully initiated, but not yet completed.
To determine the final status, you must verify the payout. See the verify transaction section for more details.
Step 2: Verify the Transfer Status
You can verify a transfer’s status to determine whether it was successful, pending, or failed. When you initiate a transfer, the response includes:
data.id
: Unique identifier for the transfer. Use this to track, manage, or update the transfer.data.status
: Current status of the transfer. When a transfer is first created, the status is alwaysNEW
. It can also change to:PENDING
: Transfer is in progress.SUCCESSFUL
: Transfer was completed successfully.FAILED
: Transfer could not be processed.
You can get the status of a transfer using any of the following options:
- Webhooks
- Callback URL
- Retrieve Transfer Endpoint
Webhooks (Recommended)
If you have webhooks enabled, Flutterwave automatically notifies your webhook URL when a transfer is completed or fails. This is the most efficient way to monitor transfer status in real time.
{
"webhook_id": "wbk_rp0bjKyAWA52ViM8xlZ0",
"timestamp": 1739877172874,
"type": "transfer.disburse",
"data": {
"id": "trf_yMZATJ11yVPNkZ",
"type": "BANK",
"source_currency": "NGN",
"destination_currency": "NGN",
"amount": 50000,
"reference": "5748745ug8ufdgjhveyebjhcdbchb",
"status": "SUCCESSFUL",
"bank": {
"account_number": "0122333334",
"code": "044",
"branch": null,
"name": null,
"routing_number": null,
"swift_code": null
},
"fee": {
"currency": "NGN",
"value": 500
},
"debit_information": {
"currency": "NGN",
"actual_debit_amount": 50000,
"rate_used": 0.00123,
"vat": 3750
},
"payment_information": {
"proof": "504828363550713897092362940989"
},
"meta": {}
}
}
Callback URL
If you provide a callback_url
when creating the transfer, Flutterwave will call this URL with details of completed or failed transfers.
{
"webhook_id": "wbk_rp0bjKyAWA52ViM8xlZ0",
"timestamp": 1739877172874,
"type": "transfer.disburse",
"data": {
"id": "trf_yMZATJ11yVPNkZ",
"type": "BANK",
"source_currency": "NGN",
"destination_currency": "NGN",
"amount": 50000,
"reference": "5748745ug8ufdgjhveyebjhcdbchb",
"status": "SUCCESSFUL",
"bank": {
"account_number": "0122333334",
"code": "044",
"branch": null,
"name": null,
"routing_number": null,
"swift_code": null
},
"fee": {
"currency": "NGN",
"value": 500
},
"debit_information": {
"currency": "NGN",
"actual_debit_amount": 50000,
"rate_used": 0.00123,
"vat": 3750
},
"payment_information": {
"proof": "504828363550713897092362940989"
},
"meta": {}
}
}
Retrieve Transfer Endpoint
You can manually check the current status of a transfer by calling the retrieve transfer endpoint and providing the transfer ID. This should be done periodically to avoid rate limiting.
curl --request GET \
--url 'https://api.flutterwave.cloud/developersandbox/transfers/{REPLACE_WITH_TRANSFER_ID}' \
--header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: {{YOUR_UNIQUE_TRACE_ID}}' \
--header 'X-Idempotency-Key: {{YOUR_UNIQUE_INDEMPOTENCY_KEY}}' \
You'll get a response similar to this:
{
"status": "success",
"message": "Transfer fetched",
"data": {
"id": "trf_ighwLOK9pHxoo9",
"type": "bank",
"action": "instant",
"reference": "5748745ug8ufdgjhveyyeebjhcdbchb",
"status": "SUCCESSFUL",
"source_currency": "NGN",
"destination_currency": "NGN",
"amount": {
"value": 50000,
"applies_to": "destination_currency"
},
"fee": {
"currency": "NGN",
"value": 500
},
"debit_information": {
"currency": "NGN",
"actual_debit_amount": 50000,
"rate_used": 0.00123,
"vat": 3750
},
"payment_information": {
"proof": "754119292904667985302775753026"
},
"callback_url": "https://webhook.site/352c4518-7797-4044-becc-669fc5b1e928",
"recipient": {
"type": "bank",
"id": "rcb_TDk7vKxUkj",
"name": {
"first": "Ajadi",
"last": "Jackson"
},
"currency": "NGN",
"bank": {
"account_number": "0122333334",
"code": "044"
}
},
"meta": {},
"created_datetime": "2025-02-18T11:20:37.282Z"
}
}
Cross-Currency Transfers
A cross-currency transfer is a financial transaction where money is sent between countries involving different currencies. In this case, the sender's currency is converted into the recipient's local currency before delivery.
Managing Payout Currency and Amount
You can specify the transfer amount in either the source currency or the destination currency. This depends on your intent:
- Use the source currency if you want to define exactly how much is deducted from your payout balance.
- Use the destination currency if you want to ensure the recipient receives a specific amount.
For example:
- If you want to pay your customer in NGN (
destination_currency
) from your NGN (source_currency
) Flutterwave account, you can either specify thesource_currency
ordestination_currency
when making your request."payment_instruction": { "source_currency": "NGN", "amount": { "applies_to": "source_currency", "value": 1000 },
"payment_instruction": { "source_currency": "NGN", "amount": { "applies_to": "destination_currency", "value": 1000 },
- If you want to pay your customer in USD (
destination_currency
) from your NGN (source_currency
) Flutterwave account, Flutterwave will convert your available NGN to USD using the current exchange rate. A request will look like this:"payment_instruction": { "source_currency": "NGN", "destination_currency": "USD", "amount": { "applies_to": "destination_currency", "value": 1000 },
Handling Transfer Limits
Transfer limits are based on the destination_currency
. If you attempt to send an amount outside the allowed range, Flutterwave returns an error like this:
{
"status": "failed",
"error": {
"type": "TRANSFER_AMOUNT_LIMIT",
"code": "228400",
"message": "Amount must be at least 100NGN.",
"validation_errors": []
}
}
For cross-currency transfers where the source_currency
differs from the destination_currency
, the source amount will be converted into the destination currency. If the converted amount exceeds or falls below the allowed limits for the destination currency, the transfer will fail.
Integration Tip
Before initiating the transfer, use the rate conversion endpoint to ensure the converted amount falls within the permitted range.
Testing Transfers
Depending on whether you want to test the successful or failed transfers in your integration, follow these steps to simulate the payment experience in test mode:
- To test the successful transfers, send a request without adding any extra headers. This is the default testing flow for all transfers.
- For failed transfers, update the charge request header with the appropriate
X-Scenario-Key
. You can find the complete list of test scenario keys in the test helpers section.
curl --request POST \
--url 'https://api.flutterwave.cloud/developersandbox/direct-transfers' \
--header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>' \
--header 'Content-Type: application/json' \
--header 'X-Trace-Id: <YOUR_UNIQUE_TRACE_ID>' \
--header 'X-Idempotency-Key: <YOUR_UNIQUE_INDEMPOTENCY_KEY>' \
--header 'X-Scenario-Key: scenario:successful' \
--data '{
"action": "instant",
"payment_instruction": {
"source_currency": "NGN",
"amount": {
"applies_to": "destination_currency",
"value": 50000
},
"recipient": {
"bank": {
"account_number": "0122333334",
"code": "044"
}
},
"destination_currency": "NGN"
},
"type": "bank",
"reference": "{{YOUR_UNIQUE_REFERENCE}}"
}'
You'll get a response like this:
{
"status": "success",
"message": "Transfer created",
"data": {
"id": "trf_pGoKPGH7rEgY4v",
"type": "bank",
"reference": "574874568ufdgjhvbjhcdbchb",
"status": "NEW",
"source_currency": "NGN",
"destination_currency": "NGN",
"amount": {
"value": 50000,
"applies_to": "destination_currency"
},
"recipient": {
"type": "bank",
"bank": {
"account_number": "0122333334",
"code": "044"
}
},
"meta": {},
"created_datetime": "2024-12-05T12:36:20.069512926Z"
}
}
Updated about 16 hours ago