Idempotency keys
Retry any request safely — duplicates are never created.
Developers
Create deposits, send payouts and reconcile settlement across 12 markets with a single, consistent REST API. Predictable semantics, signed webhooks and a full sandbox help your team ship faster.
POST /v1/payouts Idempotency-Key: wd-55120 { "merchant_order_id": "wd-55120", "amount": "12500000", "currency": "VND", "method": "bank_transfer", "beneficiary": { "bank_code": "VCB", "account_number": "0000123456", "account_name": "NGUYEN VAN A" } } # 201 Created { "id": "po_3Lm8Rv2", "status": "processing" }
Illustrative example. Exact endpoints and fields are in the API reference shared during onboarding.
Quickstart
Everything happens in the sandbox first, so you can test every path before real money moves.
Create an app in the merchant console to receive sandbox API credentials and a webhook secret.
Send the amount, currency, method and your own order reference. An idempotency key makes every retry safe.
Redirect to the hosted cashier URL, or show the QR code and bank details in your own interface.
Verify the signature, then credit the trader’s account once the status is succeeded.
curl -X POST "$IBITPAY_API/v1/payins" \ -H "Authorization: Bearer $IBITPAY_SECRET_KEY" \ -H "Idempotency-Key: dep-102938" \ -H "Content-Type: application/json" \ -d '{ "merchant_order_id": "dep-102938", "amount": "35000.00", "currency": "THB", "method": "promptpay_qr", "customer": { "account": "MT5-88012291" }, "notify_url": "https://broker.example/webhooks/ibitpay" }'
const res = await fetch(`${process.env.IBITPAY_API}/v1/payins`, { method: "POST", headers: { "Authorization": `Bearer ${process.env.IBITPAY_SECRET_KEY}`, "Idempotency-Key": order.id, "Content-Type": "application/json", }, body: JSON.stringify({ merchant_order_id: order.id, amount: "35000.00", currency: "THB", method: "promptpay_qr", customer: { account: "MT5-88012291" }, notify_url: "https://broker.example/webhooks/ibitpay", }), }); const payin = await res.json(); // Redirect the trader to payin.cashier_url
import os, requests resp = requests.post( f"{os.environ['IBITPAY_API']}/v1/payins", headers={ "Authorization": f"Bearer {os.environ['IBITPAY_SECRET_KEY']}", "Idempotency-Key": order_id, }, json={ "merchant_order_id": order_id, "amount": "35000.00", "currency": "THB", "method": "promptpay_qr", "customer": {"account": "MT5-88012291"}, "notify_url": "https://broker.example/webhooks/ibitpay", }, timeout=15, ) payin = resp.json() # redirect the trader to payin["cashier_url"]
$ch = curl_init(getenv('IBITPAY_API') . '/v1/payins'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('IBITPAY_SECRET_KEY'), 'Idempotency-Key: ' . $orderId, 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'merchant_order_id' => $orderId, 'amount' => '35000.00', 'currency' => 'THB', 'method' => 'promptpay_qr', 'customer' => ['account' => 'MT5-88012291'], 'notify_url' => 'https://broker.example/webhooks/ibitpay', ]), ]); $payin = json_decode(curl_exec($ch), true);
Illustrative example. Exact endpoints and fields are in the API reference shared during onboarding.
Webhooks
Every status change triggers a signed webhook to your endpoint. Failed deliveries are retried automatically, and the order status API is always there for reconciliation.
import crypto from "node:crypto"; // Check the X-Signature header before trusting an event export function verifyWebhook(rawBody, header, secret) { const { t, v1 } = Object.fromEntries( header.split(",").map((part) => part.split("=")) ); const expected = crypto .createHmac("sha256", secret) .update(`${t}.${rawBody}`) .digest("hex"); return v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)); }
Illustrative example. Exact endpoints and fields are in the API reference shared during onboarding.
Order lifecycle
Pay-ins and payouts move through a small set of well-defined states. Each one is timestamped and visible in the console.
Pay-in
Payout
Built for reliability
Retry any request safely — duplicates are never created.
Every request and webhook is signed and verifiable on both sides.
Machine-readable codes with clear, actionable messages.
Simulate success, failure and expiry for every method before going live.
Explore every endpoint before you write a line of code.
Order, fee and settlement data in the formats finance teams expect.
Integration options
Redirect traders to a localised, mobile-first payment page. The quickest way to go live.
Embed the cashier inside your client portal so traders never leave your brand experience.
Build your own interface and call the API server-to-server for full control over every step.
Start building
Request sandbox credentials and our integration team will help you plan your launch.