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 referenceId for 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"
  }'
FieldRequiredDescription
payeeEmailYesThe payee's email.
payeeReferenceIdYesYour ID for the payee.
taxJurisdictionNoOne of the tax jurisdictions enabled on your account (taxJurisdictions in Get Payor), or none. Decides which tax form the payee is asked for.
redirectPathNoA 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>
AttributeWhy the widget needs it
srcThe 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 attributeThe 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:

idWhen it is sentExtra fieldsWhat to do
PL_APP_MOUNTEDThe widget has started.—Hide your loading indicator.
PL_APP_HEIGHTThe widget's content height changes.height: number, in pixelsSet the iframe height, so the iframe never shows its own scrollbar.
PL_APP_RELOADThe 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:

  1. Ask Payment Labs to set your email base URL, for example https://www.example.com/payouts.
  2. Each email then links to that URL with a redirectPath query parameter, for example https://www.example.com/payouts?redirectPath=L3BheWVlL3BheW1lbnRzL1AtMDFK.... The value is the Base64-encoded path of the widget screen to open.
  3. When your page loads with a redirectPath parameter, pass the value unchanged in the redirectPath field of Get Widget URL.
  4. 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

ErrorMeaningWhat to do
400 VALIDATION_ERRORpayeeEmail 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_USEThe reference ID belongs to one payee and the email to another.See Payee Reference IDs.
422 INVALID_BODYThe body is not valid JSON.Send JSON.
The widget shows an "invalid session" screenThe 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 iframeA sandbox attribute or a Content Security Policy is blocking it.Check Step 2.

Next steps


What’s Next

Did this page help you?