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

# KYC

Privy verifies an individual user with Bridge. Privy creates the [Bridge customer](https://apidocs.bridge.xyz/platform/customers/customers/api) on the first request and links it to the Privy user, so your app only ever references the Privy user ID.

At a high level, KYC involves two steps:

<Steps>
  <Step title="Accept terms of service">
    The user accepts Bridge's terms of service through a hosted link.
  </Step>

  <Step title="Perform identity verification">
    The user submits their identity information through a hosted verification flow.
  </Step>
</Steps>

<Info>
  Bridge currently supports [hosted
  KYC](https://apidocs.bridge.xyz/platform/customers/customers/kyclinks) only. Both endpoints return
  a link that your app passes to its frontend for the user to open and complete.
</Info>

Before verifying users, [register a Bridge API key with Privy](/kyc-kyb/setup).

## Accept terms of service

<View title="REST API" icon="terminal">
  To generate a [terms of service](https://apidocs.bridge.xyz/platform/customers/customers/tos) link
  for a user, make a `POST` request to:

  ```bash theme={"system"}
  https://api.privy.io/v1/users/{user_id}/kyc/tos
  ```

  In the body of the request, include the following fields:

  <ParamField body="provider" type="'bridge'" required>
    Provider to verify the user with.
  </ParamField>

  <ParamField body="environment" type="'production' | 'sandbox'">
    Bridge environment to use. Defaults to `production`.
  </ParamField>

  <ParamField body="email" type="string">
    Email address for the Bridge customer. Falls back to the user's linked email account. Required if
    the user has no linked email.
  </ParamField>

  Below is a sample cURL command for this request:

  ```bash theme={"system"}
  curl --request POST https://api.privy.io/v1/users/did:privy:xxxxx/kyc/tos \
    -u "<your-privy-app-id>:<your-privy-app-secret>" \
    -H "privy-app-id: <your-privy-app-id>" \
    -H 'Content-Type: application/json' \
    -d '{
      "provider": "bridge",
      "environment": "sandbox",
      "email": "user@example.com"
    }'
  ```

  A successful response includes the following fields:

  <ResponseField name="provider" type="'bridge'">
    Provider the user is being verified with.
  </ResponseField>

  <ResponseField name="environment" type="'production' | 'sandbox'">
    Bridge environment used for the request.
  </ResponseField>

  <ResponseField name="status" type="string">
    Status of terms of service acceptance, as reported by Bridge.
  </ResponseField>

  <ResponseField name="link" type="string">
    URL the user opens to accept Bridge's terms of service.
  </ResponseField>

  ```json theme={"system"}
  {
    "provider": "bridge",
    "environment": "sandbox",
    "status": "pending",
    "link": "https://compliance.sandbox.bridge.xyz/accept-tos?customer_id=..."
  }
  ```
</View>

Pass the `link` to your app's frontend so the user can accept the terms of service. The request is idempotent: calling it again for the same user returns a link for the existing Bridge customer.

## Create a KYC link

<View title="REST API" icon="terminal">
  To generate a hosted KYC link for a user, make a `POST` request to:

  ```bash theme={"system"}
  https://api.privy.io/v1/users/{user_id}/kyc/links
  ```

  In the body of the request, include the following fields:

  <ParamField body="provider" type="'bridge'" required>
    Provider to verify the user with.
  </ParamField>

  <ParamField body="environment" type="'production' | 'sandbox'">
    Bridge environment to use. Defaults to `production`.
  </ParamField>

  <ParamField body="email" type="string">
    Email address for the Bridge customer. Falls back to the user's linked email account. Required if
    the user has no linked email.
  </ParamField>

  <ParamField body="endorsements" type="string[]">
    [Endorsements](https://apidocs.bridge.xyz/platform/customers/customers/endorsements) to request
    from Bridge. Each endorsement unlocks a set of rails and regions. Defaults to `['base']`.
  </ParamField>

  <ParamField body="redirect_uri" type="string">
    URI the user is redirected to after completing the hosted flow.
  </ParamField>

  <ParamField body="client_agreement_id" type="string">
    Identifier of the terms of service agreement the user accepted in your app. Only applicable if
    your app has arranged [terms of service
    reliance](https://apidocs.bridge.xyz/platform/customers/compliance/terms-of-service-reliance) with
    Bridge.
  </ParamField>

  Below is a sample cURL command for this request:

  ```bash theme={"system"}
  curl --request POST https://api.privy.io/v1/users/did:privy:xxxxx/kyc/links \
    -u "<your-privy-app-id>:<your-privy-app-secret>" \
    -H "privy-app-id: <your-privy-app-id>" \
    -H 'Content-Type: application/json' \
    -d '{
      "provider": "bridge",
      "environment": "sandbox",
      "email": "user@example.com",
      "endorsements": ["base"],
      "redirect_uri": "https://your-app.com/kyc/complete"
    }'
  ```

  The response is the user's full KYC status, including the link to complete verification. See [Track
  KYC status](#track-kyc-status) for the complete set of fields.

  ```json theme={"system"}
  {
    "provider": "bridge",
    "environment": "sandbox",
    "status": "not_started",
    "tos": {
      "status": "approved"
    },
    "kyc": {
      "status": "not_started",
      "link": "https://bridge.withpersona.com/verify?..."
    },
    "endorsements": [
      {
        "name": "base",
        "status": "incomplete",
        "missing": ["government_id_verification"]
      }
    ],
    "capabilities": {
      "payin_crypto": "pending",
      "payout_crypto": "pending",
      "payin_fiat": "pending",
      "payout_fiat": "pending"
    },
    "requirements_due": [],
    "future_requirements_due": []
  }
  ```
</View>

Pass `kyc.link` to your app's frontend so the user can complete verification. The request is idempotent: calling it again for the same user returns the existing link.

## Track KYC status

Verification is asynchronous. Bridge reviews the submission after the user completes the hosted flow, and can revoke an endorsement later. See [track KYC status](/kyc-kyb/kyc-status) to poll a user's status or subscribe to webhooks.

## Next steps

<CardGroup cols={2}>
  <Card title="Track KYC status" icon="magnifying-glass" href="/kyc-kyb/kyc-status">
    Poll a user's verification status or subscribe to webhooks
  </Card>

  <Card title="KYB" icon="building" href="/kyc-kyb/kyb">
    Verify an organization
  </Card>

  <Card title="Handling webhook events" icon="webhook" href="/user-management/users/webhooks/handling-events">
    Configure an endpoint to receive Privy webhook events
  </Card>
</CardGroup>
