Skip to main content
If you already hold an applicant’s business and personal details, you can create the bank account application from your backend before the applicant ever sees a form. When they open the onboarding component, it finds that application, carries on with it, and renders your values in the fields. The applicant confirms and corrects rather than typing everything from scratch. This is a third integration shape alongside the two on the overview. The difference from “server creates, browser finishes” is that you never hand a credential to the browser. The component is not your UI, so there is nothing to pass it to. It authenticates itself against the application it finds.

How the component finds it

On start-up the component asks the API which application belongs to the signed-in applicant, and gets back the application’s id and the client secret it already carries. Three things have to line up for that answer to name your application:
  • The same OAuth client. The application has to be created by the same client whose client_id and client_secret you put in the widget token. An application created by a sibling client is not visible to this session at all.
  • The same applicant email. applicant.workEmailAddress on the application has to equal the address the applicant signs in with, which the widget token carries as the email claim on its subject token. This is the join; see Matching the applicant.
  • Still in DRAFT. A secret is handed back only while the application is editable. Once it is SUBMITTED there is nothing for the applicant to fill in.
Nothing is minted. The component receives the same secret your create call received, so your copy keeps working and a refresh, a second tab, or a retry all land on the same application.

Walking the flow

1

Create the application with what you hold

Set X-Organization-Reference to the same opaque business identifier you put in the widget token’s organization_reference claim, and include applicant.workEmailAddress. Everything else is optional, so send the fields you have and leave the rest out.
Create Request
X-Organization-Reference is what scopes the lookup to one of your business customers. Without it the API can still find the applicant’s own application by email, but it cannot tell that a colleague at the same business already has one in progress, which is the check that stops a second application being started.
2

Do nothing with the client secret

The create response carries clientSecret as always. On this flow you do not need it: the component gets its own copy when it looks the application up. Drop it. Do not log it, persist it, or forward it to the browser.Your backend can keep patching the application with its bearer token right up until the applicant opens the component, which is useful if details arrive from your own onboarding steps over several minutes.
3

Render the component as normal

There are no extra props and no application id to thread through. The component does the lookup itself from the widget token, so the same markup serves a pre-populated applicant and one starting from nothing.
The component holds off rendering until the lookup settles, so the applicant never sees an empty form fill itself in a frame later.
4

The applicant confirms, corrects and submits

From here the flow is the ordinary one: edits, verification, submission. See Editing and submitting.

Matching the applicant

The applicant’s work email is the only thing that ties a stored application to the person in front of the component. Get it wrong and the failure is quiet rather than loud. An application whose workEmailAddress does not match the signed-in applicant, but which shares an X-Organization-Reference with them, is reported as belonging to somebody else at that business. Since a business may hold only one bank account application, the component then hides onboarding entirely, so the applicant sees no form at all rather than a blank one. That is correct when a colleague really did start the application, and is exactly the wrong outcome when the mismatch is a typo or a stale address on your side. So: create the application with the same address the applicant signs in with, and if that address changes before they finish, patch applicant.workEmailAddress to the new one.

What the applicant sees

Everything you stored comes back in the form, and every value stays editable. Two fields behave differently, because a read never returns them in full:
  • SSN and tax ID come back as their last four digits only. The field renders empty next to those four digits, and leaving it empty keeps what you stored. The applicant retypes one only if they want to change it.
  • Disclosure acceptance is never carried over. The applicant ticks the agreement in their own session, whatever your create call sent.
Beneficial owners are replaced as a set rather than merged, so the list the applicant sees is exactly the list on the application. If they delete one, it is gone.

How this interacts with resume

The secret the component picks up is the application’s own, and it expires like any other. See Expiry and refresh. A pre-populated application nobody opens runs past that window, and once it has, the lookup reports the application without a secret and the component cannot carry on with it. Resuming is the fix, and it is the same call as always: your backend posts the applicant’s email, a fresh secret is generated, stored on the application, and emailed to them. Storing it is the part that matters here. The component’s next look-up finds a live secret and picks the application up exactly as it would have on day one. The emailed copy is for an applicant working in a browser you handed a secret to; on this flow nobody has to open it. So the two pages solve different problems despite the shared word. Resume re-issues a credential that ran out. This page is about an application the browser was never handed a credential for in the first place.

Not the same as pre-filling through props

The onboarding component also accepts pre-filled values as props, documented under Pre-filling onboarding data. That path seeds the form in the browser and nothing is stored until the applicant presses Next. Pick whichever fits where your data already lives: The two combine. Where both supply a field, the stored application wins, because it is the version the server will actually submit.
The same flow will apply to credit card applications once those are public. The API for them is not available today, so there is nothing to integrate against yet.