Payee Widget
Embed Payment Labs payee onboarding and the payee dashboard in your own site with an iframe.
The payee widget is the Payment Labs payee experience in an iframe on your own site. Your payees onboard (personal details, payout method, tax forms, identity check) and follow their payments without leaving your product. Payment Labs keeps the screens up to date with each country's compliance rules, so you do not build or maintain them.
Before you start
- Any payout role can get a widget URL.
- The page that hosts the iframe must be served over HTTPS.
- Know the payee's email and your
referenceIdfor them. See Payee Reference IDs.
Step 1: Get a widget URL from your server
Call Get Widget URL from your backend. Never call it from the browser: it needs your API token.
curl -X PUT https://api-sandbox.paymentlabs.io/payors/widget-url \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payeeEmail": "[email protected]",
"payeeReferenceId": "user-1001",
"taxJurisdiction": "us"
}'| Field | Required | Description |
|---|---|---|
payeeEmail | Yes | The payee's email. |
payeeReferenceId | Yes | Your ID for the payee. |
taxJurisdiction | No | One of the tax jurisdictions enabled on your account (taxJurisdictions in Get Payor), or none. Decides which tax form the payee is asked for. |
redirectPath | No | A Base64-encoded path inside the widget to open first. See Step 4. |
{
"url": "https://widget-sandbox.paymentlabs.io/?config=eyJwYXllZSI6...&_s=MEUCIQ..."
}What the call does:
- It finds the payee by email and reference ID, or creates them, with the same rules as every other payee operation (Payee Reference IDs). A new payee may be sent an invitation email.
- It returns a signed URL for that payee. Do not change any part of it; an edited URL fails its signature check and the widget shows an error.
- The payee still signs in inside the widget the first time, with the email you sent.
Generate a new URL each time you render the widget instead of storing one.
Step 2: Render the iframe
<iframe
id="payee-widget"
title="Payouts"
src="WIDGET_URL"
allow="camera; clipboard-write"
style="width: 100%; border: 0;"
></iframe>| Attribute | Why the widget needs it |
|---|---|
src | The url from Step 1. |
allow="camera; clipboard-write" | camera lets the identity verification step scan an ID with the device camera. clipboard-write lets the payee copy values such as payment IDs. |
No sandbox attribute | The widget runs scripts, submits forms, keeps its session and opens popups (for example to connect PayPal). Leave sandbox off. |
If your security policy requires sandbox, it must include at least allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads. Without allow-scripts and allow-same-origin the widget does not load at all.
If your site sets a Content-Security-Policy, add the widget origin to frame-src: https://widget-sandbox.paymentlabs.io in sandbox and https://widget.paymentlabs.io in production.
Step 3: Handle the widget's events
The widget sends messages to your page with window.postMessage. Each message is an object with an id:
id | When it is sent | Extra fields | What to do |
|---|---|---|---|
PL_APP_MOUNTED | The widget has started. | — | Hide your loading indicator. |
PL_APP_HEIGHT | The widget's content height changes. | height: number, in pixels | Set the iframe height, so the iframe never shows its own scrollbar. |
PL_APP_RELOAD | The widget needs a fresh start, for example after the payee's session expired and they chose to continue. | — | Get a new URL from Step 1 and load it in the iframe. |
Accept messages only from the widget origin:
const WIDGET_ORIGIN = "https://widget-sandbox.paymentlabs.io"; // https://widget.paymentlabs.io in production
const iframe = document.getElementById("payee-widget") as HTMLIFrameElement;
const loader = document.getElementById("widget-loader") as HTMLElement;
async function loadWidget(): Promise<void> {
// Your own backend endpoint that calls Get Widget URL and returns { url }.
const response = await fetch("/api/payee-widget-url", { method: "POST" });
const { url } = await response.json();
iframe.src = url;
}
window.addEventListener("message", (event: MessageEvent) => {
if (event.origin !== WIDGET_ORIGIN || event.source !== iframe.contentWindow) {
return;
}
const data = event.data as { id?: string; height?: number };
switch (data.id) {
case "PL_APP_MOUNTED":
loader.hidden = true;
break;
case "PL_APP_HEIGHT":
if (typeof data.height === "number") {
iframe.style.height = `${data.height}px`;
}
break;
case "PL_APP_RELOAD":
void loadWidget();
break;
}
});
void loadWidget();Do not give the iframe a fixed height. Size it from PL_APP_HEIGHT instead.
Step 4: Deep link from Payment Labs emails
Payment Labs emails payees when something needs their attention, such as a new payment or a payout method that was rejected. Normally those emails link to the Payment Labs payee portal. If you embed the widget, Payment Labs can make them link to your page instead:
- Ask Payment Labs to set your email base URL, for example
https://www.example.com/payouts. - Each email then links to that URL with a
redirectPathquery parameter, for examplehttps://www.example.com/payouts?redirectPath=L3BheWVlL3BheW1lbnRzL1AtMDFK.... The value is the Base64-encoded path of the widget screen to open. - When your page loads with a
redirectPathparameter, pass the value unchanged in theredirectPathfield of Get Widget URL. - The widget opens on that screen once the payee has signed in.
Do not decode or re-encode the value. A value that is not valid Base64 is ignored and the widget opens on its home screen.
Track the payee
The widget does not tell your page when the payee becomes payable. Listen for the payee-payability-changed webhook on your server, or read the payee with Search Payees. See Payees and Onboarding.
Customize the look
Set the widget's colors and logo in the dashboard, under Settings → Account Theming (Administrator role). The widget uses that theme.
Errors and edge cases
| Error | Meaning | What to do |
|---|---|---|
400 VALIDATION_ERROR | payeeEmail or payeeReferenceId is missing, the email is invalid, taxJurisdiction is unknown, or the body has a field the API does not accept. | Fix the body. |
400 EMAIL_ALREADY_IN_USE | The reference ID belongs to one payee and the email to another. | See Payee Reference IDs. |
422 INVALID_BODY | The body is not valid JSON. | Send JSON. |
| The widget shows an "invalid session" screen | The URL was changed, or it was opened on a different domain than the one it was signed for. | Use the URL exactly as returned. |
| Blank iframe | A sandbox attribute or a Content Security Policy is blocking it. | Check Step 2. |
Next steps
Updated 15 days ago
What’s Next
Next: Payee Reference IDs.
