# Integrate the KYC tier system for the Embedded Components onramp Learn about KYC verification tiers, the information required for each, and how to detect when a customer's purchase amount is limited by their verification level. All customers must pass KYC verification before they can buy crypto with the onramp. Customers can provide additional information to increase their purchase limits. This guide covers the KYC tiers, the information required for each tier, and how you can detect when a customer’s purchase amount is limited by the amount of KYC information provided. For reference integration flows, screen overviews, and step-up detection patterns, see [KYC integration best practices](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md). > EU customers have additional identifier and attestation requirements beyond the standard KYC tiers. See the [EU KYC integration guide](https://docs.stripe.com/crypto/onramp/eu-kyc-integration-guide.md) for details. ## Before you begin Before you begin, [integrate the Embedded Components Onramp](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide.md). ## KYC tier comparison The following table summarizes the requirements for each KYC tier. | KYC tier | Customer inputs | | --- | --- | | L0 | Name, phone, email, address | | L1 | + Date of birth, SSN | | L2 | + Photo ID, selfie | ## KYC API ### Use attachKYCInfo for L0 and L1 KYC Call `attachKYCInfo` to initiate the L0 and L1 KYC verification processes for a customer. The fields collected by `attachKYCInfo` depend on the tier: - **L0**: `firstName`, `lastName`, `address` - **L1**: All L0 fields, plus `dateOfBirth`, `idNumber`, `idType` L0 and L1 verification are asynchronous. Call this API and check the verification status before you create an onramp session. [Creating a CryptoOnrampSession](https://docs.stripe.com/api/crypto/onramp_sessions/create.md) returns a `400` error if the customer has pending verifications. Continue retrieving the verification result until no verification remains pending. See [Determine whether a customer is verified](https://docs.stripe.com/crypto/onramp/kyc-integration-guide.md#determine-whether-a-customer-is-verified) for details. ### Use verifyIdentity for L2 KYC Call `verifyIdentity` to present a Stripe-hosted flow where the customer uploads an identity document and a selfie. L2 verification is asynchronous. Your backend must call the [Retrieve a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md) API with the `customerId`. The API returns the L2 verification status in `id_document_verified`. ### Handling KYC error for createCryptoPaymentToken Customers must submit their minimum identity information (name, phone, email, and address) before you can collect payment methods. If a customer hasn’t yet provided this minimum information, `createCryptoPaymentToken` returns a `crypto_onramp_missing_minimum_identity_verification` error. Handle this error by prompting the customer to submit their information using `attachKYCInfo`. A customer’s L0 tier verification status is independent of their ability to use the onramp. If a customer submits the minimum identity information but fails L0 verification, they can still use the onramp by completing L1 verification (date of birth and SSN). L1 verification supersedes L0. ### Determining a customer’s current KYC tier [Retrieve a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md) with the `customerId` and inspect the `kyc_tiers` field to determine a customer’s current KYC tier. The customer’s current tier is the highest tier where `verification_status` is not `not_available` or `not_started`. The `verification_status` values for each tier are: | Value | Description | | --- | --- | | `not_available` | Verification is not supported for the customer’s region. | | `not_started` | The customer hasn’t provided the required fields for this tier yet. | | `pending` | Verification has been submitted and is processing. | | `rejected` | Verification failed. | | `verified` | Verification completed successfully. | For example, given the following `kyc_tiers` response: ```json "kyc_tiers": [ { "tier": "l0", "verification_status": "verified", "verification_errors": [] }, { "tier": "l1", "verification_status": "verified", "verification_errors": [] }, { "tier": "l2", "verification_status": "not_started", "verification_errors": [] } ] ``` The customer’s current tier is L1 because it is the highest tier that is not `not_available` or `not_started`. After [Retrieving a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md), use the following snippet to determine the customer’s current KYC tier from the `kyc_tiers` field in the response: ```js // Iterates from the highest tier (l2) to the lowest (l0) and returns the first tier // whose verification_status is "pending", "rejected", or "verified" (meaning the customer // has at least attempted that tier). Returns undefined if no tier has been started. const currentTier = ["l2", "l1", "l0"].find((t) => ["pending", "rejected", "verified"].includes(customer.kyc_tiers.find((k) => k.tier === t)?.verification_status)); ``` You can also use the `provided_fields` field to determine a customer’s tier: | Customer | Provided fields | | --- | --- | | L0 | `first_name`, `last_name`, `address_line_1`, (`address_line_2`), `address_city`, `address_state`, `address_postal_code`, `address_country` | | L1 | All L0 fields, plus `id_number`, `id_type`, `dob` | | L2 | All L1 fields, plus `id_document`, `selfie` | ### Determine whether a customer is verified After retrieving the customer’s current KYC tier, check whether the tier entry for that tier has `verification_status: "verified"`. If so, the customer is verified at that tier. The following table summarizes how to use the `kyc_tiers` field to check verification status: | Customer | Verified | | --- | --- | | L0 | The `kyc_tiers` entry with `tier: "l0"` has `verification_status: "verified"` | | L1 | The `kyc_tiers` entry with `tier: "l1"` has `verification_status: "verified"` | | L2 | The `kyc_tiers` entry with `tier: "l2"` has `verification_status: "verified"` | The deprecated `verifications` field can also be used to determine verification status: | Customer | Verified | | --- | --- | | L0 | `kyc_verified: true`, `phone_verified: true` | | L1 | `kyc_verified: true` | | L2 | `kyc_verified: true`, `id_document_verified: true` | ### Interpret limit errors from CryptoOnrampSession Your backend must [Create a CryptoOnrampSession](https://docs.stripe.com/api/crypto/onramp_sessions/create.md) to create a [CryptoOnrampSession](https://docs.stripe.com/api/crypto/onramp_sessions/object.md). If the customer reaches a purchase limit, the `Create a CryptoOnrampSession` API returns an HTTP `400` error with a JSON object that describes the error. Your application can use these errors to request more information from the customer so they can complete the transaction. If creating a CryptoOnrampSession returns an HTTP `400` error with one of the following error codes, [Retrieve a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md) with the `customerId` to determine the next step. - `crypto_onramp_missing_minimum_identity_verification` - `crypto_onramp_missing_identity_verification` - `crypto_onramp_missing_document_verification` The following table summarizes how to use both the error code from [Creating a CryptoOnrampSession](https://docs.stripe.com/api/crypto/onramp_sessions/create.md) and the `kyc_tiers` field from [Retrieving a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md) to determine the next step. | Error code | Meaning | Next step | | --- | --- | --- | | `crypto_onramp_missing_minimum_identity_verification` | The customer hasn’t yet submitted the minimum identity information required to create a session. The customer needs to provide their name, phone, email, and address. | Prompt the customer to submit their minimum identity information (name, phone, email, and address) using `attachKYCInfo`. If the customer subsequently fails L0 tier verification, they can still use the onramp by completing L1 verification (date of birth and SSN), which supersedes L0. | | `crypto_onramp_missing_identity_verification` | L1 verification is required. The customer must provide full or partial L1 data to complete the transaction: - The customer is at L1 and didn’t pass the L1 identity verification required to create a session. - The customer is a verified L0 customer but the transaction requires L1 verification. | Inspect [Retrieve a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md) API response `kyc_tiers` to determine the next step. - If the customer’s current tier is L1, prompt the customer to provide full L1 KYC information, including name, address, date of birth and Social Security number. - If the customer’s current tier is L0, prompt the customer to provide only `dob`, `id_number`, `id_type`, then upload partially with `attachKYCInfo` (only include these three fields). | | `crypto_onramp_missing_document_verification` | L2 verification is required. The customer must provide a photo ID and selfie to complete the transaction: - The customer is at L2 and didn’t pass the L2 document identity verification required to create a session. - The customer is a verified L0 customer but the transaction requires L2 verification. - The customer is a verified L1 customer but the transaction requires L2 verification. | Inspect [Retrieve a CryptoCustomer](https://docs.stripe.com/api/crypto/customers/retrieve.md) API response `kyc_tiers` to determine the next step. - If the customer’s current tier is L2, prompt the customer to complete L2 KYC verification with `verifyIdentity`. - If the customer’s current tier is L0, prompt the customer to collect only `dob`, `id_number`, `id_type`, upload partially with `attachKYCInfo`, then prompt the customer to complete L2 KYC verification with `verifyIdentity`. - If the customer’s current tier is L1, prompt the customer to complete L2 KYC verification with `verifyIdentity`. |