Skip to content

Embed onboarding ​

Run Zay's onboarding inside an <iframe> on your own page, instead of redirecting the user away.

We recommend the redirect — see Onboard a user. Embed only if keeping the user on your page matters more than the extra work below. Everything else — the URL, the webhook you wait for, what to do when a user doesn't finish — is the same in both.

Requirements ​

RequirementDescription
A userCreated with Create user — you need their onboarding_url
A webhook endpointRegistered and receiving events — see Get started, steps 2–3
An HTTPS pageThe page that hosts the frame. Camera access only works on a secure origin

How it works ​

  1. Add the frame
  2. Listen for the end of the flow
  3. Wait until the user can be paid

1. Add the frame ​

Point the frame at the user's onboarding_url. Identity verification needs the camera and microphone, so delegate both to the frame — without allow, the browser blocks them inside it and the user cannot finish:

html
<iframe
  src="https://privy-stag.zay.me/onboarding?merchant_id=YOUR_MERCHANT_ID&user_id=USER_ID"
  allow="camera; microphone"
  title="Zay onboarding"
  style="border: 0; width: 100%; min-height: 720px;"
></iframe>
  • Give the frame the full width of your content column and at least about 720px of height — the identity-verification step is a full screen of its own.
  • Don't add a sandbox attribute: the onboarding opens its own sign-in and verification frames and needs scripts, forms and popups.
  • callback_url is ignored inside a frame. Use the message in the next step instead.

2. Listen for the end of the flow ​

When the user reaches the end of the web flow, the frame posts a message to your page:

json
{
  "type": "zay:onboarding",
  "status": "card_is_ready",
  "step_data": {}
}

Listen for it, and check that it comes from Zay's host:

javascript
const ZAY_ORIGIN = "https://privy-stag.zay.me"; // https://privy.zay.me in production

window.addEventListener("message", (event) => {
  if (event.origin !== ZAY_ORIGIN) return;
  if (event.data?.type !== "zay:onboarding") return;

  // The user is done with the frame: close it, or show your next screen.
  showOnboardingFinished(event.data.status);
});

status says where the user left off — for example card_is_ready, card_issuing_failed, kyc_declined or residence_country_waitlisted — so you can choose what to show. The message is sent once per page load, when there is nothing left for the user to do in the frame: usually after their card is issued and activated, a few minutes after they submit identity verification.

The message does not mean the user can be paid. Don't decide anything from it.

3. Wait until the user can be paid ​

Exactly as with the redirect: when the user's card is ready to spend, Zay sends user.onboarding_completed to your webhook endpoint, with the wallet_address to pay. See Onboard a user.