Payout
Controlled payout: the platform freezes your available balance first, then enters the approval/disbursement flow; large or risk-flagged orders go to manual review. The final result always comes via callback/query.
Create POST /payout/create
Business parameters (plus the common parameters):
| Field | Required | Description |
|---|---|---|
mch_order_no | Yes | Your order number, unique within your account |
product_code | Yes | Payout product code |
amount | Yes | Amount in the currency's minor unit |
currency | Yes | Currency enabled for you; not-enabled returns 1001 |
payee_name | Yes | Payee name |
payee_account | Yes | Payee account number |
bank_code | Yes | Bank/wallet code, per currency and must match currency — see Bank Codes. Not in the table → 2006; no serving channel for that currency → 1001 |
attach | No | Pass-through data (string) |
Request example:
{
"merchant_no": "M100001",
"app_id": "app_10001",
"key_version": 1,
"timestamp": 1769990400,
"nonce": "c3d4e5f6a1b2c3d4",
"sign_type": "RSA2",
"mch_order_no": "PO20260601001",
"product_code": "PAYOUT",
"amount": 500000,
"currency": "PHP",
"payee_name": "Juan Dela Cruz",
"payee_account": "09171234567",
"bank_code": "PH_GXI",
"sign": "BASE64_SIGNATURE"
}Response data fields:
| Field | Description |
|---|---|
platform_order_no / mch_order_no | Order numbers |
amount / fee / currency | Amount / fee / currency (minor units) |
status | APPROVED (proceeding to disbursement) or FROZEN (manual review) |
review_required | Whether manual review is required (status=FROZEN when true) |
Response example:
{
"code": "0",
"msg": "OK",
"data": {
"platform_order_no": "P20260601123456efgh",
"mch_order_no": "PO20260601001",
"amount": 500000,
"fee": 6000,
"currency": "PHP",
"status": "APPROVED",
"review_required": false
}
}Query POST /payout/query
Business parameters (plus the common parameters):
| Field | Required | Description |
|---|---|---|
mch_order_no | One of two | Your order number |
platform_order_no | One of two | Platform order number (prefer exactly one) |
Request example:
{
"merchant_no": "M100001",
"app_id": "app_10001",
"key_version": 1,
"timestamp": 1769990800,
"nonce": "d4e5f6a1b2c3d4e5",
"sign_type": "RSA2",
"platform_order_no": "P20260601123456efgh",
"sign": "BASE64_SIGNATURE"
}Response data fields:
| Field | Description |
|---|---|
platform_order_no / mch_order_no | Order numbers |
amount / fee / currency | Amount / fee / currency (minor units) |
status | Order status, see Statuses |
finished_at | Terminal time (Unix seconds; present once terminal) |
reversed_at | Reversal time (Unix seconds; present if the order was reversed) |
fail_reason | Failure reason (present on failure) |
Response example:
{
"code": "0",
"msg": "OK",
"data": {
"platform_order_no": "P20260601123456efgh",
"mch_order_no": "PO20260601001",
"amount": 500000,
"fee": 6000,
"currency": "PHP",
"status": "SUCCESS",
"finished_at": 1769991200
}
}Async callback
When a payout order reaches a terminal state, the platform POSTs to your payout callback URL. Verification and reply rules are the same as collection callbacks (platform-key signature, platform public key by key_version, verify-then-process, reply success, increasing retries).
Callback fields:
| Field | Description |
|---|---|
platform_order_no / mch_order_no | Order numbers |
amount / fee / currency | Amount / fee / currency (minor units) |
status | Terminal: SUCCESS / FAILED / REJECTED, see Statuses |
fail_reason | Failure reason (present on failure/rejection) |
key_version | Platform callback key version |
timestamp / nonce / sign_type / sign | Platform signing fields |
Callback example (success):
{
"platform_order_no": "P20260601123456efgh",
"mch_order_no": "PO20260601001",
"amount": 500000,
"fee": 6000,
"currency": "PHP",
"status": "SUCCESS",
"key_version": 1,
"timestamp": 1769991205,
"nonce": "e5f6a1b2c3d4e5f6",
"sign_type": "RSA2",
"sign": "PLATFORM_SIGN_BASE64"
}Callback example (failure):
{
"platform_order_no": "P20260601123456efgh",
"mch_order_no": "PO20260601001",
"amount": 500000,
"fee": 6000,
"currency": "PHP",
"status": "FAILED",
"fail_reason": "UPSTREAM_REJECTED",
"key_version": 1,
"timestamp": 1769991205,
"nonce": "e5f6a1b2c3d4e5f6",
"sign_type": "RSA2",
"sign": "PLATFORM_SIGN_BASE64"
}Reply (after successful verification and processing — HTTP 200, body exactly):
successQuery vs callback
The callback body is a terminal snapshot (it does not carry finished_at/attach); call the query endpoint when you need the terminal time or pass-through data.
Reversal callback (payout.reversed)
Even after a payout order reaches the terminal SUCCESS state, the platform may reverse it (e.g. upstream bank bounce / funds clawed back). On reversal the platform sends an additional callback to the same payout callback URL, with the same signing and reply rules as above.
- How to recognize it: the reversal callback carries
event="payout.reversed"and does not carry astatusfield; normal terminal callbacks carrystatusand noevent. Route only by theeventfield. - Order status does not change: it stays
SUCCESS; the query endpoint reflects it viareversed_at. Mark the payout as reversed in your own system and handle the funds accordingly. - Idempotency: at most one reversal callback per order (delivery retries aside). Dedupe key =
platform_order_no+ callback type — do not dedupe by order number alone, or the reversal will be mistaken for an already-processed terminal callback and dropped.
Callback fields:
| Field | Description |
|---|---|
event | Always payout.reversed |
reversed_at | Reversal time (Unix seconds) |
platform_order_no / mch_order_no | Order numbers |
amount / fee | Original order amount / fee (minor units) |
currency | Currency |
reversal_amount | Reversed amount (minor units) |
fee_refund | Refunded fee (minor units) |
key_version / timestamp / nonce / sign_type / sign | Platform signing fields |
Callback example (reversal):
{
"event": "payout.reversed",
"reversed_at": 1770099205,
"platform_order_no": "P20260601123456efgh",
"mch_order_no": "PO20260601001",
"amount": 500000,
"fee": 6000,
"currency": "PHP",
"reversal_amount": 500000,
"fee_refund": 6000,
"key_version": 1,
"timestamp": 1770099210,
"nonce": "a1b2c3d4e5f6a1b2",
"sign_type": "RSA2",
"sign": "PLATFORM_SIGN_BASE64"
}Reply as usual: after successful verification and processing, respond HTTP 200 with the exact plain text success.