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_idandclient_secretyou 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.workEmailAddresson the application has to equal the address the applicant signs in with, which the widget token carries as theemailclaim 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 isSUBMITTEDthere is nothing for the applicant to fill in.
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 whoseworkEmailAddress 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.
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.