> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tesouro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pre-populating an application

> Create a bank account application from your backend with the details you already hold, then let the onboarding component find it and show the applicant a form that is already filled in.

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](/embedded-banking/guides/embedded-components/onboarding), 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](/embedded-banking/guides/bank-account-applications/overview#integration-shapes). 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](/embedded-banking/guides/users/widget-tokens). 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](/embedded-banking/guides/users/widget-tokens) carries as the `email` claim on its subject token. This is the join; see [Matching the applicant](#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

<Steps>
  <Step title="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.

    ```bash title="Create Request" lines theme={null}
    curl -X POST 'https://api.sandbox.tesouro.com/embedded-banking/v1/bank-account-applications' \
      -H 'Authorization: Bearer ACCESS_TOKEN' \
      -H 'X-Organization-Reference: northfield-cust-4821' \
      -H 'Content-Type: application/json' \
      -d '{
        "applicant": {
          "workEmailAddress": "alex@maplestreetcoffee.com",
          "firstName": "Alex",
          "lastName": "Chen",
          "mobilePhoneNumber": "5553452343",
          "homeAddress": {
            "address1": "234 Main St",
            "city": "Phoenix",
            "state": "AZ",
            "postalCode": "85001",
            "countryCode": "USA"
          }
        },
        "businessDetails": {
          "legalName": "Maple Street Coffee LLC",
          "dbaName": "Maple Street Coffee",
          "businessStructureType": "LLC",
          "phoneNumber": "5553453434",
          "website": "https://www.maplestreetcoffee.com",
          "northAmericanIndustryClassificationSystemCode": "SNACK_AND_NONALCOHOLIC_BEVERAGE_BARS",
          "address": {
            "address1": "123 Main St",
            "city": "Phoenix",
            "state": "AZ",
            "postalCode": "85001",
            "countryCode": "USA"
          }
        }
      }'
    ```

    `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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.

    ```tsx lines theme={null}
    <EmbeddedProvider
      userId={currentUser.id}
      userEmail={currentUser.email}
      widgetTokenRefreshUrl="/api/widget-token"
    >
      <OnboardingWidget bankingDashboardHref="/banking/dashboard" />
    </EmbeddedProvider>
    ```

    The component holds off rendering until the lookup settles, so the applicant never sees an empty form fill itself in a frame later.
  </Step>

  <Step title="The applicant confirms, corrects and submits">
    From here the flow is the ordinary one: edits, verification, submission. See [Editing and submitting](/embedded-banking/guides/bank-account-applications/submitting).
  </Step>
</Steps>

## 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](/embedded-banking/guides/authentication/application-client-secret#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](/embedded-banking/guides/bank-account-applications/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](/embedded-banking/guides/embedded-components/onboarding#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:

|                                             | Pre-populated application                                            | Props                                          |
| :------------------------------------------ | :------------------------------------------------------------------- | :--------------------------------------------- |
| Where the values start                      | Your backend, stored on the application before the applicant arrives | Your page, passed into the component at render |
| Stored if the applicant never finishes      | Yes                                                                  | No                                             |
| Reaches the applicant on a different device | Yes                                                                  | No                                             |
| Your backend can keep editing it            | Yes                                                                  | No                                             |

The two combine. Where both supply a field, the stored application wins, because it is the version the server will actually submit.

<Note>
  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.
</Note>
