Embed onboarding

The embedded onboarding component opens the frameOS KYC and compliance flow as an inline modal — your users never leave your page. Before the browser can open the modal, your backend needs to create the account, provision capabilities, and mint a session.

All three steps use your secret key and must run server-side. Your secret key never touches the browser.

1. Create an account with capabilities

Capabilities determine which steps appear in the onboarding flow. Provision only what your integration needs — adding capabilities later is fine, but every capability adds steps to the flow.

REQUEST
curl --request POST \
  --url https://api.framepayments.com/v1/accounts \
  --header 'Authorization: Bearer YOUR_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "type": "individual",
  "profile": {
    "individual": {
      "email": "user@example.com"
    }
  },
  "capabilities": ["kyc", "phone_verification", "card_receive"]
}'

See the Capabilities concept for the full list of available capabilities.

2. Mint an onboarding session

A session scopes the publishable key in the browser to one onboarding flow against this account. Mint a fresh session per flow — don't cache them.

REQUEST
curl --request POST \
  --url https://api.framepayments.com/v1/onboarding_sessions \
  --header 'Authorization: Bearer YOUR_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "account_id": "<ACCOUNT_ID>"
}'

The response includes a client_secret. That's the only field your frontend needs.

3. Pass client_secret to the frontend

Return the client_secret from your own endpoint and call onboarding.open() in the browser.

OPEN THE COMPONENT
const onboarding = await Frame.createOnboarding("<PUBLISHABLE_KEY>");

const { client_secret } = await fetch("/api/onboarding/start", {
  method: "POST",
}).then((r) => r.json());

onboarding.open({ clientSecret: client_secret });

The component handles every step of the flow from there. Listen for frame:complete (or set onSuccess) on the element to know when the user finishes; see the embedded onboarding reference for the full event surface.

Reconcile from webhooks, not events

The browser fires onSuccess / frame:complete the moment the user finishes the last step in the modal. Webhooks confirm what Frame actually accepted — they may arrive slightly later and they're the authoritative signal for downstream state. Treat the browser event as a UI cue ("show the success screen"); treat the webhook as the source of truth ("mark this account onboarded in our database").