Onboarding Element
The Onboarding element embeds the frameOS KYC and compliance flow directly in your application as a modal. Unlike hosted onboarding sessions, no redirect is required — the flow opens inline and your users never leave your page.
Before opening the component, your server creates the account, provisions capabilities, and mints a session — then hands just the client_secret to the browser.
<script src="https://js.framepayments.com/v1/index.js"></script>
<script>
(async () => {
const onboarding = await Frame.createOnboarding("<PUBLISHABLE_KEY>");
onboarding.setAttribute("merchant-name", "My Company");
onboarding.onSuccess = (account) => {
console.log("Onboarding complete", account.id);
};
onboarding.onClose = () => {
console.log("Modal closed");
};
// Your server mints the onboarding session and returns the client_secret.
// Your secret key never touches the browser.
const { client_secret } = await fetch("/api/onboarding/start", {
method: "POST",
}).then((r) => r.json());
onboarding.open({ clientSecret: client_secret });
})();
</script>
How a Frame integration is structured
Every Frame integration involves three actors:
Your frontend runs frame-js in the browser. It holds your publishable key (pk_*) — safe to expose — and a short-lived client_secret that scopes it to a single session. It never sees your secret key.
Your backend holds your secret key (sk_*) and owns everything that requires it: creating accounts, provisioning capabilities, minting sessions, receiving webhooks. It can use a Frame server SDK or call the Frame API directly. It hands only the client_secret to your frontend — nothing more.
The Frame API is the source of truth. Your backend authenticates with your secret key. Your frontend authenticates with the client_secret. The two paths are intentionally separate so your secret key never has a route to the browser.
For onboarding specifically: your backend creates the account and mints the session, passes client_secret to your frontend, and your frontend calls onboarding.open({ clientSecret }). The component handles everything from there.
Before you begin
Account creation, capability provisioning, and session minting all happen server-side using your secret key. 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.
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 Capabilities for the full list of available capabilities.
Prefill as much profile information as you know when creating the account — this reduces the number of fields the user has to fill in and improves completion rates.
2. Mint an onboarding session
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>"
}'
3. Pass the client_secret to your frontend and call onboarding.open({ clientSecret }).
API Reference
Frame.createOnboarding(publishableKey)
A lightweight factory that creates the onboarding web component. Unlike Frame.init(), this does not load Evervault, FingerprintJS, or Sift — it only loads what's needed for the onboarding flow.
Parameters
Your publishable API key from the Frame Dashboard.
Returns
Returns a FrameOnboarding element instance.
const onboarding = await Frame.createOnboarding("<PUBLISHABLE_KEY>");
Attributes
Set attributes on the element to customize the modal appearance.
Attributes
Display name shown inside the onboarding modal.
URL of the merchant logo shown in the modal header.
onboarding.setAttribute("merchant-name", "My Company");
onboarding.setAttribute("merchant-logo", "https://example.com/logo.png");
onboarding.open(options)
Bootstraps the onboarding session and opens the modal. The clientSecret is the client_secret field from the onboarding session your server minted.
Parameters
The client_secret returned by POST /v1/onboarding_sessions. Minted server-side — never hardcoded in the browser.
onboarding.open({ clientSecret: "onb_sess_..." });
onboarding.close()
Programmatically closes the modal.
onboarding.close();
Callbacks
Assign callbacks directly on the element to respond to onboarding events.
Callbacks
Called when all onboarding steps have been completed successfully. Receives the completed account object.
Called when a step encounters an error.
Called each time an individual step is completed. Receives the step key and the updated account object.
Called when the user closes the modal.
onboarding.onSuccess = (account) => {
console.log("All steps complete", account.id);
};
onboarding.onError = ({ step, error }) => {
console.error("Error on step", step, error.message);
};
onboarding.onStepComplete = (step, account) => {
console.log("Step complete:", step, account.id);
};
onboarding.onClose = () => {
console.log("Modal closed");
};
Events
The element also dispatches DOM CustomEvents that bubble up the tree — useful when you can't hold a direct reference to the element.
Events
Fired when all onboarding steps are complete. event.detail contains { account, session }.
Fired each time a single step is completed. event.detail contains { step, account }.
Fired when the onboarding session has expired (server returns 410). Prompt the user to restart.
Fired when a step encounters an error. event.detail contains { step, error }.
Fired when the modal is closed by the user or programmatically.
onboarding.addEventListener("frame:complete", (e) => {
console.log("Complete", e.detail.account.id);
});
onboarding.addEventListener("frame:expired", () => {
// Session expired — restart the onboarding flow
startOnboarding();
});
onboarding.addEventListener("frame:error", (e) => {
console.error("Error on step", e.detail.step, e.detail.error.message);
});