Overview
The ZBD Widget is an embeddable iframe that handles the full cashout flow for your users — identity verification (KYC), bank account linking (Plaid), and ACH payouts. Your backend handles user creation, session minting, and balance funding; the widget handles everything else.
Integration Flow
1. Create User POST /api/v1/cashout/users (your server → ZBD API)
2. Fund User POST /v1/cashout/funding-transfers (your server → ZBD API)
3. Create Session POST /api/v1/cashout-widget (your server → ZBD API)
4. Embed Widget <iframe src="{widget_url}" /> (your frontend)
5. Listen for Events window.addEventListener("message", ...) (your frontend)
Authentication
Widget endpoints use two auth patterns:
| Context | Auth | Header |
|---|
| Your server → ZBD | Publisher API key | apikey: YOUR_API_KEY |
| Widget iframe → ZBD | Session JWT (automatic) | Authorization: Bearer {session_token} |
Never expose your publisher API key in the browser. Steps 1–3 must happen on your server.
After creating a session, embed the widget URL as an iframe:
<iframe
src="https://ramp.zbdpay.com?session_token=eyJhbGci..."
style="width: 100%; height: 600px; border: none;"
allow="camera; microphone"
></iframe>
The widget communicates with your page via postMessage. Listen for events on the parent window:
window.addEventListener("message", (event) => {
const { type, payload } = event.data ?? {};
if (!type?.startsWith("ZBD_")) return;
switch (type) {
case "ZBD_WIDGET_READY":
console.log("Widget loaded", payload?.user);
break;
case "ZBD_CASHOUT_SUCCESS":
console.log("Cashout submitted", payload);
// Close the widget iframe, refresh balances
break;
case "ZBD_WIDGET_CLOSE":
// User closed the widget — remove the iframe
break;
}
});
| Event | Payload | Description |
|---|
ZBD_WIDGET_READY | { user? } | Widget loaded and authenticated |
ZBD_WIDGET_CLOSE | — | User closed the widget |
ZBD_CASHOUT_SUCCESS | { cashout_id, amount, currency_code, usd_equivalent } | Cashout submitted successfully |
ZBD_KYC_COMPLETE | { tier } | Identity verification approved |
ZBD_METHOD_ADDED | { payout_method_id, type, label } | Bank account linked via Plaid |
ZBD_AUTH_TOKEN_READY | { auth_token, user? } | OTP verified, auth JWT available |
ZBD_WIDGET_ERROR | { code, message } | An error occurred |
Embed Parameters
Pass these as URL query parameters on the widget URL:
| Parameter | Required | Description |
|---|
session_token | Yes | JWT from Create Session |
flow | No | cashout (default), kyc, add-method |
theme | No | zbd-default, zbd-light |
embed | No | true for chrome-less mode (no header/footer) |
component | No | balance, history, method-picker for standalone components |
Webhooks
After a cashout is initiated, ZBD sends lifecycle webhooks to the webhook_url you provided when creating the session. The webhook_url must be HTTPS.
Initiated
Sent immediately after the cashout is submitted and the user’s balance is debited. Includes fee_amount.
{
"event": "ZBD_WIDGET.V1.CASHOUT.INITIATED",
"timestamp": "2026-05-28T22:21:03.067Z",
"data": {
"payout_id": "f4ba94c1-...",
"user_account_id": "4ac4fd8a-...",
"amount": "10.00",
"currency": "USD",
"payment_method": "ach",
"status": "initiated",
"reference_id": "player-42",
"fee_amount": "0.50"
}
}
Completed
Sent when the ACH transfer settles at the bank (typically 1–3 business days).
{
"event": "ZBD_WIDGET.V1.CASHOUT.COMPLETED",
"timestamp": "2026-05-30T14:12:00.000Z",
"data": {
"payout_id": "f4ba94c1-...",
"user_account_id": "4ac4fd8a-...",
"amount": "10.00",
"currency": "USD",
"payment_method": "ach",
"status": "completed",
"reference_id": "player-42"
}
}
Failed
Sent when the ACH transfer is rejected by the bank or payment processor. Includes error_reason.
{
"event": "ZBD_WIDGET.V1.CASHOUT.FAILED",
"timestamp": "2026-05-30T14:12:00.000Z",
"data": {
"payout_id": "f4ba94c1-...",
"user_account_id": "4ac4fd8a-...",
"amount": "10.00",
"currency": "USD",
"payment_method": "ach",
"status": "failed",
"reference_id": "player-42",
"error_reason": "Insufficient funds at receiving bank"
}
}
Payload Fields
| Field | Type | Description |
|---|
payout_id | UUID | Unique cashout identifier |
user_account_id | UUID | ZBD user ID |
amount | string | Decimal in major units (e.g. "9.50" = $9.50) |
currency | string | Always USD |
payment_method | string | Always ach |
status | string | initiated, completed, or failed |
reference_id | string | Your reference_id from Create User (null if not linked) |
fee_amount | string | Fee deducted (only on initiated) |
error_reason | string | Failure reason (only on failed) |
ACH returns (post-settlement reversals) do not trigger a webhook. They are handled internally by ZBD.