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

# Onboard & Verify a Business

> Run the business happy path end to end — configure a business-only application, send an owner through OAuth, register the legal entity, and track KYB to an approved business account.

This Quickstart is [Onboard & Connect a Customer](/quickstart/onboard-customers) for businesses. You'll configure an application that can only ever land on a business account, walk an owner through the OAuth grant, register their legal entity and beneficial ownership roster, track the KYB case to a decision, and prove the connection by issuing a card on the business account.

The person in front of you is always an individual first. They sign in as themselves, they get identity-verified as themselves, and *then* they register a business. Everything below follows that order.

<Info>
  **Prerequisites**

  * A **Fluz account with a staging application** — see [Prepare your accounts](/get-started/prepare-accounts) and [API credentials](/get-started/api-credentials).
  * A `client_id`, `client_secret`, and registered `redirect_uri` — see [Configure OAuth App](/create-an-o-auth-app).
  * Requests go to the **sandbox** — no real money, no real PII. See [Staging vs. Live Environment](/concepts/environments).
</Info>

## The full flow

<Steps>
  <Step title="Configure the application for businesses" icon="sliders">
    Two permission lists live on the **Permissions** tab of your app, and they are edited independently.

    | List                     | What to put on it                                                                                                                                                               |
    | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
    | **Permissions**          | What your app may do on a **personal** account. Include the permission to **register a business** — it is granted by the personal account, so it belongs here and nowhere else. |
    | **Business permissions** | What your app may do on a **business** account once one exists.                                                                                                                 |

    A non-empty **Business permissions** list is what makes your app business-enabled. Leave it empty and users are never offered the option to apply for a business account, no matter what else you configure.

    While you're in the app editor, register your `redirect_uri` on the **OAuth** tab and add a webhook URL subscribed to **KYB status update** — that's how you'll learn the review finished without polling.

    <Warning>
      The consent screen is built from these lists, not from your authorize URL. A permission you don't select here is never offered to the user and can never appear on a token. Full detail: [Business Accounts in OAuth](/business-accounts-in-o-auth).
    </Warning>
  </Step>

  <Step title="Ask Fluz to restrict the app to business accounts" icon="building-lock">
    Fluz can configure your application so the flow never settles on a personal account. With that in place, a user who has no business account skips the account picker entirely and goes straight into business registration.

    <Warning>
      **This isn't self-serve.** Contact your Fluz account manager to have the restriction applied to your application. Until it is, users with no business account are offered their personal account alongside the option to apply for a business one — and some of them will pick the personal account.
    </Warning>

    Skip this if your app legitimately serves both consumers and businesses. Asking for it is a statement that a personal-account grant is always a bug for you.
  </Step>

  <Step title="Send the owner through the OAuth grant" icon="plug">
    Build the authorize URL:

    ```text Authorization URL theme={null}
        https://uni.staging.fluzapp.com/authorize
          ?response_type=code
          &client_id=<YOUR_OAUTH_CLIENT_ID>
          &redirect_uri=<YOUR_REGISTERED_REDIRECT_URI>
          &state=<UNGUESSABLE_VALUE_YOU_STORED>
          &external_id=<YOUR_ID_FOR_THIS_ACCOUNT>
    ```

    With the app restricted to business accounts, a first-time user sees: sign-in and 2FA, then a consent screen carrying **two groups** — the consumer permissions they're granting now, and the business permissions being pre-approved for the business they're about to create. The list is read-only; they accept all of it or they don't finish.

    They're redirected back to your `redirect_uri` with `?code=...&state=...`. Validate `state`, then capture the single-use `code` server-side.

    <Tip>
      Give the **business** its own `external_id`, derived from your own business record — not from the owning user. An external ID binds to one Fluz account on first use, so an ID you spend on someone's personal account can't be reused for their business.
    </Tip>

    Full parameter reference: [Client-Facing OAuth Grant Flow](/client-facing-o-auth-grant-flow).
  </Step>

  <Step title="Exchange the code for the applicant's token" icon="key">
    Same exchange as any other grant — Basic auth with base64 of `client_id:client_secret`:

    ```bash Exchange (cURL) theme={null}
        curl -X GET "https://uni.staging.fluzapp.com/token/exchange?code=<AUTH_CODE>&redirect_uri=<YOUR_REGISTERED_REDIRECT_URI>" \
          -H "Authorization: Basic <base64 of CLIENT_ID:CLIENT_SECRET>"
    ```

    The `redirect_uri` must byte-match the one you used at `/authorize`.

    <Note>
      The business doesn't exist yet, so this token belongs to the applicant's **personal** account — that is correct, and it's the token `registerBusiness` requires. Read the account off the exchange response and persist it rather than inferring it from your own records of who started the flow.
    </Note>
  </Step>

  <Step title="Verify the applicant's identity (KYC)" icon="id-card">
    KYB verifies the business and the *other* owners. It does not verify the applicant, so the applicant has to be verified before you register anything — otherwise registration fails with `ARG-0001`.

    Call `verifyUserInformation` with the applicant's token. In staging this test identity always returns `APPROVED`:

    <CodeGroup>
      ```graphql Mutation theme={null}
          mutation VerifyUserInformation(
            $firstName: String!
            $lastName: String!
            $streetLine1: String!
            $city: String!
            $state: String!
            $postalCode: String!
            $country: String!
            $dateOfBirth: String!
            $ssnLast4: String!
          ) {
            verifyUserInformation(
              firstName: $firstName
              lastName: $lastName
              streetLine1: $streetLine1
              city: $city
              state: $state
              postalCode: $postalCode
              country: $country
              dateOfBirth: $dateOfBirth
              ssnLast4: $ssnLast4
            ) {
              status
              message
            }
          }
      ```

      ```json Variables (approved test identity) theme={null}
          {
            "firstName": "John",
            "lastName": "Smith",
            "streetLine1": "222333 Peachtree Place",
            "city": "Atlanta",
            "state": "GA",
            "postalCode": "30318",
            "country": "United States",
            "dateOfBirth": "02/28/1975",
            "ssnLast4": "3333"
          }
      ```
    </CodeGroup>

    Which check the applicant needs depends on the `isUsPerson` value you'll send for them in the next step: `true` requires a successful SSN (CIP) verification on file, `false` requires a successful document verification. See [User KYC Verification](/user-kyc-verification) and [Testing KYC Flows](/test-kyc-flows).

    <Warning>
      A user can be submitted at most **3 times** before returning `ERROR`. Don't burn attempts on the user you're about to make an applicant.
    </Warning>
  </Step>

  <Step title="Register the business" icon="building">
    First resolve the category the entity trades under — never hardcode these UUIDs:

    ```graphql theme={null}
        query {
          getBusinessCategories {
            id
            name
            subCategories { id name }
          }
        }
    ```

    Then submit the entity and the full ownership roster in one call, still with the applicant's **personal-account** token:

    <CodeGroup>
      ```graphql Mutation theme={null}
          mutation RegisterBusiness($input: RegisterBusinessInput!) {
            registerBusiness(input: $input) {
              accountId
              kybStatus
              success
              error { code message }
            }
          }
      ```

      ```json Variables theme={null}
          {
            "input": {
              "businessName": "Acme Corporation",
              "dbaName": "Acme Co",
              "businessStructure": "LLC",
              "businessLegalAddress": {
                "streetAddressLine1": "123 Main Street",
                "city": "San Francisco",
                "state": "California",
                "postalCode": "94102",
                "country": "United States"
              },
              "stateOfIncorporation": "California",
              "taxId": "12-3456789",
              "businessCategoryId": "<FROM_getBusinessCategories>",
              "businessSubCategoryId": "<FROM_getBusinessCategories>",
              "natureOfBusiness": "E-commerce retail",
              "businessAccountUsage": ["CORPORATE_SPENDING_ADMIN"],
              "externalReferenceId": "your-business-id-123",
              "confirm": {
                "allOwnersWith25PercentageOwnershipListed": true,
                "noOwnersMoreThan25Percentage": false
              },
              "owners": [
                {
                  "firstName": "John",
                  "lastName": "Smith",
                  "emailAddress": "john@example.com",
                  "phoneNumber": "+14155551234",
                  "title": "Chief Executive Officer",
                  "ownershipPercentage": 100,
                  "isControlPerson": true,
                  "isInvited": false,
                  "isUsPerson": true
                }
              ]
            }
          }
      ```
    </CodeGroup>

    A success response returns an `accountId` and a `kybStatus` of `SUBMITTED`. Store the `accountId` immediately — it's your only handle on the application.

    Three things that reject most first attempts:

    * **`isUsPerson` is required on every owner**, including the applicant and invited owners. It's the single most common cause of a rejected roster.
    * **Exactly one owner must be the applicant** — matched by email or phone against the token's user — and exactly one must be the control person.
    * **The legal address is checked against an address-validation provider.** Invented streets fail with `BS-0002`; use [Test Addresses](/test-addresses).

    <Warning>
      Errors come back **inside the response payload**, not as GraphQL errors — branch on `success` and the `error` object. Full parameter and error reference: [Register a Business](/business-registration).
    </Warning>
  </Step>

  <Step title="Get the remaining owners verified, then wait" icon="users">
    `SUBMITTED` means the payload validated and a case opened. It does not mean approved.

    Mint a **business-account** token — `generateUserAccessToken` with the applicant's `userId` and the new business `accountId` — and read the roster:

    ```graphql theme={null}
        query GetBusiness {
          getBusiness {
            accountId
            kybStatus
            owners { id name verificationType status }
          }
        }
    ```

    Owners on the document path get a link from [requestOwnerDocumentVerificationLink](/owner-verification-link); owners you marked `isInvited: true` are emailed by Fluz and verify themselves. Keep going until `kybStatus` is final **and** every owner reports `READY`.

    The status moves `PENDING` → `APPROVED` or `DECLINED`, usually within one to two business days. Take the decision off the **KYB status update** webhook you configured in Step 1 and use `getBusiness` for reconciliation — hourly, not per page load.

    <Note>
      Show the user an honest "under review" state. Don't drop them into a business dashboard that can't transact yet, and don't auto-retry a decline — a second submission is blocked by `BS-0007`. Full lifecycle: [Register & Verify Businesses](/kyb-overview).
    </Note>
  </Step>

  <Step title="Operate on the business account — prove it" icon="credit-card">
    Once `kybStatus` is `APPROVED`, run any Fluz operation with the **business-account** token and it executes against the business. There is no separate business API.

    ```graphql theme={null}
        mutation {
          createVirtualCard(
            input: {
              idempotencyKey: "9f2c4d61-77aa-4b0e-8f2a-1c9d3e5b7a04"
              offerId: "ed669305-5e43-40a0-9a25-7a15ed174628"
              spendLimit: 250.00
              lockCardNextUse: true
              cardNickname: "First card on a verified business"
            }
          ) {
            virtualCardId
            virtualCardLast4
            status
          }
        }
    ```

    An `ACTIVE` card back means the loop is closed: configured → authorized → verified → registered → approved → operating.
  </Step>
</Steps>

## You're done 🎉

You've taken a business from an empty application to a verified account that can spend. From here:

<CardGroup cols={2}>
  <Card title="Business accounts in OAuth" icon="building-lock" href="/business-accounts-in-o-auth">
    The two permission lists, the account picker, and which account a code resolves to.
  </Card>

  <Card title="Register & verify businesses" icon="clipboard-check" href="/kyb-overview">
    Prerequisites, the KYB status lifecycle, and tracking a case to a decision.
  </Card>

  <Card title="Submit business documents" icon="file-arrow-up" href="/submit-business-documents">
    Authorized-signer uploads and responding to documentation requests.
  </Card>

  <Card title="Onboard & connect a customer" icon="user-plus" href="/quickstart/onboard-customers">
    The same journey for individuals.
  </Card>
</CardGroup>

<Note>
  **Want to learn more?** Contact us at [support@fluz.app](mailto:support@fluz.app) to speak with our experts or request a demo.
</Note>
