Bank Account Onboarding
Walks an applicant through bank-account onboarding: business details, personal details, optional additional owners, and a result screen.
Part ofEmbedded Banking
Walks an applicant through bank-account onboarding: business details, personal details, optional additional owners, and a result screen. The widget handles saving, validation, submission and waiting for the account to open; you supply auth and two callbacks.
Quick Start
The widget renders inline and fills its container, so mounting it is what shows it. Give the container a real height, typically a dedicated onboarding page or a route you navigate to from your own trigger (for example OfferWidget's primary CTA).
import { BankAccountOnboardingWidget } from '@tesouro/embedded-components-react';
export function OnboardingPage() {
return (
<div style={{ height: '100vh', minHeight: 600 }}>
<BankAccountOnboardingWidget
baseUrl="https://api.example.com"
widgetToken="wt_live_abc123"
organizationId="org_xyz"
onEmbeddedOnboardingCompletedSuccessfully={handleComplete}
onNavigateToDashboard={goToDashboard}
/>
</div>
);
}Behaviour
- The widget renders nothing until widget init and the organization's application status resolve, and nothing at all when the user is already
ACTIVE/INACTIVE. Each suppression emits anonboarding.suppressedanalytics event and callsonSuppressed(reason)— see Suppression reasons. - A draft your backend pre-populated resumes as the applicant's own when it holds their work email: the widget picks up its application client secret, so you pass no application ID or secret. Without the email it shows the "Application in progress" card.
- One mounted widget serves one applicant. Remount it (a new React
key) whenever the applicant changes. SwappingwidgetTokenin place is treated as a token rotation and keeps the form. onEmbeddedOnboardingCompletedSuccessfullyfires only when the account is fully open. An application still under review, declined, or slow to provision shows a "we'll email you" pending screen instead.- The first step has no Back button and there is no Cancel control, since the widget has nowhere to go back to; Back between completed steps still works.
- Save and continue later saves the application and confirms with a toast, leaving the applicant on the same step. Delete removes it, confirms with a toast, and starts a fresh application. The toasts render inside the widget, so keep it mounted.
- Disclosure acceptance is recorded once the account opens. If that fails, the success screen still shows and
AcceptDisclosuresWidgetis the retry path.
If the organization already has an application, the widget renders by its status:
| Application | Renders |
|---|---|
None, DELETED or CANCELED | The form |
DRAFT started by the applicant | The form, resumed |
DRAFT started by someone else | "Application in progress" card |
SUBMITTED (either applicant) | "Under review" card |
DENIED (either applicant), within 30 days | "Not approved" card |
DENIED more than 30 days ago, or COMPLETE | Nothing (onboarding.suppressed) |
The card replaces the form. Its copy comes from labels.applicationStatus. Set showApplicationStatusRefresh to add a Check again button to the card; the widget does not re-read the status on window focus. If a re-check finds a status the widget has no copy for, the card switches to labels.applicationStatus.unavailable and keeps the button, so the user can try again.
Suppression reasons
Not every reason means an account exists. If you unlock account-scoped navigation from onSuppressed, branch on the reason rather than treating them alike.
OnboardingSuppressionReason | Value | Account provisioned? |
|---|---|---|
UserAlreadyOnboarded | user_already_onboarded | Yes |
OrganizationApplicationComplete | organization_application_complete | Yes |
UserInvitedToOrganization | user_invited_to_organization | No — pending disclosure acceptance |
OrganizationApplicationStartedByOther | organization_application_started_by_other | No — another member's application is still in flight |
OrganizationApplicationDenialExpired | organization_application_denial_expired | No — the application was denied more than 30 days ago |
OrganizationApplicationStatusUnrecognized | organization_application_status_unrecognized | No — the application has a status the widget does not recognize |
Props
Widget props
| Prop | Type | Default | Description |
|---|---|---|---|
onEmbeddedOnboardingCompletedSuccessfullyEvent | () => void | Required | Called when the application is approved and the account is open. |
onNavigateToDashboardEvent | () => void | Required | Called when the user clicks the dashboard CTA on the result screen. |
initialBusinessDetails |
| undefined | Prefills step 2 (business details); the user can still edit. |
initialPersonalDetails |
| undefined | Prefills step 1 (personal details); the user can still edit. |
initialAdditionalOwners | HostSuppliedOwner[] | [] | Prefills the owners list on step 2. |
bankLogoSrc | string | undefined | Bank logo URL for the result screen; falls back to the bank name as text. |
disclosureLinks | DisclosureLinks | undefined | URLs for Terms of Use, Privacy Policy, Electronic Communication and Patriot Act on the Terms step. You resolve these; omit to leave unset. |
onSuppressedEvent |
| undefined | Called once the widget decides to render nothing, with the reason. Use it to swap in your own content instead of leaving the empty render. |
showApplicationStatusRefresh | boolean | false | Adds a Check again button to the application status card that re-reads the organization's application. Never shown over the form or result screens. |
onResultPhaseChangeEvent |
| undefined | Called with true once the widget starts processing a submitted application, and with false if it unmounts after that. While true it keeps its processing or result screen up whatever the status read says, so leave it mounted. |
Label overrides
| Prop | Type | Default | Description |
|---|---|---|---|
labelsLabels | BankAccountOnboardingWidgetLabels | English labels | Per-step and shell copy overrides, merged with the English defaults. |
Examples
Overriding labels
Pass only the strings you want to change; everything else falls back to English. See the exported BankAccountOnboardingWidgetLabels type for every key. APPLICATION_STATUS_LABELS_EN holds the English status-card copy, so an override can reuse it.
<BankAccountOnboardingWidget
labels={{
stepIndicator: {
personal: 'Personal',
business: 'Negocio',
review: 'Revisión',
terms: 'Términos',
done: 'Listo',
},
businessDetails: { heading: 'Datos de la empresa' },
result: { successHeadingLine1: 'Tu cuenta está lista' },
}}
onEmbeddedOnboardingCompletedSuccessfully={handleComplete}
onNavigateToDashboard={goToDashboard}
/>TypeScript
import type {
BankAccountOnboardingWidgetProps,
BankAccountOnboardingWidgetLabels,
DisclosureLinks,
OnboardingSuppressionReason,
ApplicationStatusLabels,
BusinessDetailsValues,
BusinessDetailsLabels,
PersonalDetailsValues,
PersonalDetailsLabels,
AdditionalOwner,
AdditionalOwnersLabels,
ResultLabels,
} from '@tesouro/embedded-components-react';