Create a payout
Last updated:
The createPayout method disburses funds from your UnitPay merchant balance to an external destination — a card, an Indonesian bank account, an international SWIFT beneficiary, or a USDT (TRC-20) wallet. Payouts are server-to-server only; never expose the secret key in client-side code or logs.
When to use it
- Marketplace seller settlement — pay out to seller bank accounts after their orders complete.
- Payroll and contractor disbursement — bulk-pay payees by uploading a batch and submitting one row per beneficiary.
- Out-of-band refunds — when the original payment cannot be reversed (window expired, channel unsupported), issue an equivalent payout to the customer's account.
- Treasury moves and partner settlement — cross-border USD/EUR via SWIFT or USDT on Tron.
For reversing an in-window card or VA payment, prefer refundPayment — it preserves the original payment-method audit trail. Use createPayout only when refund is not possible.
Prerequisites
- Project KYC must be in active state. Payouts are blocked while KYC is pending or under review.
- Merchant balance in the requested currency must cover the payout sum plus the destination-rail fee. Available balance is visible in the merchant cabinet under Balance → Available for payout.
- Destination type must be enabled on your project. Card and Indonesian bank payouts are enabled by default; SWIFT and USDT (TRC-20) require an explicit enablement request to onboarding.
- Payout limits (per-transaction and per-day) are set by merchant tier. Submit a request through the cabinet to raise them.
Endpoint
Send the request to https://api.unitpay.net/api. POST over TLS is required — GET is rejected for payout methods because the secret key would otherwise leak into URL logs and proxy buffers.
Required parameters
| Parameter | Type | Description |
|---|---|---|
method | string | Always createPayout. |
params[projectId] | integer | Your project ID, visible in the merchant cabinet under Settings → Project. |
params[paymentType] | string | Destination rail: card, bank_idr, bank_swift, or usdt_trc20. See Supported destinations below. |
params[sum] | number | Payout amount in major currency units (for example, 1500000.00 for IDR 1,500,000). Two decimal places maximum. Must be greater than zero and not exceed your available balance net of fee. |
params[purpose] | string | Human-readable purpose of the payout (for example, Marketplace settlement, order #9821). Stored in the audit trail and shown to the recipient where the rail supports it. |
params[account] | string | Destination identifier: card number for card, IDR account number for bank_idr, IBAN/account number for bank_swift, wallet address for usdt_trc20. Allowlists for travel-rule controls apply to crypto rails. |
params[signature] | string | HMAC signature of the request. See Signing the request below. |
Optional parameters
| Parameter | Type | Description |
|---|---|---|
params[currency] | string | ISO 4217 currency code: IDR, USD, EUR, or USDT. Defaults to the project's primary currency. Must match a balance you hold. |
params[merchantPayoutId] | string | Your idempotency key (max 64 chars). Two requests with the same merchantPayoutId create only one payout — the second call returns the original payoutId. Strongly recommended for production traffic. |
params[recipientName] | string | Beneficiary full name. Required for bank_swift; optional but recommended for card and bank_idr. |
params[recipientBankCode] | string | Bank routing code: BIC/SWIFT for bank_swift, BI bank code for bank_idr. Required where the destination rail cannot infer the bank from the account number alone. |
Supported destinations
paymentType | Currencies | Account format | Settlement window |
|---|---|---|---|
card | IDR, USD, EUR | 13–19-digit PAN (Visa, Mastercard, JCB) | Same business day for IDR cards; 1–3 business days for international cards |
bank_idr | IDR | Indonesian bank account number + BI bank code | Real-time via BI-FAST when supported by destination bank; otherwise next-day SKNBI |
bank_swift | USD, EUR | IBAN or local account number + BIC/SWIFT code | 1–3 business days, depending on correspondent banking chain |
usdt_trc20 | USDT | Tron (TRC-20) wallet address | Minutes, subject to network congestion |
Use getBinInfo to validate a card destination before submitting a payout — it returns the card brand, type, country, and issuing bank so you can reject ineligible cards before they hit limits.
Signing the request
UnitPay signs payout requests with HMAC-SHA-256 using your project secret key. The algorithm matches createPayment:
- Take all
params[*]values exceptparams[signature]and sort them by parameter name. - Concatenate the values, separated by the literal four-character delimiter
{up}, in this order:method, then each sorted parameter value, then your project secret key. - Hash the resulting string with SHA-256 and lowercase the hex digest.
- Pass the digest as
params[signature].
Rotate the secret key from Settings → Project → Secret key if you suspect compromise. Never commit it to source control.
Example request
POST https://api.unitpay.net/api
Content-Type: application/x-www-form-urlencoded
method=createPayout
¶ms[projectId]=123456
¶ms[paymentType]=bank_idr
¶ms[sum]=1500000.00
¶ms[currency]=IDR
¶ms[purpose]=Marketplace%20settlement%20order%20%239821
¶ms[account]=1234567890
¶ms[recipientBankCode]=014
¶ms[recipientName]=Andi%20Pratama
¶ms[merchantPayoutId]=settlement-9821
¶ms[signature]=<sha256-hex-digest>
Successful response
UnitPay replies with HTTP 200 and a JSON body once the payout is queued. Settlement to the beneficiary follows the destination rail's window — the response only confirms acceptance.
{
"result": {
"payoutId": 9871234,
"status": "new",
"sum": 1500000.00,
"fee": 5500.00,
"currency": "IDR",
"createdDate": "2026-05-08T14:22:11+07:00",
"merchantPayoutId": "settlement-9821"
}
}
| Field | Description |
|---|---|
payoutId | UnitPay's unique payout identifier. Store it alongside your merchantPayoutId for reconciliation and status queries. |
status | Initial state, almost always new. Final state is delivered via callback or polled with getPayout. |
fee | Destination-rail fee in the same currency as sum. Already deducted from your balance. |
createdDate | ISO 8601 timestamp in Asia/Jakarta (WIB) timezone. |
Error response
{
"error": {
"message": "Insufficient balance for this payout.",
"code": -32100
}
}
| code | Meaning |
|---|---|
-32000 | Invalid signature or secret key. |
-32100 | Insufficient balance net of fee. |
-32101 | Destination rail (paymentType) is not enabled for this project. |
-32102 | Per-transaction or daily payout limit exceeded. |
-32103 | Account format invalid for the requested rail (for example, IBAN missing for bank_swift). |
-32104 | Destination card or wallet failed prevalidation (closed card, blocked wallet, sanctioned country). |
-32105 | Project KYC is not in active state. |
Treat any error response as a hard failure: do not retry blindly. If you receive a network timeout or HTTP 5xx, retry with the same merchantPayoutId — the idempotency key prevents duplicate disbursement.
Test mode
Each project has a separate test secret key and a sandbox createPayout endpoint that mirrors production behaviour without moving real funds. Test payouts always reach success within seconds and emit the same callback shape, so you can validate your handler end-to-end before going live.
Important constraints
- Idempotency. Always send
merchantPayoutId. Network timeouts and intermediary retries can cause UnitPay to receive your request more than once; only the idempotency key prevents duplicate disbursement. - Travel rule for crypto. USDT (TRC-20) payouts are subject to wallet-allowlist enforcement and source-of-funds checks. Allowlist your treasury and partner wallets in the cabinet before relying on automated USDT disbursement.
- Sanctions screening. Beneficiaries are screened against UN, EU, OFAC, and OFSI lists at submission. A match returns error code
-32104and the payout is not queued. - Operator approval. Project policy may require manual operator approval for payouts above a configurable threshold. The payout sits in
pending_reviewuntil an operator approves it in the cabinet.
Next steps
- Get payout information — query the current state, settlement timestamp, and fee.
- Look up card information by BIN — prevalidate card destinations before disbursement.
- Callback handler — the same handler also receives payout state changes; reuse signature verification.
Metode createPayout mencairkan dana dari saldo merchant UnitPay Anda ke tujuan eksternal — kartu, rekening bank Indonesia, penerima SWIFT internasional, atau dompet USDT (TRC-20). Pencairan dana hanya dilakukan server-ke-server; jangan pernah mengekspos kunci rahasia di kode sisi klien atau di log.
Kapan menggunakannya
- Penyelesaian penjual marketplace — cairkan ke rekening bank penjual setelah pesanan mereka selesai.
- Penggajian dan pembayaran kontraktor — cairkan secara massal kepada penerima dengan mengunggah batch dan mengirim satu baris per penerima.
- Pengembalian dana di luar saluran — jika pembayaran asli tidak dapat dibalik (jendela kedaluwarsa, saluran tidak mendukung), terbitkan pencairan setara ke rekening pelanggan.
- Pemindahan kas dan penyelesaian mitra — lintas-batas USD/EUR melalui SWIFT atau USDT di Tron.
Untuk membalik pembayaran kartu atau VA yang masih dalam jendela, lebih baik gunakan refundPayment — metode itu mempertahankan jejak audit metode pembayaran asli. Gunakan createPayout hanya jika pengembalian dana tidak memungkinkan.
Prasyarat
- KYC proyek harus berstatus aktif. Pencairan dana diblokir selama KYC tertunda atau dalam tinjauan.
- Saldo merchant pada mata uang yang diminta harus mencakup jumlah pencairan ditambah biaya saluran tujuan. Saldo tersedia ditampilkan di kabinet merchant pada Saldo → Tersedia untuk pencairan.
- Tipe tujuan harus diaktifkan untuk proyek Anda. Pencairan kartu dan bank Indonesia aktif secara default; SWIFT dan USDT (TRC-20) memerlukan permintaan pengaktifan eksplisit ke tim onboarding.
- Batas pencairan (per-transaksi dan harian) ditentukan oleh tier merchant. Ajukan permintaan melalui kabinet untuk menaikkannya.
Endpoint
Kirim permintaan ke https://api.unitpay.net/api. POST melalui TLS wajib digunakan — GET ditolak untuk metode pencairan karena kunci rahasia akan bocor ke log URL dan buffer proxy.
Parameter wajib
| Parameter | Tipe | Deskripsi |
|---|---|---|
method | string | Selalu createPayout. |
params[projectId] | integer | ID proyek Anda, terlihat di kabinet merchant pada Pengaturan → Proyek. |
params[paymentType] | string | Saluran tujuan: card, bank_idr, bank_swift, atau usdt_trc20. Lihat Tujuan yang didukung di bawah. |
params[sum] | number | Jumlah pencairan dalam satuan mata uang utama (misalnya 1500000.00 untuk IDR 1.500.000). Maksimal dua tempat desimal. Harus lebih besar dari nol dan tidak melebihi saldo tersedia setelah dikurangi biaya. |
params[purpose] | string | Tujuan pencairan dalam bahasa manusia (misalnya Penyelesaian marketplace, pesanan #9821). Disimpan dalam jejak audit dan ditampilkan kepada penerima jika saluran mendukung. |
params[account] | string | Identifikasi tujuan: nomor kartu untuk card, nomor rekening IDR untuk bank_idr, IBAN/nomor rekening untuk bank_swift, alamat dompet untuk usdt_trc20. Daftar putih (allowlist) untuk kontrol travel-rule berlaku pada saluran kripto. |
params[signature] | string | Tanda tangan HMAC permintaan. Lihat Menandatangani permintaan di bawah. |
Parameter opsional
| Parameter | Tipe | Deskripsi |
|---|---|---|
params[currency] | string | Kode mata uang ISO 4217: IDR, USD, EUR, atau USDT. Default ke mata uang utama proyek. Harus sesuai dengan saldo yang Anda miliki. |
params[merchantPayoutId] | string | Kunci idempotensi Anda (maks 64 karakter). Dua permintaan dengan merchantPayoutId yang sama hanya membuat satu pencairan — panggilan kedua mengembalikan payoutId yang sama. Sangat disarankan untuk trafik produksi. |
params[recipientName] | string | Nama lengkap penerima. Wajib untuk bank_swift; opsional namun disarankan untuk card dan bank_idr. |
params[recipientBankCode] | string | Kode rute bank: BIC/SWIFT untuk bank_swift, kode bank BI untuk bank_idr. Wajib jika saluran tujuan tidak dapat menyimpulkan bank dari nomor rekening saja. |
Tujuan yang didukung
paymentType | Mata uang | Format akun | Jendela penyelesaian |
|---|---|---|---|
card | IDR, USD, EUR | PAN 13–19 digit (Visa, Mastercard, JCB) | Hari kerja yang sama untuk kartu IDR; 1–3 hari kerja untuk kartu internasional |
bank_idr | IDR | Nomor rekening bank Indonesia + kode bank BI | Real-time melalui BI-FAST jika didukung bank tujuan; selain itu SKNBI hari berikutnya |
bank_swift | USD, EUR | IBAN atau nomor rekening lokal + kode BIC/SWIFT | 1–3 hari kerja, tergantung rantai bank koresponden |
usdt_trc20 | USDT | Alamat dompet Tron (TRC-20) | Hitungan menit, tergantung kepadatan jaringan |
Gunakan getBinInfo untuk memvalidasi tujuan kartu sebelum mengirim pencairan — metode itu mengembalikan brand, tipe, negara, dan bank penerbit sehingga Anda dapat menolak kartu yang tidak memenuhi syarat sebelum mereka memakai kuota batas.
Menandatangani permintaan
UnitPay menandatangani permintaan pencairan dengan HMAC-SHA-256 menggunakan kunci rahasia proyek Anda. Algoritmanya sama dengan createPayment:
- Ambil semua nilai
params[*]kecualiparams[signature]dan urutkan berdasarkan nama parameter. - Gabungkan nilainya, dipisahkan oleh pembatas literal empat-karakter
{up}, dalam urutan:method, kemudian setiap nilai parameter terurut, lalu kunci rahasia proyek Anda. - Hash string yang dihasilkan dengan SHA-256 dan ubah ke huruf kecil pada digest hex.
- Lampirkan digest sebagai
params[signature].
Lakukan rotasi kunci rahasia dari Pengaturan → Proyek → Kunci rahasia jika Anda menduga kompromi. Jangan pernah menaruhnya di kontrol versi.
Contoh permintaan
POST https://api.unitpay.net/api
Content-Type: application/x-www-form-urlencoded
method=createPayout
¶ms[projectId]=123456
¶ms[paymentType]=bank_idr
¶ms[sum]=1500000.00
¶ms[currency]=IDR
¶ms[purpose]=Penyelesaian%20marketplace%20pesanan%20%239821
¶ms[account]=1234567890
¶ms[recipientBankCode]=014
¶ms[recipientName]=Andi%20Pratama
¶ms[merchantPayoutId]=settlement-9821
¶ms[signature]=<sha256-hex-digest>
Respons sukses
UnitPay membalas dengan HTTP 200 dan body JSON setelah pencairan masuk antrian. Penyelesaian ke penerima mengikuti jendela saluran tujuan — respons hanya mengkonfirmasi penerimaan permintaan.
{
"result": {
"payoutId": 9871234,
"status": "new",
"sum": 1500000.00,
"fee": 5500.00,
"currency": "IDR",
"createdDate": "2026-05-08T14:22:11+07:00",
"merchantPayoutId": "settlement-9821"
}
}
| Field | Deskripsi |
|---|---|
payoutId | Identifikasi pencairan unik dari UnitPay. Simpan bersama merchantPayoutId Anda untuk rekonsiliasi dan kueri status. |
status | Status awal, hampir selalu new. Status final disampaikan via callback atau melalui getPayout. |
fee | Biaya saluran tujuan dalam mata uang yang sama dengan sum. Sudah dipotong dari saldo Anda. |
createdDate | Cap waktu ISO 8601 dalam zona Asia/Jakarta (WIB). |
Respons error
{
"error": {
"message": "Saldo tidak cukup untuk pencairan ini.",
"code": -32100
}
}
| code | Arti |
|---|---|
-32000 | Tanda tangan atau kunci rahasia tidak valid. |
-32100 | Saldo tidak cukup setelah dipotong biaya. |
-32101 | Saluran tujuan (paymentType) belum diaktifkan untuk proyek ini. |
-32102 | Batas pencairan per-transaksi atau harian terlampaui. |
-32103 | Format akun tidak valid untuk saluran yang diminta (misalnya IBAN tidak ada untuk bank_swift). |
-32104 | Kartu atau dompet tujuan gagal pravalidasi (kartu tertutup, dompet diblokir, negara terkena sanksi). |
-32105 | KYC proyek tidak berstatus aktif. |
Anggap respons error apa pun sebagai kegagalan keras: jangan mengulang permintaan tanpa pemeriksaan. Jika Anda menerima timeout jaringan atau HTTP 5xx, ulangi dengan merchantPayoutId yang sama — kunci idempotensi mencegah pencairan ganda.
Mode uji
Setiap proyek memiliki kunci rahasia uji terpisah dan endpoint createPayout sandbox yang mencerminkan perilaku produksi tanpa memindahkan dana sungguhan. Pencairan dalam mode uji selalu mencapai success dalam hitungan detik dan memancarkan bentuk callback yang sama, sehingga Anda dapat memvalidasi handler end-to-end sebelum aktif.
Batasan penting
- Idempotensi. Selalu kirim
merchantPayoutId. Timeout jaringan dan retry perantara dapat menyebabkan UnitPay menerima permintaan Anda lebih dari sekali; hanya kunci idempotensi yang mencegah pencairan ganda. - Travel rule untuk kripto. Pencairan USDT (TRC-20) tunduk pada penerapan daftar putih dompet dan pemeriksaan source-of-funds. Daftarputihkan dompet treasury dan mitra Anda di kabinet sebelum mengandalkan pencairan USDT otomatis.
- Penyaringan sanksi. Penerima disaring terhadap daftar UN, EU, OFAC, dan OFSI saat pengiriman. Jika cocok, dikembalikan kode error
-32104dan pencairan tidak masuk antrian. - Persetujuan operator. Kebijakan proyek dapat mensyaratkan persetujuan operator manual untuk pencairan di atas ambang batas yang dapat dikonfigurasi. Pencairan tersebut berstatus
pending_reviewhingga operator menyetujuinya di kabinet.
Langkah selanjutnya
- Informasi pencairan dana — periksa status saat ini, cap waktu penyelesaian, dan biaya.
- Cari informasi kartu berdasarkan BIN — pravalidasi tujuan kartu sebelum pencairan.
- Penanganan callback — handler yang sama juga menerima perubahan status pencairan; gunakan kembali verifikasi tanda tangan.