@relm-dev/onboard-sdk
v0.0.3
Published
Embed the Relm customer onboarding flow (DynamicOnboardingFlow) inside any partner web app. Pass an access token + refresh token and launch it.
Maintainers
Readme
@relm-dev/onboard-sdk
Embed the Relm customer onboarding flow (the in-house DynamicOnboardingFlow) inside any partner web app. You give it an access token and refresh token; it launches the fully-hosted flow in a secure iframe and calls you back when the customer is done.
The onboarding UI itself runs unchanged on the Relm platform — this package only mounts it, performs the token handshake, and surfaces lifecycle callbacks. It reuses the same iframe + postMessage mechanism as the existing Relm payroll SDK.
Install
npm install @relm-dev/onboard-sdk
# or
yarn add @relm-dev/onboard-sdkreact / react-dom are optional peer dependencies — only required if you use the /react entry.
Quick start (framework-agnostic)
import { RelmOnboarding } from '@relm-dev/onboard-sdk';
const onboarding = new RelmOnboarding({
providerAccessToken: '<provider-access-token>',
providerRefreshToken: '<provider-refresh-token>',
flavor: 'LIVE', // 'LOCAL' | 'SANDBOX' | 'PREPROD' | 'LIVE'
onComplete: (payload) => console.log('Onboarding complete', payload),
onError: (err) => console.error('Onboarding error', err),
onSessionExpired: () => redirectToLogin(),
onClose: () => console.log('Widget closed'),
});
// Launch it (opens a centered modal by default)
document.querySelector('#start')?.addEventListener('click', () => onboarding.open());init/open/close factory style
Mirrors the existing payroll SDK shape:
import { createOnboardingWidget } from '@relm-dev/onboard-sdk';
const widget = createOnboardingWidget({ providerAccessToken, providerRefreshToken, flavor: 'SANDBOX' });
widget.open();
// widget.close();
// widget.destroy();React usage
import { RelmOnboardingWidget } from '@relm-dev/onboard-sdk/react';
export function Onboard({ providerAccessToken, providerRefreshToken }) {
return (
<RelmOnboardingWidget
providerAccessToken={providerAccessToken}
providerRefreshToken={providerRefreshToken}
flavor="LIVE"
onComplete={() => console.log('done')}
/>
);
}Control it imperatively with a ref and autoOpen={false}:
const ref = useRef(null);
<RelmOnboardingWidget ref={ref} autoOpen={false} providerAccessToken={a} providerRefreshToken={r} />;
// ref.current.open() / ref.current.close()Inline (embed in your own layout)
<div id="onboard-host" style={{ width: 1200, height: 700 }} />;
new RelmOnboarding({
providerAccessToken, providerRefreshToken,
mode: 'inline',
mount: '#onboard-host',
}).open();Configuration
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| providerAccessToken | string | — | Required. Short-lived onboarding access token issued by the provider (Relm) — not the partner's own app token. |
| providerRefreshToken | string | — | Required. Provider refresh token used to silently renew the session. |
| flavor | 'LOCAL' \| 'SANDBOX' \| 'PREPROD' \| 'LIVE' | 'LIVE' | Environment preset (maps to an embed origin). |
| baseUrl | string | — | Explicit app origin; overrides flavor. |
| route | string | '/customer' | Route that renders the onboarding flow. |
| partnerId | string | — | Forwarded to the app for partner branding. |
| mode | 'drawer' \| 'modal' \| 'inline' | 'modal' | Presentation style (modal is centered). |
| width | string | min(1120px, calc(100vw - 48px)) | CSS width of the panel (drawer/modal). |
| height | string | min(760px, calc(100vh - 40px)) | CSS height of the panel (drawer/modal). |
| mount | string \| HTMLElement | — | Target for inline mode. |
| showHeader | boolean | false | Show a titled header bar with close button. |
| showCloseButton | boolean | true | Show a small floating close (X) when the header is hidden. |
| title | string | 'Complete your verification' | Header title (only used when showHeader is true). |
| closeOnEscape | boolean | true | Close drawer/modal on Esc. |
| zIndex | number | 2147483000 | Overlay base z-index. |
Callbacks
| Callback | Fired when |
| --- | --- |
| onReady() | The app acknowledged the handshake and is displaying the flow. |
| onComplete(payload) | The customer finished onboarding. The widget then closes. |
| onError(payload) | The app reported an error. |
| onSessionExpired() | The token is no longer valid. The widget closes. |
| onClose() | The widget was closed (by the user, host, or completion). |
How the handshake works
- The SDK renders an iframe pointed at
${baseUrl}${route}?a=<access>&r=<refresh>(the URL params are a bootstrap fallback and are stripped from history by the app on load). - On
iframe.onload, the SDK postsINIT_WIDGET(with the tokens) to the app, retrying until the app repliesWIDGET_READY. - The app stores the tokens, enters widget mode, and runs the flow. Its API layer transparently refreshes the access token using the refresh token.
- The app posts
ONBOARDING_COMPLETE,ONBOARDING_ERROR,SESSION_EXPIRED, orCLOSE_WIDGET, which the SDK maps to your callbacks.
All messages are origin-checked against the resolved baseUrl.
Notes
- The tokens are only ever sent to the resolved Relm origin.
- This package requires a browser environment (it touches
window/document). - The hosted platform behaviour is unchanged when the flow is opened directly (non-widget) — widget-only messaging is gated behind widget mode.
