Create Payout API
Initiate money transfers to beneficiaries via bank transfers, mobile money, and other payment methods
Create Payout API
Create a new payout to transfer money to a beneficiary. The payout amount is immediately deducted from your wallet, and the funds are sent through the appropriate payment provider.
Endpoint
POST /api/v1/payoutsAuthentication: Bearer token required
Request Headers
Authorization: Bearer <access_token>
Content-Type: application/jsonRequest Body
Required Fields
{
"reference_id": "PAY_2024_001",
"wallet_id": 1234,
"amount": 100.00,
"currency": "USD",
"beneficiary_email": "john.doe@example.com",
"beneficiary_first_name": "John",
"beneficiary_last_name": "Doe"
}Complete Request Schema
{
"reference_id": "PAY_2024_001",
"wallet_id": 1234,
"amount": 100.00,
"currency": "USD",
"beneficiary_id": "ben_abc123xyz",
"beneficiary_email": "john.doe@example.com",
"beneficiary_first_name": "John",
"beneficiary_last_name": "Doe",
"beneficiary_country": "US",
"beneficiary_phone": "+1234567890",
"beneficiary_identifiers": {
"account_number": "123456789",
"routing_number": "987654321"
},
"description": "Q1 2024 Bonus Payment",
"metadata": {
"employee_id": "EMP001",
"department": "Engineering"
},
"notify_beneficiary": true
}Field Descriptions
| Field | Type | Required | Description |
|---|---|---|---|
reference_id | string | Yes | Unique client reference for idempotency |
wallet_id | number | Yes | Source wallet ID for the payout |
amount | number | Yes | Payout amount (must be > 0) |
currency | string | Yes | Currency code (ISO 4217) |
beneficiary_id | string | No | ID of saved beneficiary (alternative to inline info) |
beneficiary_email | string | Yes* | Beneficiary email (*required if no beneficiary_id) |
beneficiary_first_name | string | Yes* | First name (*required if no beneficiary_id) |
beneficiary_last_name | string | Yes* | Last name (*required if no beneficiary_id) |
beneficiary_country | string | No | Country code (ISO 3166-1 alpha-2) |
beneficiary_phone | string | No | Phone number with country code |
beneficiary_identifiers | object | No | Payment identifiers (bank account, mobile number, etc.) |
description | string | No | Internal description for the payout |
metadata | object | No | Custom key-value pairs for your reference |
notify_beneficiary | boolean | No | Send email notification to beneficiary (default: false) |
Validation Rules
- reference_id: Must be unique across all your payouts (idempotency key)
- amount: Must be positive and within provider min/max limits
- currency: Must be supported by an available payout provider
- wallet_id: Must belong to your client and have sufficient balance (including fees)
- beneficiary_email: Must be valid email format when provided
Response
Success Response
Status Code: 201 Created
{
"id": "pyt_xyz789abc",
"reference_id": "PAY_2024_001",
"status": "created",
"amount": 100.00,
"currency": "USD",
"fees": 1.50,
"total_amount": 101.50,
"beneficiary": {
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe",
"country": "US",
"phone": "+1234567890"
},
"description": "Q1 2024 Bonus Payment",
"created_at": "2024-01-15T10:30:00Z",
"queued_at": null,
"submitted_at": null,
"completed_at": null,
"failed_at": null,
"cancelled_at": null
}Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique payout identifier (use for tracking) |
reference_id | string | Your client reference |
status | string | Current payout status |
amount | number | Payout amount to beneficiary |
currency | string | Currency code |
fees | number | Fees charged for this payout |
total_amount | number | Total deducted from wallet (amount + fees) |
beneficiary | object | Beneficiary information |
failure_code | string | Error code if failed (nullable) |
failure_reason | string | Error description if failed (nullable) |
description | string | Payout description (nullable) |
created_at | string | Creation timestamp (ISO 8601) |
queued_at | string | When queued for processing (nullable) |
submitted_at | string | When submitted to provider (nullable) |
completed_at | string | When completed successfully (nullable) |
failed_at | string | When failed (nullable) |
cancelled_at | string | When cancelled (nullable) |
Payout Statuses
| Status | Description | Wallet Impact |
|---|---|---|
created | Payout created and queued | Deducted |
queued | In queue for processing | Deducted |
processing | Being processed by provider | Deducted |
settling | Funds settling to beneficiary | Deducted |
action_required | Needs additional action | Deducted |
completed | Successfully delivered | Deducted |
failed | Processing failed | Refunded |
cancelled | Cancelled by client | Refunded |
expired | Expired before completion | Refunded |
Error Responses
400 Bad Request - Validation Error
{
"error": "ValidationException",
"message": "reference_id is required"
}400 Bad Request - Invalid Amount
{
"error": "ValidationException",
"message": "amount must be greater than 0"
}400 Bad Request - Insufficient Funds
{
"error": "InternalServerError",
"message": "insufficient wallet balance"
}400 Bad Request - Duplicate Reference
{
"error": "InternalServerError",
"message": "duplicate reference_id"
}401 Unauthorized
{
"error": "UnauthorizedAccess",
"message": "Authentication required"
}500 Internal Server Error
{
"error": "InternalServerError",
"message": "Failed to create payout"
}Examples
Basic Payout with Inline Beneficiary
curl -X POST "{{host}}/api/v1/payouts" \
-H "Authorization: Bearer your_access_token" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "PAY_2024_001",
"wallet_id": 1234,
"amount": 100.00,
"currency": "USD",
"beneficiary_email": "john.doe@example.com",
"beneficiary_first_name": "John",
"beneficiary_last_name": "Doe",
"beneficiary_country": "US",
"beneficiary_identifiers": {
"account_number": "123456789",
"routing_number": "987654321"
},
"description": "Bonus Payment",
"notify_beneficiary": true
}'<?php
function createPayout($accessToken, $data) {
$url = '{{host}}/api/v1/payouts';
$options = [
'http' => [
'header' => [
"Content-Type: application/json",
"Authorization: Bearer $accessToken"
],
'method' => 'POST',
'content' => json_encode($data)
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
throw new Exception('Failed to create payout');
}
return json_decode($result, true);
}
// Usage
$payout = createPayout('your_access_token', [
'reference_id' => 'PAY_2024_001',
'wallet_id' => 1234,
'amount' => 100.00,
'currency' => 'USD',
'beneficiary_email' => 'john.doe@example.com',
'beneficiary_first_name' => 'John',
'beneficiary_last_name' => 'Doe',
'beneficiary_country' => 'US',
'beneficiary_identifiers' => [
'account_number' => '123456789',
'routing_number' => '987654321'
],
'description' => 'Bonus Payment',
'notify_beneficiary' => true
]);
echo "Payout ID: " . $payout['id'] . "\n";
echo "Status: " . $payout['status'] . "\n";
echo "Total Deducted: " . $payout['total_amount'] . " " . $payout['currency'] . "\n";
?>Payout with Saved Beneficiary
curl -X POST "{{host}}/api/v1/payouts" \
-H "Authorization: Bearer your_access_token" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "PAY_2024_002",
"wallet_id": 1234,
"amount": 50.00,
"currency": "USD",
"beneficiary_id": "ben_abc123xyz",
"description": "Recurring payment"
}'Mobile Money Payout
curl -X POST "{{host}}/api/v1/payouts" \
-H "Authorization: Bearer your_access_token" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "MM_2024_001",
"wallet_id": 5678,
"amount": 1000.00,
"currency": "KES",
"beneficiary_email": "jane@example.com",
"beneficiary_first_name": "Jane",
"beneficiary_last_name": "Wanjiku",
"beneficiary_country": "KE",
"beneficiary_phone": "+254712345678",
"beneficiary_identifiers": {
"mobile_number": "+254712345678",
"provider": "mpesa"
},
"notify_beneficiary": true
}'Error Handling Example
async function createPayoutWithRetry(payoutData, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('/api/v1/payouts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getAccessToken()}`
},
body: JSON.stringify(payoutData)
});
const result = await response.json();
if (response.status === 201) {
return result;
}
// Don't retry client errors (4xx)
if (response.status >= 400 && response.status < 500) {
throw new Error(result.message || 'Validation error');
}
// Retry server errors (5xx)
if (attempt < maxRetries) {
await sleep(Math.pow(2, attempt) * 1000); // Exponential backoff
continue;
}
throw new Error(result.message || 'Server error');
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
}
}
}
// Usage
try {
const payout = await createPayoutWithRetry({
reference_id: `PAY_${Date.now()}`,
wallet_id: 1234,
amount: 100.00,
currency: 'USD',
beneficiary_email: 'recipient@example.com',
beneficiary_first_name: 'Recipient',
beneficiary_last_name: 'Name'
});
console.log('Payout created:', payout.id);
} catch (error) {
console.error('Failed to create payout:', error.message);
}Idempotency
The reference_id field serves as an idempotency key. If you submit a payout with a reference_id that already exists:
- The request will fail with a duplicate reference error
- The original payout is not modified
This prevents accidental duplicate payouts due to network retries or application errors.
Best Practice
Generate unique reference IDs using a combination of:
- Timestamp or sequential counter
- Random component
- Business context (e.g., invoice number)
// Example reference ID generation
const referenceId = `PAY_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;Wallet Deduction
The payout amount plus fees are deducted immediately when the payout is created:
- Validation - Check wallet balance covers amount + fees
- Deduction - Immediately deduct total amount from wallet
- Processing - Payout enters processing queue
- Outcome - If failed/cancelled, amount is refunded to wallet
Important: Ensure your wallet has sufficient balance for both the payout amount and any applicable fees before creating a payout.
Best Practices
Before Creating Payouts
- Check provider availability - Verify a provider supports the target currency/country
- Calculate total cost - Account for payout amount + fees
- Validate wallet balance - Ensure sufficient funds before submission
- Verify beneficiary details - Validate bank/mobile details are correct
During Processing
- Store payout IDs - Save the returned
idfor tracking - Monitor status - Poll the Get Payout API for updates
- Handle failures gracefully - Check
failure_codeandfailure_reasonfor details
Production Recommendations
- Use webhooks - Set up webhooks for real-time status updates (when available)
- Implement reconciliation - Regularly compare your records with API data
- Set up alerts - Monitor for unusual failure rates
- Test thoroughly - Use sandbox environment before production
Related Endpoints
- List Payouts - View all your payouts
- Get Payout - Check payout status and details
- Cancel Payout - Cancel a pending payout