# KYC integration best practices Reference integration flows and implementation patterns for the KYC tier system. This page provides reference integration flows and best practices for implementing the KYC tier system. Before using these flows, read the [KYC integration guide](https://docs.stripe.com/crypto/onramp/kyc-integration-guide.md) to understand the tier structure, API changes, and error codes. For a working reference implementation, see the [crypto-embedded-components-onramp](https://github.com/stripe-samples/crypto-embedded-components-onramp) sample app. ## Screen overview A typical onramp integration has the following screens: | Screen | Purpose | Client SDK calls | Server calls | | ------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Login | Authenticate the customer and register or retrieve their crypto customer record. | [`registerLinkUser`](https://docs.stripe.com/crypto/onramp/embedded-components.md) | [`GET /v1/crypto/customers/:id`](https://docs.stripe.com/api/crypto/customers/retrieve.md) | | KYC | Collect and submit identity information for the required tier. | - L0/L1: [`attachKYCInfo`](https://docs.stripe.com/crypto/onramp/embedded-components.md) - L2: [`verifyIdentity`](https://docs.stripe.com/crypto/onramp/embedded-components.md) | [`GET /v1/crypto/customers/:id`](https://docs.stripe.com/api/crypto/customers/retrieve.md) | | Wallet | Collect or select the customer’s destination wallet address. | Not applicable | [`GET /v1/crypto/customers/:id/crypto_consumer_wallets`](https://docs.stripe.com/api/crypto/consumer_wallets/list.md) | | Payment | Collect the customer’s payment method. | [`createCryptoPaymentToken`](https://docs.stripe.com/crypto/onramp/embedded-components.md) | Not applicable | | Buy | Display transaction limits, confirm amount, and execute purchase. | Not applicable | - [`GET /v1/crypto/onramp_transaction_limits`](https://docs.stripe.com/api/crypto/onramp_transaction_limits/retrieve.md) - [`GET /v1/crypto/onramp_quotes`](https://docs.stripe.com/api/crypto/onramp_quotes/retrieve.md) - [`POST /v1/crypto/onramp_sessions`](https://docs.stripe.com/api/crypto/onramp_sessions/create.md) | An onramp integration reuses the KYC screen across tiers and renders different fields depending on what’s needed: - **L0**: Name and address fields (`attachKYCInfo` with `firstName`, `lastName`, `address`) - **Step-up to L1**: SSN and date of birth fields (`attachKYCInfo` with `dateOfBirth`, `idNumber`, `idType`) - **L0 + L1 combined**: All fields collected in a single KYC screen visit - **L2**: The Stripe-hosted identity document verification flow (`verifyIdentity`), available after L1 is verified The reference app implements all screens in a single [WizardView](https://github.com/stripe-samples/crypto-embedded-components-onramp/blob/main/react-web/src/WizardView.tsx) component: - [Login](https://github.com/stripe-samples/crypto-embedded-components-onramp/blob/main/react-web/src/WizardView.tsx#L302) - [KYC](https://github.com/stripe-samples/crypto-embedded-components-onramp/blob/main/react-web/src/WizardView.tsx#L354) - [Wallet](https://github.com/stripe-samples/crypto-embedded-components-onramp/blob/main/react-web/src/WizardView.tsx#L481) - [Payment](https://github.com/stripe-samples/crypto-embedded-components-onramp/blob/main/react-web/src/WizardView.tsx#L596) - [Buy](https://github.com/stripe-samples/crypto-embedded-components-onramp/blob/main/react-web/src/WizardView.tsx#L653) The KYC step renders different fields and submits information using the onramp SDK based on the KYC level. ## Step-up detection strategy We recommend using a proactive approach for detecting step-ups because it provides a better customer experience: you can display the limit and explain why additional verification is needed before the customer encounters an error. To implement this approach: 1. On the buy screen, call `GET /v1/crypto/onramp_transaction_limits` before the customer confirms their purchase. 2. Compare the customer’s desired amount against the returned limits for their current tier. 3. If the amount exceeds the limit, prompt for step-up before attempting to create a session. If you can’t implement proactive detection, you can use a reactive approach and attempt to create the session instead. If it returns a `400` error, retrieve the customer to determine the required tier and route the customer accordingly: - `crypto_onramp_missing_minimum_identity_verification`: The customer hasn’t submitted minimum identity information yet. Route to the KYC screen for L0. - `crypto_onramp_missing_identity_verification`: L1 verification is required. Route to the KYC screen for L1. - `crypto_onramp_missing_document_verification`: L2 verification is required. Route to the KYC screen for L2. ## Polling verification status After submitting KYC information, poll `GET /v1/crypto/customers/:id` until `verification_status` is `verified` or `rejected` for the relevant tier. L0 and L1 verification typically complete within a few seconds. L2 verification (document review) can take longer — adjust your polling interval and provide appropriate loading indicators for each tier. For a reference implementation, see the [`refreshKycLevel`](https://github.com/stripe-samples/crypto-embedded-components-onramp/blob/main/react-web/src/ExampleApp.tsx#L116) function in the sample app. ## Integration scenarios The following scenarios cover the common KYC verification paths: - [L0 verification succeeds](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l0-verification-succeeds): Customer passes L0 and completes a purchase within L0 limits - [L0 succeeds, step-up to L1](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l0-step-up-l1): Customer passes L0 but needs L1 to complete a higher-value purchase - [L0 succeeds, step-up to L2](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l0-step-up-l2): Customer steps up from L0 to L1, then from L1 to L2 - [L1 verification succeeds](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l1-verification-succeeds): Customer provides L0 and L1 information upfront - [L1 succeeds, step-up to L2](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l1-step-up-l2): Customer is L1-verified but needs L2 to complete a higher-value purchase - [L2 verification succeeds](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l2-verification-succeeds): Customer completes all three tiers upfront - [Handling verification failures](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#verification-failures) - [L0 failure](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l0-failure) - [L1 failure](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l1-failure) - [L2 failure](https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices.md#l2-failure) ### L0 verification succeeds When a verification succeeds, the customer provides basic identity information, passes L0 verification, and completes a purchase within L0 limits. L0 verification succeeds: Login, KYC (L0), Wallet, Payment, and Buy screens in sequence, ending in a successful purchase. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) #### Recommended operations 1. **Login**: Register or retrieve the crypto customer. Check `kyc_tiers` to determine if KYC is already complete. 2. **KYC (L0)**: Call `attachKYCInfo` with `firstName`, `lastName`, and `address`. Poll `GET /v1/crypto/customers/:id` until the L0 tier `verification_status` is `verified` or `rejected`. 3. **Wallet**: Create or select a destination wallet. 4. **Payment**: Call `createCryptoPaymentToken` to collect payment method. 5. **Buy**: Call `GET /v1/crypto/onramp_transaction_limits` to display the customer’s available limits. Get a quote and create a session. ### L0 succeeds, step-up to L1 In this flow, the customer passes L0 verification but attempts a purchase that exceeds L0 limits. The integration routes the customer back to the KYC screen, which collects SSN and date of birth for L1 verification. L0 succeeds, step-up to L1: after the Buy screen detects the amount exceeds L0 limits, the user goes to the KYC screen for L1 verification before retrying the purchase. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) #### Recommended operations 1. **Detect step-up to L1**: On the Buy screen, call `GET /v1/crypto/onramp_transaction_limits`. If the customer’s desired amount exceeds their L0 limit, or if creating a session returns `crypto_onramp_missing_identity_verification`, route the customer back to the KYC screen for L1. 2. **KYC (L1)**: Call `attachKYCInfo` with `dateOfBirth`, `idNumber`, and `idType`. Poll until L1 `verification_status` is `verified` or `rejected`. 3. **Return to Buy**: After L1 verification succeeds, return the customer to the Buy screen. Refresh limits and proceed with the purchase. ### L0 succeeds, step-up to L2 The customer passes L0 verification, exceeds L0 limits (triggering a step-up to L1), then exceeds L1 limits (triggering a step-up to L2). Each step-up returns to the same KYC screen, which renders different fields for each tier. L0 succeeds, step-up to L2: the user exceeds L0 limits triggering a step-up to L1, then exceeds L1 limits triggering a step-up to L2, before completing the purchase. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) #### Recommended operations 1. **Detect step-up to L1**: On the Buy screen, call `GET /v1/crypto/onramp_transaction_limits`. If the amount exceeds the L0 tier limit, or if creating a session returns `crypto_onramp_missing_identity_verification`, route the customer to the KYC screen for L1. 2. **KYC (L1)**: Call `attachKYCInfo` with `dateOfBirth`, `idNumber`, and `idType`. Poll until L1 `verification_status` is `verified`. 3. **Return to Buy**: After L1 verification succeeds, return the customer to the Buy screen. Call `GET /v1/crypto/onramp_transaction_limits` again to check the customer’s L1 tier limits. 4. **Detect step-up to L2**: If the amount exceeds the L1 tier limit, or if creating a session returns `crypto_onramp_missing_document_verification`, route the customer to the KYC screen for L2. 5. **KYC (L2)**: Call `verifyIdentity` to present the Stripe-hosted identity verification flow. Poll `GET /v1/crypto/customers/:id` until L2 `verification_status` is `verified` or `rejected`. 6. **Return to Buy**: After L2 verification succeeds, return the customer to the Buy screen with refreshed limits. ### L1 verification succeeds The customer provides L0 and L1 information upfront and passes both tiers. This approach collects more information initially but avoids step-up interruptions for moderate purchase amounts. L1 verification succeeds: Login, KYC (L0 + L1), Wallet, Payment, and Buy screens in sequence, ending in a successful purchase at L1 limits. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) #### Recommended operations 1. **KYC (L0 + L1)**: Call `attachKYCInfo` with all L0 and L1 fields in a single request: `firstName`, `lastName`, `address`, `dateOfBirth`, `idNumber`, `idType`. Poll until both L0 and L1 `verification_status` values are `verified`. 2. **Buy**: The customer has higher limits available. Fetch limits and proceed. ### L1 succeeds, step-up to L2 The customer is L1-verified but attempts a purchase that exceeds L1 limits. The KYC screen shows the Stripe-hosted identity document verification flow. L1 succeeds, step-up to L2: after the Buy screen detects the amount exceeds L1 limits, the user goes to the KYC screen for L2 document verification before retrying the purchase. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) #### Recommended operations 1. **Detect step-up to L2**: If the session creation returns `crypto_onramp_missing_document_verification`, or limits indicate the amount requires L2, route the customer to the KYC screen for L2. 2. **KYC (L2)**: Call `verifyIdentity`. Poll until L2 is `verified` or `rejected`. 3. **Return to Buy**: Refresh limits and complete the purchase. ### L2 verification succeeds The customer completes all three tiers upfront in a single KYC screen visit and purchases at the maximum L2 limits with no step-up interruptions. On the KYC screen, L1 form fields are collected and verified first, then `verifyIdentity` is called for L2 within the same screen. L2 verification succeeds: Login, KYC (L0 + L1 + L2), Wallet, Payment, and Buy screens in sequence, ending in a successful purchase at maximum L2 limits. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) #### Recommended operations 1. **KYC (L0 + L1)**: Call `attachKYCInfo` with all L0 and L1 fields: `firstName`, `lastName`, `address`, `dateOfBirth`, `idNumber`, `idType`. Poll `GET /v1/crypto/customers/:id` until L1 `verification_status` is `verified`. 2. **KYC (L2)**: Once L1 is verified, call `verifyIdentity` to present the Stripe-hosted identity document verification flow within the same KYC screen. Poll until L2 `verification_status` is `verified`. 3. **Buy**: With L2 verified, the customer has the maximum available limits. Fetch limits, get a quote, and create a session. ### Handling verification failures When verification fails at any tier, the `verification_status` is `rejected` and `verification_errors` contains details about the failure. Handle these cases based on the tier. #### Transaction eligibility after a step-up failure If a step-up fails, transaction eligibility depends on which tier failed. - **L2 failure with L1 previously verified**: The customer can continue to transact within their L1 tier limits. Your integration should fall back to the Buy screen with L1 limits displayed. - **L1 failure with L0 previously verified**: The customer can’t fall back to transacting within L0 limits. Once L1 verification information has been submitted, the customer must pass L1 verification before they can transact again. Your integration should prompt the customer to correct and resubmit their L1 information. In general, submitting information for a higher tier is a one-way transition for L0 and L1. Design your step-up flows to clearly communicate to customers that providing L1 information (date of birth and SSN) replaces their L0 verification status and requires successful L1 verification to continue using the onramp. #### L0 failure If L0 verification fails, the customer can still proceed by completing L1 verification. L1 supersedes L0. L0 failure: when L0 verification is rejected, the user can still pass by completing L1 verification. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) Prompt the customer to provide additional information (date of birth and SSN) to verify at L1. #### L1 failure If L1 verification fails, check the `verification_errors` field for details. Common causes include mismatched identity information. The customer can retry by calling `attachKYCInfo` again with corrected information. L1 failure: when L1 verification is rejected, the user can correct and resubmit their information. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) Display a user-friendly error message based on `verification_errors` and allow the customer to correct and resubmit. #### L2 failure If L2 verification fails, the customer can retry the identity document upload. Call `verifyIdentity` again to present a new verification session. L2 failure: when L2 verification is rejected, the user can retry the identity document upload. (See full diagram at https://docs.stripe.com/crypto/onramp/kyc-integration-best-practices) Common L2 failures include blurry photos, expired documents, or selfie mismatches. Prompt the customer to retry with clearer images.