Withdrawals (Payouts)
API reference for creating and managing outbound payments.
The API routes use /payouts but the type field in responses is "withdrawal". This reflects the internal model where withdrawals are the counterpart to deposits.
Create Withdrawal
POST /api/payoutsCreates a new withdrawal intent to send funds to a recipient. The response tells your application what to do next — see Actions.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
reference_id | string | Yes | Your unique identifier for this payout. Must be unique within your tenant. |
amount | integer or string | Yes | Amount in cents. 500000 = 5000.00. Decimal strings like "5000.00" are also accepted and auto-converted. |
currency | string | Yes | Currency code (e.g., "ETB"). |
channel | string | Yes | Withdrawal channel. See Channels. |
method | string | No | Sub-method (e.g., "telebirr", "cbe"). |
fields | object | No | Recipient details required by the channel (e.g., account number, phone). |
Channels
| Channel | PSP | Flow | When to Use |
|---|---|---|---|
direct_payout | Chapa | Single-step bank or mobile money transfer | Fiat payouts (ETB) to bank accounts or mobile wallets |
crypto_payout | NOWPayments | Crypto payout with optional verification step | Crypto payouts to wallet addresses |
Examples
TIMESTAMP=$(date +%s)
BODY='{"reference_id":"payout-78901","amount":500000,"currency":"ETB","channel":"direct_payout","fields":{"account_number":"1000123456789","account_name":"Abebe Kebede"}}'
curl -X POST https://pay.phoenixverse.io/api/payouts \
-H "Content-Type: application/json" \
-H "X-Key-Id: your-key-id" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: <signature-of-$TIMESTAMP.$BODY>" \
-d "$BODY"{
"intent_id": "01912e4c-9d5e-7def-ac12-3456789012cd",
"action": "await",
"message": "Transfer is being processed"
}TIMESTAMP=$(date +%s)
BODY='{"reference_id":"payout-78902","amount":250000,"currency":"ETB","channel":"direct_payout","method":"telebirr","fields":{"account_number":"+251911234567","account_name":"Abebe Kebede"}}'
curl -X POST https://pay.phoenixverse.io/api/payouts \
-H "Content-Type: application/json" \
-H "X-Key-Id: your-key-id" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: <signature-of-$TIMESTAMP.$BODY>" \
-d "$BODY"{
"intent_id": "01912e4d-ae6f-7def-bd23-4567890123de",
"action": "await",
"message": "Transfer is being processed"
}TIMESTAMP=$(date +%s)
BODY='{"reference_id":"payout-78903","amount":2500,"currency":"USDT","channel":"crypto_payout","fields":{"address":"TXrk4d5x7Bj3e6g7Y8Zw9AbCdEf1234567"}}'
curl -X POST https://pay.phoenixverse.io/api/payouts \
-H "Content-Type: application/json" \
-H "X-Key-Id: your-key-id" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: <signature-of-$TIMESTAMP.$BODY>" \
-d "$BODY"{
"intent_id": "01912e4e-be7f-7def-ce34-5678901234ef",
"action": "await",
"message": "Payout is being processed"
}The action: "await" response means the PSP is processing the transfer. Wait for a webhook to confirm completion.
For NOWPayments crypto payouts, the create response may also include:
| Field | Description |
|---|---|
batch_withdrawal_id | NOWPayments payout batch id |
payout_status | Raw NOWPayments payout status |
payout_currency | Network-specific payout currency code |
fee_amount | Estimated payout fee in cents |
fee_currency | Fee currency code |
address_validation | Whether NOWPayments accepted the destination address during validation |
If NOWPayments rejects the wallet address during validation, Phoenix Pay returns 422 and does not create the payout.
If the NOWPayments account is still configured for manual merchant verification, Phoenix Pay returns 422 with a provider error instead of exposing that verification step to your app.
Errors
| Status | Error | Cause |
|---|---|---|
400 | "missing required parameter: reference_id" | A required field is missing or empty |
400 | "missing required parameter: channel" | The channel field is required |
409 | "duplicate_reference" | The reference_id already exists for your tenant. Response includes intent_id. |
422 | PSP-specific error | The PSP rejected the request. |
Get Withdrawal
GET /api/payouts/:idRetrieve a withdrawal by its Phoenix Pay UUID.
curl https://pay.phoenixverse.io/api/payouts/01912e4c-9d5e-7def-ac12-3456789012cd \
-H "X-Key-Id: your-key-id" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <signature>"{
"id": "01912e4c-9d5e-7def-ac12-3456789012cd",
"reference_id": "payout-78901",
"display_ref": "WDR-20260311-D4E5F6",
"type": "withdrawal",
"status": "completed",
"amount": 500000,
"currency": "ETB",
"channel": "direct_payout",
"payment_method": null,
"error_code": null,
"error_detail": null,
"psp": "chapa",
"psp_external_id": "chapa_tr_12345",
"inserted_at": "2026-03-11T12:30:00.000000Z",
"updated_at": "2026-03-11T12:45:00.000000Z"
}Response Fields
| Field | Type | Description |
|---|---|---|
id | string (UUID v7) | Unique intent identifier |
reference_id | string | Your reference ID from the create request |
display_ref | string | Auto-generated human-readable reference |
type | string | Always "withdrawal" |
status | string | Current status: created, pending, completed, failed, expired |
amount | integer | Amount in cents |
currency | string | Currency code |
channel | string | The channel used (e.g., "direct_payout") |
payment_method | string or null | Sub-method if specified |
error_code | string or null | Error code if failed |
error_detail | string or null | Human-readable failure reason |
psp | string or null | PSP handling the latest attempt |
psp_external_id | string or null | PSP's own transaction ID |
inserted_at | string (ISO 8601) | When the intent was created |
updated_at | string (ISO 8601) | When the intent was last updated |
For NOWPayments payouts, psp_external_id is the payout batch identifier returned by NOWPayments.
Errors
| Status | Error | Cause |
|---|---|---|
404 | "not_found" | No withdrawal with this ID exists for your tenant |
Get Withdrawal by Reference
GET /api/payouts/ref/:reference_idRetrieve a withdrawal using your original reference_id.
curl https://pay.phoenixverse.io/api/payouts/ref/payout-78901 \
-H "X-Key-Id: your-key-id" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <signature>"The response format is identical to Get Withdrawal.
List Withdrawals
GET /api/payoutsRetrieve a paginated list of withdrawals for your tenant.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 25 | Results per page (max 100) |
status | string | — | Filter by status: created, pending, completed, failed, expired |
currency | string | — | Filter by currency code |
from | string (date) | — | Filter by creation date on or after (ISO 8601 date) |
to | string (date) | — | Filter by creation date on or before |
search | string | — | Search by reference ID (partial match) |
Both from and to must be provided for date filtering to take effect.
curl "https://pay.phoenixverse.io/api/payouts?page=1&limit=10" \
-H "X-Key-Id: your-key-id" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <signature>"Response
{
"data": [
{
"id": "01912e4c-9d5e-7def-ac12-3456789012cd",
"reference_id": "payout-78901",
"display_ref": "WDR-20260311-D4E5F6",
"type": "withdrawal",
"status": "completed",
"amount": 500000,
"currency": "ETB",
"channel": "direct_payout",
"payment_method": null,
"error_code": null,
"error_detail": null,
"psp": "chapa",
"psp_external_id": "chapa_tr_12345",
"inserted_at": "2026-03-11T12:30:00.000000Z",
"updated_at": "2026-03-11T12:45:00.000000Z"
}
],
"meta": {
"page": 1,
"limit": 10,
"total": 1,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
}Results are sorted by creation date, newest first.