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

# KYB WebSDK

> The hosted verification flow: launch a business's people into Etherfuse, and they complete every step in the widget.

You launch a person into the business's verification; they and the rest of the business's owners and representatives do everything else in the widget at `/kyb`.

## The flow

<Steps>
  <Step title="(Optional) Pre-create the business and invite its members">
    Skip this and your customer creates the business themselves on first launch, and the app asks them for a company name, legal name, and country. Pre-create it when you want the organization and its members in place before anyone launches.

    Create the business with [`POST /ramp/organization`](/api-reference/organizations/create-child-org) and `accountType: business`. You may pass your own `id` (a UUID) to match an identifier in your own system; omit it and Etherfuse assigns one and returns it as `organizationId`. Pass `country` to generate the KYB onboarding at creation so the user lands straight in verification, and with it `legalName`, the incorporated name on the company's registry documents. `displayName` stays a friendly label.

    <CodeGroup>
      ```json Request theme={null}
      {
        "displayName": "Acme Inc.",
        "legalName": "Acme Corporation S.A. de C.V.",
        "accountType": "business",
        "country": "MX"
      }
      ```

      ```json Response theme={null}
      {
        "organizationId": "00000000-0000-4000-8000-000000000001",
        "displayName": "Acme Inc.",
        "accountType": "business"
      }
      ```
    </CodeGroup>

    Now add the people who will complete KYB with [`POST /ramp/organization/00000000-0000-4000-8000-000000000001/member`](/api-reference/organizations/add-organization-member), two ways:

    * **By `email`**: the invite is accepted automatically the first time that email launches via your `kyb` JWT. Pass an optional `emailInviteLink` to send them an invite pointing at your own page.
    * **By `customerId`**: add an existing customer directly by their `sub`. No email is sent. A `customerId` exists only after that person has [launched](/guides/jwt-authentication#launch-the-user-into-the-app) or exchanged a JWT via [`POST /auth/token`](/guides/jwt-authentication).

    <CodeGroup>
      ```json By email theme={null}
      { "email": "bob@acme.com", "role": "member" }
      ```

      ```json By customerId theme={null}
      { "customerId": "cccccccc-cccc-4ccc-8ccc-cccccccccc01", "role": "admin" }
      ```
    </CodeGroup>

    <Note>
      Membership can change at any time: [add a member](/api-reference/organizations/add-organization-member), [list members](/api-reference/organizations/list-organization-members), [remove a member](/api-reference/organizations/remove-organization-member), or [revoke a pending invite](/api-reference/organizations/revoke-organization-invite).
    </Note>
  </Step>

  <Step title="Launch a user into /kyb">
    A [Business KYB](/guides/user-launch-flows#business-kyb) launch, using [JWT User Authentication](/guides/jwt-authentication#sign-a-user-jwt). The `sub` is the **person's** `customerId`, not the business's `organizationId`.

    ```html theme={null}
    <form method="POST" action="https://sandbox.etherfuse.com/auth/launch">
      <input type="hidden" name="grant_type" value="urn:ietf:params:oauth:grant-type:jwt-bearer" />
      <input type="hidden" name="assertion" value="<kyb_scoped_jwt>" />
      <input type="hidden" name="target" value="/kyb" />
    </form>
    ```

    If that person belongs to more than one organization, append `?org=<organizationId>` to the target to choose which one to verify. Without it the app uses the last organization that person selected, falling back to their first, so pass it whenever the person may belong to more than one.

    If you pre-created the business, the user lands directly in its verification, and an emailed invite is accepted at this point. If you did not, the app first prompts them to create the business, then continues.

    <Warning>
      **Camera access.** The owner and representative identity checks need the camera. If you embed the launch in an **iframe**, the iframe element must delegate camera access with a wildcard origin: `allow="camera *; microphone *"`. The bare form (`allow="camera"`) only grants the camera to the iframe's own `src` origin, but the scan runs from a nested frame, so without the `*` the browser blocks the camera and the scan never starts. A top-level tab or popup needs nothing extra.
    </Warning>
  </Step>

  <Step title="The business completes the steps in the widget">
    Everything in [What verification covers](/guides/kyb#what-verification-covers), in an authenticated session that can be left and resumed. The people the business names as its owners and legal representatives each complete an identity check of their own, so more than one person usually takes part.
  </Step>

  <Step title="Watch for the result">
    The [`kyb_updated`](/webhooks#event-types) webhook fires with `status: "approved"` and the business can transact and hold API keys. See [Tracking verification status](/guides/kyb#tracking-verification-status).
  </Step>
</Steps>

## Error handling

Failures specific to launching. Verification outcomes are in [Tracking verification status](/guides/kyb#tracking-verification-status). A business asked to send something again reads `in_progress`, and launching it again is how its people do that. `denied` is a final rejection, which launching again does not reopen.

| What you see                                                | Meaning                                                                                                                        | What to do                                                                                        |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| The launch never reaches `/kyb`                             | The JWT was rejected: wrong scope, expired, bad signature, or an unrecognized `target`.                                        | See [Authentication errors](/guides/authentication-errors).                                       |
| The user is prompted to create a business they already have | The launch did not resolve to your organization, usually because the person belongs to more than one and no `?org=` was given. | Append `?org=<organizationId>` to the target.                                                     |
| The user lands in the app as a new person                   | The JWT `sub` was not that person's `customerId`.                                                                              | Re-sign with the correct `sub`. Do not reuse the old session.                                     |
| The identity scan never starts in an iframe                 | Camera permission. The bare `allow="camera"` grants only the iframe's own origin, but the scan runs from a nested frame.       | Set `allow="camera *; microphone *"` on the iframe. A top-level tab or popup needs nothing extra. |

## Sandbox

The verification checks do not run, so any document images and any identity scan will pass. To skip the flow entirely, use **Approve KYB** in the "Ready to approve?" panel on the sandbox [KYB page](https://sandbox.etherfuse.com/account/kyb): it approves the organization without review, which is the fastest way to unlock sandbox API keys. See [Initial Setup](/initial-setup) and [Test environment](/test-environment).
