> ## 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.

# KYC WebSDK

> The hosted verification flow: create the customer, launch them into Etherfuse, and the customer completes every step in the widget.

The default flow. You create the customer and launch them; the customer does everything else in the widget at `/idv`.

## The flow

<Steps>
  <Step title="Create the customer's organization">
    Create a `personal` organization with [`POST /organization`](/v2/api-reference/organizations/create-organization). **You generate the `id`.** This UUID is the customer's organization id, the `organizationId` you use everywhere else.

    <CodeGroup>
      ```bash Sandbox theme={null}
      curl -X POST https://api.sand.etherfuse.com/organization \
        -H "Authorization: <api_key>" \
        -H "Content-Type: application/json" \
        -d '{
          "id": "<customer_uuid>",
          "displayName": "Ana Ejemplo",
          "accountType": "personal",
          "userInfo": {
            "email": "ana@example.com",
            "displayName": "Ana Ejemplo"
          }
        }'
      ```

      ```bash Production theme={null}
      curl -X POST https://api.etherfuse.com/organization \
        -H "Authorization: <api_key>" \
        -H "Content-Type: application/json" \
        -d '{
          "id": "<customer_uuid>",
          "displayName": "Ana Ejemplo",
          "accountType": "personal",
          "userInfo": {
            "email": "ana@example.com",
            "displayName": "Ana Ejemplo"
          }
        }'
      ```
    </CodeGroup>

    <Warning>
      **Send `userInfo.email`.** An email address is required for the customer's verification record and status notifications. A customer with no email on file cannot start verification.
    </Warning>
  </Step>

  <Step title="Launch the customer into /idv">
    An [Identity verification](/v2/guides/user-launch-flows#identity-verification) launch, using [JWT User Authentication](/v2/guides/jwt-authentication#sign-a-user-jwt).

    ```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="<verification_scoped_jwt>" />
      <input type="hidden" name="target" value="/idv" />
    </form>
    ```

    <Warning>
      **The JWT `sub` MUST equal the org `id` from step 1.** Any other `sub` registers a brand-new person instead, and the verification won't attach to your customer.
    </Warning>

    <Tip>
      Append **`&lang=es`** to the target (`/idv?lang=es`) for Spanish. Pass an optional `return_url` on the launch to send the customer back to your app when they finish.
    </Tip>
  </Step>

  <Step title="The customer completes the steps in the widget">
    Everything in the [Requirements List](/v2/guides/onboarding#requirements-list), plus any [country-specific](/v2/guides/onboarding#country-specific-requirements) requirement, in one authenticated session. The widget takes them through the [stages](/v2/guides/onboarding#requirements-unlock-in-stages) in order, so you send nothing. The customer can leave and come back; progress is kept.
  </Step>

  <Step title="Watch for the result">
    A [`kycUpdated`](/v2/api-reference/webhooks/kycupdated) webhook arrives with `kyc.approved: true`, and the customer can now get quotes and create orders. Subscribe with [`POST /webhook_subscription`](/v2/api-reference/webhook-subscriptions/create-webhook-subscription) and `eventType: "kycUpdated"`; see [Webhooks](/v2/webhooks). See [Tracking verification status](/v2/guides/onboarding#tracking-verification-status).
  </Step>
</Steps>

## Error handling

Failures specific to launching. Verification outcomes (`denied`, needs-work) are in [Tracking verification status](/v2/guides/onboarding#tracking-verification-status); a rejected customer is resubmitted by launching them again.

| What you see | Meaning | What to do |
| - | - | - |
| The launch never reaches `/idv` | The JWT was rejected: wrong scope, expired, bad signature, or an unrecognized `target`. | See [Authentication errors](/v2/guides/authentication-errors). |
| The customer lands in `/idv` as a new person | The JWT `sub` wasn't the org `id`. | Sign a new JWT with `sub` = the org `id`. Don't reuse the old session. |
| The liveness 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 don't run, so any document images and any selfie will pass. The one exception is Mexico's constancia. See [Country-specific requirements](/v2/guides/onboarding#country-specific-requirements) and [Test environment](/v2/test-environment).

## When to reach for something else

<CardGroup cols={2}>
  <Card title="KYC API" icon="code" href="/v2/guides/kyc-api">
    You already collect identity data and documents in your own UI.
  </Card>

  <Card title="Sumsub shared token" icon="share-nodes" href="/v2/guides/kyc-sumsub-shared-token">
    You already verified the customer in your own Sumsub account.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.