Make Payout
Send a disbursement to a mobile money wallet or bank account
makePayout(request)
Initiates a payout and returns a PaymentInstance immediately. Use the event listeners to track status changes in real time. A payout moves funds from your Nylon Pay balance to an external mobile money wallet or bank account.
const payout = await nylonpay.makePayout(request);Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | yes | Payout amount in smallest currency unit. Minimum 5,000 UGX for mobile money, 50,000 UGX for bank transfers |
currency | Currency | yes | ISO 4217 currency code (e.g., "UGX") |
description | string | yes | Payout description |
customer.name | string | yes | Recipient full name |
customer.phoneNumber | string | yes | Recipient phone number. Any common format accepted, auto-normalized to the market's calling code (256… for UGX, 254… for KES). See Phone Number Format |
customer.email | string | no | Recipient email address |
destination.accountHolderName | string | yes | Name on the receiving account |
destination.accountNumber | string | yes | Mobile money number or bank account number |
destination.bankName | string | no | Bank name, required for bank account payouts |
destination.phone | string | yes | Recipient phone number, required for both mobile money and bank payouts. Any common format accepted, auto-normalized |
reference | string | no | Unique reference (any valid UUID). Auto-generated as UUID v4 if omitted |
metadata | Record<string, string> | no | Custom key-value pairs attached to the transaction |
tags | string[] | no | Up to 10 business labels for filtering. See Smart Tags |
testOutcome | "success", "fail", or a Nylon failure code | no | Force the sandbox result while testing. Omit it and the sandbox picks success about 70% of the time. Sandbox keys only |
prompt | "pin" | no | Keep the sandbox payout pending until you enter the customer PIN. See Testing the customer PIN. Sandbox keys only |
Example
import { createNylonPay } from '@nile-squad/nylonpay-ts';
const nylonpay = createNylonPay({
apiKey: 'npk_test_...',
apiSecret: 'nps_test_...',
onError: (error) => {
if (error.reason === 'SERVICES_DOWN') {
console.error('Pause payout attempts:', error.message);
}
},
});
const payout = await nylonpay.makePayout({
amount: 50000,
currency: 'UGX',
description: 'Supplier payment #4471',
customer: {
name: 'Jane Doe',
phoneNumber: '+256700000000',
},
destination: {
accountHolderName: 'Jane Doe',
accountNumber: '1234567890',
phone: '+256700000000',
},
reference: '550e8400-e29b-41d4-a716-446655440000', // UUID (any version accepted)
});
payout.on('success', ({ transaction }) => {
console.log('Payout delivered:', transaction.id);
});Forcing the sandbox result
While testing with a sandbox key, pass testOutcome to force a result. Use "success" to walk the happy path and "fail" or a code such as "insufficient_balance" to walk the failure path, including webhooks and your event handlers. Forced fails are labelled Sandbox simulated:. Omit it and the sandbox succeeds about 70% of the time.
Sending testOutcome with a live key is rejected as a validation error, so remove it before going live.
const payout = await nylonpay.makePayout({
amount: 50000,
currency: 'UGX',
description: 'Supplier payment #4471',
customer: { name: 'Jane Doe', phoneNumber: '+256700000000' },
destination: {
accountHolderName: 'Jane Doe',
accountNumber: '1234567890',
phone: '+256700000000',
},
testOutcome: 'fail',
});
payout.on('failed', ({ error }) => {
console.log('Payout failed as forced:', error);
});Testing the customer PIN
Pass prompt: 'pin' with a sandbox key to test the step where the customer enters a PIN. The payout stays pending until you type the customer PIN on the phone in the dashboard Sandbox page. The PIN is 5 digits and appears above the phone. Use the Set PIN button to change it. Every sandbox payout from your merchant accepts the same PIN, whichever sandbox key made the payout.
The customer gets 3 attempts. Three wrong PINs fail the payout. No PIN within 2 minutes fails it too. After the correct PIN, the payout finishes and your webhook is sent as usual. The makePayoutAndResolve call can wait up to about 2 minutes while the PIN is pending.
The phone's Messages also show the customer's SMS. A payout reads like "You have received UGX 7,000 from YOUR MERCHANT NAME on ...". Each message shows the new balance of the test customer wallet, which starts at UGX 1M. This is test money only.
If you pass both testOutcome and prompt, the forced outcome applies and prompt is ignored. Pass only prompt to test the PIN step.
Sending prompt with a live key is rejected as a validation error, so remove it before going live.
const payout = await nylonpay.makePayout({
amount: 7000,
currency: 'UGX',
description: 'Order 1042',
customer: { name: 'Test Customer', phoneNumber: '+256700000000' },
destination: {
accountHolderName: 'Test Customer',
accountNumber: '1234567890',
phone: '+256700000000',
},
prompt: 'pin',
});Resolve Variant
Use makePayoutAndResolve() when you prefer a single blocking call instead of event listeners. It waits until the payout reaches a terminal state and returns a Result.
const result = await nylonpay.makePayoutAndResolve({
amount: 50000,
currency: 'UGX',
description: 'Supplier payment #4471',
customer: { name: 'Jane Doe', phoneNumber: '+256700000000' },
destination: {
accountHolderName: 'Jane Doe',
accountNumber: '1234567890',
phone: '+256700000000',
},
});
if (result.isOk) {
console.log('Payout status:', result.value.status);
}Payouts From KES, TZS, RWF, and CDF Wallets
A payout from one of these wallets returns on_hold first. You confirm the
exchange rate on the dashboard, and the payout is sent when you confirm. If you
cancel, or do not answer within 24 hours, it ends cancelled and the money
returns to your wallet. Track it with getStatus() or your webhook. See
Pricing.
Idempotency
The reference field prevents duplicate payouts. If a request fails and you retry with the same reference, the SDK returns the existing payout instance instead of creating a new one.
A supplied reference must be a valid UUID of any version. The SDK generates a UUID v4 when you omit reference. Never reuse the same reference for different payouts.
Return Value
makePayout() returns a PaymentInstance with:
| Method | Description |
|---|---|
.on(event, handler) | Listen for events (processing, success, failed, cancelled, error) |
.once(event, handler) | Listen once, then auto-remove |
.off(event, handler) | Remove a handler |
.wait() | Promise that resolves Transaction on success, null on failure/cancel/error. Never rejects. |
.reference | The transaction reference |
.status | Current transaction status |
See Also
- Collect Payment, accept payments from customers
- Payment Events, the event lifecycle shared by collections and payouts
- Merchant Onboarding, settlement limits by KYC level