# Handle verification with the API Learn how Connect platforms can use webhooks and the API to handle verification of connected accounts. Connect platforms that onboard connected accounts using the API must provide Stripe with required information for [Know Your Customer](https://support.stripe.com/questions/know-your-customer) (KYC) purposes and to enable [account capabilities](https://docs.stripe.com/connect/account-capabilities.md). They must collect the information themselves and use the Accounts and Persons APIs to provide it to Stripe. We then verify the information, asking for more details when needed. Responsible platforms must also monitor their connected accounts’ requirement statuses and [handle any updates](https://docs.stripe.com/connect/handle-verification-updates.md) in a timely manner. ## Verification process Before enabling charges and *payouts* (A payout is the transfer of funds to an external account, usually a bank account, in the form of a deposit) for a connected account, Stripe needs certain information that varies based on: - The origin country of the connected accounts - The [service agreement type](https://docs.stripe.com/connect/service-agreement-types.md) applicable to the connected accounts - The [capabilities](https://docs.stripe.com/connect/account-capabilities.md) requested for the connected accounts - The [business_type](https://docs.stripe.com/api/accounts/object.md#account_object-business_type) (for example, individual or company) and [company.structure](https://docs.stripe.com/api/accounts/object.md#account_object-company-structure) (for example, `public_corporation` or `private_partnership`) Platforms need to choose the proper [onboarding flow](https://docs.stripe.com/connect/identity-verification.md#onboarding-flows) for their business and users to meet the KYC requirements. That means providing all the requisite information up front or incrementally. Either way, set up your integration to watch for and respond to requests from Stripe. 1. Establish a [Connect webhook](https://docs.stripe.com/connect/webhooks.md) URL in your [webhook settings](https://dashboard.stripe.com/account/webhooks) to watch for activity, especially events of the `account.updated` type. When using the [Persons API](https://docs.stripe.com/api/persons.md), you should also watch for `person.updated` events. 1. Immediately after creating an account, check the `Account` object’s [requirements.currently_due](https://docs.stripe.com/api/accounts/object.md#account_object-requirements-currently_due) attribute for any additional requirements. Obtain any required information from the connected account and update the `Account`. As long as `requirements.currently_due` isn’t empty, the `Account` has outstanding requirements that might restrict its capabilities. 1. Continue watching for `account.updated` event notifications to see if the `requirements` hash changes, and ask the connected account for additional information as needed. When you provide additional information, you don’t need to resubmit previously verified details. For example, if the `dob` is already verified, you don’t need to provide it again unless it changes. ### Change information after verification After an individual or company is verified, you can [change some of their information](https://docs.stripe.com/connect/update-verified-information.md), with limitations. See the [Update Account](https://docs.stripe.com/api/accounts/update.md) API for limitations based on the configuration of the connected account. [Contact support](https://support.stripe.com/contact) to make changes outside of these limitations. ### Stripe risk review requirements Stripe risk reviews of a connected account can add extra requirements, which you can’t provide using the API. You can [take action in your Dashboard](https://docs.stripe.com/connect/dashboard/managing-individual-accounts.md#actions-required), or the connected account can provide them through [a Connect embedded component](https://docs.stripe.com/connect/supported-embedded-components.md#onboarding-and-compliance), [Stripe-hosted onboarding](https://docs.stripe.com/connect/hosted-onboarding.md), or a [remediation link](https://docs.stripe.com/connect/dashboard/remediation-links.md). ## Determine if verification is needed When you receive an `account.updated` event to your webhook or [fetch an account](https://docs.stripe.com/api.md#retrieve_account) with the API, you receive an [Account](https://docs.stripe.com/api/accounts/object.md) object. The `Account` object’s `charges_enabled` and `payouts_enabled` attributes indicate whether the account can create charges and accept payouts. The `Account` object has a `requirements` hash, representing the requirements needed to verify the account. After a requirement is collected, verified, or reviewed, it is considered resolved. The `requirements` hash has the following arrays: - `eventually_due`: Requirements that you might need to resolve, depending on whether the corresponding thresholds are reached. After a requirement becomes required, it also appears in the `currently_due` list. If a requirement becomes required and its due date is before the existing `current_deadline`, the `current_deadline` changes to the corresponding threshold’s enforcement date. - `currently_due`: Requirements that you must resolve by the `current_deadline` for the account to remain `active`. The `currently_due` requirement is a subset of `eventually_due`. - `past_due`: Requirements that have disabled capabilities because they weren’t resolved before the `current_deadline`. The `past_due` requirement is a subset of `currently_due`. - `errors`: Details about validation and verification failures for `due` requirements that must be resolved. - `requirement`: Identifies the requirement corresponding to the error. - `code`: An enum value describing why the requirement is invalid or can’t be verified. - `reason`: An English message describing the error in more detail. The reason string can also suggest how to resolve the error. - `disabled_reason`: Describes why the account isn’t enabled and why it can’t process charges or transfers. - `current_deadline`: Date that the requirements in `currently_due` must be resolved by to keep the account `active`. It represents the earliest deadline across all of the account’s requested capabilities and risk requirements, including any hidden capabilities. - `pending_verification`: Requirements that are being reviewed, or which might become required depending on the results of a review. It’s an empty array unless an asynchronous verification is pending. Unsuccessful verification moves a requirement to `eventually_due`, `currently_due`, `alternative_fields_due` or `past_due`. A requirement subject to both failed and pending verifications can also remain in `pending_verification`. The example below shows what the `requirements` hash might look like for an account that has some information that’s `currently_due`, some information that’s `eventually_due`, and some information that’s raising verification `errors`. ```json { "id": ""{{CONNECTED_ACCOUNT_ID}}"", "object": "account", "requirements": { "alternatives": [], "current_deadline": 1529085600, "currently_due": [ "company.tax_id", "company.verification.document", "tos_acceptance.date", "tos_acceptance.ip" ], "disabled_reason": null, "errors": [ { "requirement": "company.verification.document", "reason": "The company name on the account couldn't be verified. Either update your business name or upload a document containing the business name.", "code": "failed_name_match" }, ] "eventually_due": [ "company.address.city", "company.address.line1", "company.address.postal_code", "company.address.state", "company.tax_id", "company.verification.document", "external_account", "tos_acceptance.date", "tos_acceptance.ip" ], "past_due": [], "pending_verification": [] }, ... } ``` If `requirements.currently_due` contains entries, check `requirements.current_deadline`. The `current_deadline` is a Unix timestamp identifying when information is needed. Usually, if Stripe doesn’t receive the information by the `current_deadline`, payouts on the account are disabled. However, other consequences might apply in some situations. For example, if payouts are already disabled and the account is unresponsive to our inquiries, Stripe might also disable the ability to process charges. Separately, the [requirements.disabled_reason](https://docs.stripe.com/api/accounts/object.md#account_object-requirements-disabled_reason) property can have a value. The value is a string describing the reason why this account is unable to make payouts or charges. In some instances, platforms and connected accounts can submit a form to resolve or appeal the reason. - Connected accounts with access to the full Stripe Dashboard, including Standard accounts, can access additional information (if available) in the Dashboard. - Platforms can look up an account on the [Connected accounts](https://docs.stripe.com/connect/dashboard/review-actionable-accounts.md) page to determine an account’s `disabled_reason`. You might be able to provide additional information on behalf of your connected accounts. If the disabled reason is associated with an appeal, you can generate a link to a form for the account to resolve the appeal. | Reason | Meaning | | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `action_required.requested_capabilities` | You need to request capabilities for the connected account. For details, see [Request and unrequest capabilities](https://docs.stripe.com/connect/account-capabilities.md#requesting-unrequesting). | | `listed` | The account might be on a prohibited persons or companies list (Stripe investigates and either rejects or reinstates the account accordingly). | | `rejected.fraud` | The account is rejected because of suspected fraud or illegal activity. | | `rejected.incomplete_verification` | The account is rejected from incomplete verification requirements within the required threshold. | | `rejected.listed` | The account is rejected because it’s on a third-party prohibited persons or companies list (such as financial services provider or government). | | `rejected.other` | The account is rejected for another reason. | | `rejected.terms_of_service` | The account is rejected because of suspected terms of service violations. | | `requirements.past_due` | Additional verification information is required to enable capabilities on this account. | | `requirements.pending_verification` | Stripe is currently verifying information on the connected account. No action is required. Inspect the [requirements.pending_verification](https://docs.stripe.com/api/accounts/object.md#account_object-requirements-pending_verification) array to see the information being verified. | | `under_review` | The account is under review by Stripe. | ## Validation and verification errors The `Account` object includes a [requirements.errors](https://docs.stripe.com/api/accounts/object.md#account_object-requirements-errors) array that explains why the validation or verification requirements haven’t been met, which you need to enable your account and capabilities. The `errors` array has the following attributes: - `requirement`: Specifies which information from the `currently_due` or `alternative_fields_due` array is needed. - `code`: Indicates the type of error that occurred. See the [API reference](https://docs.stripe.com/api/accounts/object.md#account_object-requirements-errors-code) for all possible error codes. - `reason`: Explains why the error occurred and how to resolve the error. Below is an example that shows what the `errors` array might look like for an account with requirements that are `currently_due`. The example shows the reason why the submitted information can’t be used to enable the account, and how to resolve the error. If verification or validation is unsuccessful, requirements can reappear in `currently_due` or `alternative_fields_due` with error information. Set a [Connect webhook](https://docs.stripe.com/connect/webhooks.md) to receive the `account.updated` event to receive these updates. ```json { "id": ""{{CONNECTED_ACCOUNT_ID}}"", "object": "account", "requirements": { "current_deadline": 1234567800, "currently_due": [ "company.address.line1", "{{PERSON_ID}}.verification.document", ], "errors": [ { "requirement": "company.address.line1", "code": "invalid_street_address", "reason": "The provided street address cannot be found. Please verify the street name and number are correct in \"10 Downing Street\"", }, { "requirement": "{{PERSON_ID}}.verification.document", "code": "verification_document_failed_greyscale", "reason": "Greyscale documents cannot be read. Please upload a color copy of the document.", } ] }, ... } ``` If verification or validation is unsuccessful but no requirements are currently due, a webhook triggers indicating that required information is eventually due. ## Business information When information about a business is submitted, Stripe verifies the new information. For example, Stripe might verify that the provided business URL is valid, reachable, and includes information about the business. To check the status of verification information regarding a business, retrieve the `Account` object’s `requirements` hash. Below is a list of errors related to business information verification: | Error | Resolution | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | | `invalid_business_profile_name` | Business names must be easy for people to understand and must consist of recognizable words. | | `invalid_business_profile_name_denylisted` | Generic or well-known business names aren’t supported. Make sure the provided business name matches the account’s business. | | `invalid_product_description_length` | A product description must be at least 10 characters. | | `invalid_product_description_url_match` | A product description must be different from the URL of the business. | | `invalid_url_denylisted` `invalid_url_format` `invalid_url_web_presence_detected` `invalid_url_website_business_information_mismatch` `invalid_url_website_empty` `invalid_url_website_inaccessible` `invalid_url_website_inaccessible_geoblocked` `invalid_url_website_inaccessible_password_protected` `invalid_url_website_incomplete` `invalid_url_website_incomplete_cancellation_policy` `invalid_url_website_incomplete_customer_service_details` `invalid_url_website_incomplete_legal_restrictions` `invalid_url_website_incomplete_refund_policy` `invalid_url_website_incomplete_return_policy` `invalid_url_website_incomplete_terms_and_conditions` `invalid_url_website_incomplete_under_construction` `invalid_url_website_other` | See [Handle URL verification errors](https://docs.stripe.com/connect/handling-api-verification.md#url-verification) below. | ## Statement descriptors Stripe validates the statement descriptor and statement descriptor prefix when [set on an account](https://docs.stripe.com/connect/statement-descriptors.md). For example, Stripe might verify that the provided statement descriptor matches the description of the business. When validating the statement descriptor matches the business description, Stripe uses the first 22 characters of the statement descriptor, representing the part that is provided to the card networks. A business description is a close match of the account’s `business_profile.name`, `business_profile.url`, or the name of the company or individual. To retrieve the status of verification information regarding statement descriptors, review the `requirements` on the `Account` object. Below is a list of errors related to statement descriptor verification: | Error | Resolution | | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `invalid_statement_descriptor_length` | A statement descriptor must be at least 5 characters. | | `invalid_statement_descriptor_business_mismatch` | A statement descriptor must be similar to the business name, legal entity name, or URL of the account. | | `invalid_statement_descriptor_denylisted` `invalid_statement_descriptor_prefix_denylisted` | Generic or well-known statement descriptors aren’t supported. | | `invalid_statement_descriptor_prefix_mismatch` | The statement descriptor prefix must be similar to your statement descriptor, business name, legal entity name, or URL. | ## Business representatives You must collect and submit information about the people associated with a connected account. The process depends on whether your connected accounts are companies or individuals: - Companies only: Use the [Persons](https://docs.stripe.com/api/persons.md) API to add the information to a `Person` object associated with the `Account` object. - Individuals only: You can use the [Persons](https://docs.stripe.com/api/persons.md) API or the [individual](https://docs.stripe.com/api/accounts/object.md#account_object-individual) hash on the `Account` object. - A combination of individuals and companies: Use the [Persons](https://docs.stripe.com/api/persons.md) API to add the information to a `Person` object associated with the account. That lets you use the same process for all of your connected accounts, regardless of their type. To retrieve the status of verification information regarding a person, use the [requirements](https://docs.stripe.com/api/persons/object.md#person_object-requirements) hash. Below is a list of errors related to person verification: | Error | Resolution | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `invalid_address_city_state_postal_code` | Stripe couldn’t validate the combination of the city, state, and postal code in the provided address. | | `invalid_address_highway_contract_box` | The address of the person must be a valid physical address from which the account conducts business and can’t be a Highway Contract Box. | | `invalid_address_private_mailbox` | The address of the person must be a valid physical address from which the account conducts business and can’t be a private mailbox. | | `invalid_dob_age_under_minimum` | The person must be at least 13 years old. | | `invalid_dob_age_over_maximum` | The person’s date of birth must be within the past 120 years. | | `invalid_phone_number` | Stripe couldn’t validate the phone number on the account. Make sure the formatting matches the country of the person. | | `invalid_street_address` | Stripe couldn’t validate the street name and/or number for the provided address. | | `invalid_tax_id` `invalid_tax_id_format` | Tax IDs must be a unique set of 9 numbers without dashes or other special characters. | ## Acceptable verification documents by country To learn about specific document requirements, view [Acceptable verification documents by country](https://docs.stripe.com/acceptable-verification-documents.md). ## Company information During the verification process, you might need to collect information about the company for an account. To retrieve the status of verification information regarding an account’s company, use the `Account` object’s [company.verification](https://docs.stripe.com/api/accounts/object.md#account_object-company-verification) subhash: ```json { "id": ""{{CONNECTED_ACCOUNT_ID}}"", "object": "account", ... "company": { "verification": { "document": null }, ... }, ... } ``` You can look up the definition for each verification attribute on the `Account` object. ## Handle document verification problems Problems with identity documents, either with uploaded files themselves or with using them to validate other information, cause many requirement verification errors. To help you recognize and handle the most common problems, the tables below list requirement error `code` values related to document issues and provide guidance for resolving them. If verification fails, don’t resubmit the same file. Duplicate uploads fail automatically. The following errors relate to uploaded document files: | Code | Resolution | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `verification_document_corrupt` `verification_document_failed_copy` `verification_document_failed_greyscale` `verification_document_incomplete` `verification_document_not_readable` `verification_document_not_uploaded` `verification_document_not_signed` `verification_document_missing_back` `verification_document_missing_front` `verification_document_too_large` | The upload failed due to a problem with the file itself. Ask your account user to provide a new file that meets these requirements: - Color image (8,000 pixels by 8,000 pixels or smaller) - 10 MB or less - Identity documents are JPG or PNG format - Address or legal entity documents are JPG, PNG, or PDF format - Legal entity documents must include all pages - Must not be password protected | | `verification_document_country_not_supported` `verification_document_invalid` `verification_document_type_not_supported` | The provided file isn’t an acceptable form of ID from a supported country, or isn’t a type of legal entity document that is expected. Ask your account user to provide a new file that meets that requirement. For a list, see [Acceptable ID types by country](https://docs.stripe.com/connect/handling-api-verification.md#acceptable-verification-documents). | | `verification_failed_other` `verification_document_failed_other` | Your team can contact Stripe to learn more about why identity verification failed. | | `verification_document_expired` `verification_document_issue_or_expiry_date_missing` | The issue or expiry date is missing on the document, or the document is expired. If it’s an identity document, its expiration date must be after the date the document was submitted. If it’s an address document, the issue date must be within the last six months. | The following errors relate to identity verification: | Code | Resolution | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `verification_failed_keyed_identity` | The name on the account couldn’t be verified. Ask your account user to verify that they have provided their full legal name and to also provide a government-issued photo ID matching that name. | | `verification_document_name_mismatch` `verification_document_dob_mismatch` `verification_document_address_mismatch` `verification_document_id_number_mismatch` `verification_document_photo_mismatch` | The information on the ID document doesn’t match the information provided by the account user. Ask them to verify and correct the provided information on the account. | | `verification_document_fraudulent` `verification_document_manipulated` | The document might have been altered. Contact Stripe Support for more information. | The following errors relate to business verification: | Code | Resolution | | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `verification_failed_keyed_match` `verification_failed_document_match` | The information on the account couldn’t be verified. Your account user can either upload a document to confirm their account details, or update their information on their account. | | `verification_failed_tax_id_not_issued` `verification_failed_tax_id_match` | The information that your account user provided couldn’t be verified with the IRS. Ask them to correct any possible errors in the company name or tax ID, or upload a document that contains those fields. (US only) | | `verification_failed_id_number_match` `verification_failed_name_match` `verification_failed_address_match` | The information on the document doesn’t match the information provided by the account user. Ask them to verify and correct the provided information on the account, or upload a document with information that matches the account. | | `verification_document_address_missing` `verification_document_id_number_missing` `verification_document_name_missing` | The uploaded document is missing a required field. Ask your account user to upload another document that contains the missing field. | | `verification_legal_entity_structure_mismatch` | Business type or structure seems to be incorrect. Provide the correct business type and structure for this account. | The following errors relate to relationship verification: | Code | Resolution | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `information_missing` | Refer to the error message to understand what information was missing in the document or keyed-in data. If related to holding companies with significant ownership, the error code also provides the missing holding companies we’ve identified. For more information, refer to our [beneficial ownership verification for holding companies](https://support.stripe.com/questions/beneficial-ownership-verification-for-holding-companies) support article. | | `verification_failed_authorizer_authority` | We couldn’t verify the authority of the provided authorizer. Change the authorizer to a person who is registered as an authorized representative. Refer to our [Representative authority verification](https://support.stripe.com/questions/representative-authority-verification) support article. | | `verification_failed_representative_authority` | We couldn’t verify the authority of the account representative. Add an authorizer to the account and provide a Letter of Authorization signed by the authorizer. Refer to our [Representative authority verification](https://support.stripe.com/questions/representative-authority-verification) support article. | | `verification_missing_owners` | Business owners not provided. Provide information for all business owners or invite them to provide it themselves. The owners we have identified as missing are: [Name1, Name2]. | | `verification_missing_directors` | Directors are missing from the account. Update the account and upload a registration document with current directors. | | `verification_document_directors_mismatch` | Directors from the document are missing from the account. Update the account and upload a registration document with current directors. | | `verification_rejected_ownership_exemption_reason` | The ownership exemption reason was rejected. Choose a different exemption reason or upload a proof of ultimate beneficial ownership document instead. | ## Handle URL verification errors Stripe’s Terms of Service require all e-commerce businesses to populate their `Account`’s [business_profile.url](https://docs.stripe.com/api/accounts/object.md#account_object-business_profile-url) property with a working URL of their business website when requesting the `card_payments` capability. A connected account is considered an e-commerce business if it promotes or sells any products or services through an online website, social media profile, or mobile application. For more information, see the [Business website for account activation FAQ](https://support.stripe.com/questions/business-website-for-account-activation-faq). If the connected account doesn’t operate a website to promote their business, sell products, or accept payments, they’re required to provide the [business_profile.product_description](https://docs.stripe.com/api/accounts/object.md#account_object-business_profile-product_description) instead. A product description needs to detail the type of products being sold, as well as the manner in which the business charges its customers (for example, in-person transactions). URLs for e-commerce businesses need to conform to certain card network standards. In order to comply with these standards, Stripe conducts a number of verifications when reviewing URLs. To learn about best practices for URLs and common elements for e-commerce businesses, see the [website checklist](https://docs.stripe.com/get-started/checklist/website.md). In many cases, you can resolve business URL verification errors by [generating a remediation link](https://docs.stripe.com/connect/dashboard/remediation-links.md) from your platform Dashboard. Otherwise, update the `Account` object’s [business_profile.url](https://docs.stripe.com/api/accounts/update.md#update_account-business_profile-url). If you resolve the error another way, such as by fixing a problem with the company’s website, trigger re-verification by changing the URL on the `Account` object to any other value, then immediately changing it back. Not all URL-related issues can be resolved using the API. Certain types of URL verification errors require additional information on how to access the connected account’s website or to attest that the account is exempt from URL requirements. These types of issues require you or your connected account to provide supplemental information. If you can’t resolve the issue, direct your connected account to [contact Stripe Support](https://support.stripe.com/contact). To help you recognize and handle the most common problems, the following table lists requirement error `code` values related to issues with the business URL and provides guidance for resolving them. | Error | Resolution | | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `invalid_url_denylisted` | The provided URL matches a generic business website that Stripe believes is unrelated to the account. To resolve the issue, provide a URL that is specific to the business. | | `invalid_url_format` | The provided URL is in the incorrect format. To resolve the issue, provide a correctly formatted URL, such as `https://example.com`. | | `invalid_url_website_inaccessible` | We can’t reach the website at the provided URL. If you block certain regions from viewing your website, temporarily remove the blocker until your website has been verified. | | `invalid_url_website_business_information_mismatch` | Information on the website at the provided URL doesn’t match the information on the Stripe account. | | `invalid_url_website_incomplete` | The website at the provided URL is missing either a business name or a clear description of goods and services offered. | | `invalid_url_website_other` | We are unable to verify the account’s business using a website, social media profile, or mobile application at the provided URL. | | `invalid_url_web_presence_detected` | We have detected that the account uses a website, social media profile, or mobile application to sell or promote products or services, but a URL hasn’t been provided. To resolve the issue, provide a URL. | | `invalid_url_website_incomplete_customer_service_details` | The website doesn’t contain customer service details. | | `invalid_url_website_incomplete_return_policy` | The website doesn’t contain a return policy and process. | | `invalid_url_website_incomplete_refund_policy` | The website doesn’t contain a refund policy. | | `invalid_url_website_incomplete_cancellation_policy` | The website doesn’t contain a cancellation policy. | | `invalid_url_website_incomplete_legal_restrictions` | The website doesn’t contain applicable disclosures for products and services that are subject to legal or export restrictions. | | `invalid_url_website_incomplete_terms_and_conditions` | The website doesn’t contain terms and conditions. | | `invalid_url_website_incomplete_under_construction` | We are unable to verify the website at the provided URL, because the website is still under construction. | | `invalid_url_website_inaccessible_password_protected` | We are unable to verify the website at the provided URL, because the website is password-protected. | | `invalid_url_website_inaccessible_geoblocked` | We are unable to verify the website at the provided URL, because certain regions are blocked from accessing it. If you block certain regions from viewing your website, temporarily remove the blocker until your website has been verified. | | `invalid_url_website_empty` | We are unable to verify the website at the provided URL, because the website has no content. | ## Handle liveness requirements An account can have one or more [Person](https://docs.stripe.com/api/persons.md) objects with a `proof_of_liveness` requirement. A `proof_of_liveness` requirement might require collection of an electronic ID credential such as [MyInfo](https://www.singpass.gov.sg/main/individuals/) in Singapore, or by using Stripe Identity to collect a document or selfie. We recommend using Stripe-hosted or embedded onboarding to satisfy all variations of the `proof_of_liveness` requirement. #### Hosted [Stripe-hosted onboarding](https://docs.stripe.com/connect/hosted-onboarding.md) can complete all variations of `proof_of_liveness` requirements. [Create an Account Link](https://docs.stripe.com/connect/hosted-onboarding.md#create-account-link) using the connected account ID, and send the account to the `url` returned. ```curl curl https://api.stripe.com/v1/account_links \ -u "<>:" \ -d account="{{CONNECTEDACCOUNT_ID}}" \ --data-urlencode refresh_url="https://example.com/refresh" \ --data-urlencode return_url="https://example.com/return" \ -d type=account_onboarding \ -d "collection_options[fields]"=currently_due ``` ```cli stripe account_links create \ --account="{{CONNECTEDACCOUNT_ID}}" \ --refresh-url="https://example.com/refresh" \ --return-url="https://example.com/return" \ --type=account_onboarding \ -d "collection_options[fields]"=currently_due ``` ```ruby # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = Stripe::StripeClient.new("<>") account_link = client.v1.account_links.create({ account: '{{CONNECTEDACCOUNT_ID}}', refresh_url: 'https://example.com/refresh', return_url: 'https://example.com/return', type: 'account_onboarding', collection_options: {fields: 'currently_due'}, }) ``` ```python # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = StripeClient("<>") # For SDK versions 12.4.0 or lower, remove '.v1' from the following line. account_link = client.v1.account_links.create({ "account": "{{CONNECTEDACCOUNT_ID}}", "refresh_url": "https://example.com/refresh", "return_url": "https://example.com/return", "type": "account_onboarding", "collection_options": {"fields": "currently_due"}, }) ``` ```php // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys $stripe = new \Stripe\StripeClient('<>'); $accountLink = $stripe->accountLinks->create([ 'account' => '{{CONNECTEDACCOUNT_ID}}', 'refresh_url' => 'https://example.com/refresh', 'return_url' => 'https://example.com/return', 'type' => 'account_onboarding', 'collection_options' => ['fields' => 'currently_due'], ]); ``` ```java // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys StripeClient client = new StripeClient("<>"); AccountLinkCreateParams params = AccountLinkCreateParams.builder() .setAccount("{{CONNECTEDACCOUNT_ID}}") .setRefreshUrl("https://example.com/refresh") .setReturnUrl("https://example.com/return") .setType(AccountLinkCreateParams.Type.ACCOUNT_ONBOARDING) .setCollectionOptions( AccountLinkCreateParams.CollectionOptions.builder() .setFields(AccountLinkCreateParams.CollectionOptions.Fields.CURRENTLY_DUE) .build() ) .build(); // For SDK versions 29.4.0 or lower, remove '.v1()' from the following line. AccountLink accountLink = client.v1().accountLinks().create(params); ``` ```node // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const accountLink = await stripe.accountLinks.create({ account: '{{CONNECTEDACCOUNT_ID}}', refresh_url: 'https://example.com/refresh', return_url: 'https://example.com/return', type: 'account_onboarding', collection_options: { fields: 'currently_due', }, }); ``` ```go // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys sc := stripe.NewClient("<>") params := &stripe.AccountLinkCreateParams{ Account: stripe.String("{{CONNECTEDACCOUNT_ID}}"), RefreshURL: stripe.String("https://example.com/refresh"), ReturnURL: stripe.String("https://example.com/return"), Type: stripe.String("account_onboarding"), CollectionOptions: &stripe.AccountLinkCreateCollectionOptionsParams{ Fields: stripe.String("currently_due"), }, } result, err := sc.V1AccountLinks.Create(context.TODO(), params) ``` ```dotnet // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys var options = new AccountLinkCreateOptions { Account = "{{CONNECTEDACCOUNT_ID}}", RefreshUrl = "https://example.com/refresh", ReturnUrl = "https://example.com/return", Type = "account_onboarding", CollectionOptions = new AccountLinkCollectionOptionsOptions { Fields = "currently_due", }, }; var client = new StripeClient("<>"); var service = client.V1.AccountLinks; AccountLink accountLink = service.Create(options); ``` The account receives a prompt to complete the `proof_of_liveness` requirement, along with any other currently due requirements. Listen to the `account.updated` event sent to your webhook endpoint to be notified when the account completes requirements and updates their information. After the account completes the requirement, the account is redirected to the `return_url` specified. #### Embedded [Embedded onboarding](https://docs.stripe.com/connect/embedded-onboarding.md) can complete all forms of `proof_of_liveness` requirements. When [creating an Account Session](https://docs.stripe.com/api/account_sessions/create.md), enable account onboarding by specifying `account_onboarding` in the `components` parameter. If you don’t need to collect bank account information, disable `external_account_collection`. This typically applies to Connect platforms that want to use third-party external account collection providers. ```curl curl https://api.stripe.com/v1/account_sessions \ -u "<>:" \ -d account="{{CONNECTEDACCOUNT_ID}}" \ -d "components[account_onboarding][enabled]"=true \ -d "components[account_onboarding][features][external_account_collection]"=false ``` ```cli stripe account_sessions create \ --account="{{CONNECTEDACCOUNT_ID}}" \ -d "components[account_onboarding][enabled]"=true \ -d "components[account_onboarding][features][external_account_collection]"=false ``` ```ruby # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = Stripe::StripeClient.new("<>") account_session = client.v1.account_sessions.create({ account: '{{CONNECTEDACCOUNT_ID}}', components: { account_onboarding: { enabled: true, features: {external_account_collection: false}, }, }, }) ``` ```python # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = StripeClient("<>") # For SDK versions 12.4.0 or lower, remove '.v1' from the following line. account_session = client.v1.account_sessions.create({ "account": "{{CONNECTEDACCOUNT_ID}}", "components": { "account_onboarding": { "enabled": True, "features": {"external_account_collection": False}, }, }, }) ``` ```php // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys $stripe = new \Stripe\StripeClient('<>'); $accountSession = $stripe->accountSessions->create([ 'account' => '{{CONNECTEDACCOUNT_ID}}', 'components' => [ 'account_onboarding' => [ 'enabled' => true, 'features' => ['external_account_collection' => false], ], ], ]); ``` ```java // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys StripeClient client = new StripeClient("<>"); AccountSessionCreateParams params = AccountSessionCreateParams.builder() .setAccount("{{CONNECTEDACCOUNT_ID}}") .setComponents( AccountSessionCreateParams.Components.builder() .setAccountOnboarding( AccountSessionCreateParams.Components.AccountOnboarding.builder() .setEnabled(true) .setFeatures( AccountSessionCreateParams.Components.AccountOnboarding.Features.builder() .setExternalAccountCollection(false) .build() ) .build() ) .build() ) .build(); // For SDK versions 29.4.0 or lower, remove '.v1()' from the following line. AccountSession accountSession = client.v1().accountSessions().create(params); ``` ```node // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const accountSession = await stripe.accountSessions.create({ account: '{{CONNECTEDACCOUNT_ID}}', components: { account_onboarding: { enabled: true, features: { external_account_collection: false, }, }, }, }); ``` ```go // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys sc := stripe.NewClient("<>") params := &stripe.AccountSessionCreateParams{ Account: stripe.String("{{CONNECTEDACCOUNT_ID}}"), Components: &stripe.AccountSessionCreateComponentsParams{ AccountOnboarding: &stripe.AccountSessionCreateComponentsAccountOnboardingParams{ Enabled: stripe.Bool(true), Features: &stripe.AccountSessionCreateComponentsAccountOnboardingFeaturesParams{ ExternalAccountCollection: stripe.Bool(false), }, }, }, } result, err := sc.V1AccountSessions.Create(context.TODO(), params) ``` ```dotnet // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys var options = new AccountSessionCreateOptions { Account = "{{CONNECTEDACCOUNT_ID}}", Components = new AccountSessionComponentsOptions { AccountOnboarding = new AccountSessionComponentsAccountOnboardingOptions { Enabled = true, Features = new AccountSessionComponentsAccountOnboardingFeaturesOptions { ExternalAccountCollection = false, }, }, }, }; var client = new StripeClient("<>"); var service = client.V1.AccountSessions; AccountSession accountSession = service.Create(options); ``` After creating the Account Session and [initializing ConnectJS](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#account-sessions), you can render the Account onboarding component in the front end: #### JavaScript ```js // Include this element in your HTML const accountOnboarding = stripeConnectInstance.create('account-onboarding'); accountOnboarding.setOnExit(() => { console.log('User exited the onboarding flow'); }); container.appendChild(accountOnboarding); // Optional: make sure to follow our policy instructions above // accountOnboarding.setFullTermsOfServiceUrl('{{URL}}') // accountOnboarding.setRecipientTermsOfServiceUrl('{{URL}}') // accountOnboarding.setPrivacyPolicyUrl('{{URL}}') // accountOnboarding.setCollectionOptions({ // fields: 'eventually_due', // futureRequirements: 'include', // }) // accountOnboarding.setOnStepChange((stepChange) => { // console.log(`User entered: ${stepChange.step}`); // }); ``` #### React ```jsx import * as React from "react"; import { ConnectAccountOnboarding, ConnectComponentsProvider } from "@stripe/react-connect-js"; const AccountOnboardingUI = () => { return ( { console.log("The account has exited onboarding"); }} // Optional: make sure to follow our policy instructions above // fullTermsOfServiceUrl="{{URL}}" // recipientTermsOfServiceUrl="{{URL}}" // privacyPolicyUrl="{{URL}}" // collectionOptions={{ // fields: 'eventually_due', // futureRequirements: 'include', // }} // onStepChange={(stepChange) => { // console.log(`User entered: ${stepChange.step}`); // }} /> ); } ``` The account receives a prompt to complete the `proof_of_liveness` requirement, along with any other currently due requirements. Listen to the `account.updated` event sent to your webhook endpoint to be notified when the account completes requirements and updates their information. After the account completes the requirements, ConnectJS calls your `onExit` JavaScript handler. #### Identity You can use [Stripe Identity](https://docs.stripe.com/identity.md) to fulfill a `proof_of_liveness` requirement on a `Person` object by collecting a document and selfie. [Create a VerificationSession](https://docs.stripe.com/api/identity/verification_sessions/create.md). Specify the `related_person` parameter to associate the verification data collected with the `Person` object that requires the `proof_of_liveness`, as shown in the following example. ```curl curl https://api.stripe.com/v1/identity/verification_sessions \ -u "<>:" \ -d type=document \ -d "options[document][require_matching_selfie]"=true \ -d "related_person[account]"="{{CONNECTEDACCOUNT_ID}}" \ -d "related_person[person]"="{{PERSON_ID}}" ``` ```cli stripe identity verification_sessions create \ --type=document \ -d "options[document][require_matching_selfie]"=true \ -d "related_person[account]"="{{CONNECTEDACCOUNT_ID}}" \ -d "related_person[person]"="{{PERSON_ID}}" ``` ```ruby # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = Stripe::StripeClient.new("<>") verification_session = client.v1.identity.verification_sessions.create({ type: 'document', options: {document: {require_matching_selfie: true}}, related_person: { account: '{{CONNECTEDACCOUNT_ID}}', person: '{{PERSON_ID}}', }, }) ``` ```python # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = StripeClient("<>") # For SDK versions 12.4.0 or lower, remove '.v1' from the following line. verification_session = client.v1.identity.verification_sessions.create({ "type": "document", "options": {"document": {"require_matching_selfie": True}}, "related_person": { "account": "{{CONNECTEDACCOUNT_ID}}", "person": "{{PERSON_ID}}", }, }) ``` ```php // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys $stripe = new \Stripe\StripeClient('<>'); $verificationSession = $stripe->identity->verificationSessions->create([ 'type' => 'document', 'options' => ['document' => ['require_matching_selfie' => true]], 'related_person' => [ 'account' => '{{CONNECTEDACCOUNT_ID}}', 'person' => '{{PERSON_ID}}', ], ]); ``` ```java // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys StripeClient client = new StripeClient("<>"); VerificationSessionCreateParams params = VerificationSessionCreateParams.builder() .setType(VerificationSessionCreateParams.Type.DOCUMENT) .setOptions( VerificationSessionCreateParams.Options.builder() .setDocument( VerificationSessionCreateParams.Options.Document.builder() .setRequireMatchingSelfie(true) .build() ) .build() ) .setRelatedPerson( VerificationSessionCreateParams.RelatedPerson.builder() .setAccount("{{CONNECTEDACCOUNT_ID}}") .setPerson("{{PERSON_ID}}") .build() ) .build(); // For SDK versions 29.4.0 or lower, remove '.v1()' from the following line. VerificationSession verificationSession = client.v1().identity().verificationSessions().create(params); ``` ```node // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const verificationSession = await stripe.identity.verificationSessions.create({ type: 'document', options: { document: { require_matching_selfie: true, }, }, related_person: { account: '{{CONNECTEDACCOUNT_ID}}', person: '{{PERSON_ID}}', }, }); ``` ```go // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys sc := stripe.NewClient("<>") params := &stripe.IdentityVerificationSessionCreateParams{ Type: stripe.String(stripe.IdentityVerificationSessionTypeDocument), Options: &stripe.IdentityVerificationSessionCreateOptionsParams{ Document: &stripe.IdentityVerificationSessionCreateOptionsDocumentParams{ RequireMatchingSelfie: stripe.Bool(true), }, }, RelatedPerson: &stripe.IdentityVerificationSessionCreateRelatedPersonParams{ Account: stripe.String("{{CONNECTEDACCOUNT_ID}}"), Person: stripe.String("{{PERSON_ID}}"), }, } result, err := sc.V1IdentityVerificationSessions.Create(context.TODO(), params) ``` ```dotnet // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys var options = new Stripe.Identity.VerificationSessionCreateOptions { Type = "document", Options = new Stripe.Identity.VerificationSessionOptionsOptions { Document = new Stripe.Identity.VerificationSessionOptionsDocumentOptions { RequireMatchingSelfie = true, }, }, }; options.AddExtraParam("related_person[account]", "{{CONNECTEDACCOUNT_ID}}"); options.AddExtraParam("related_person[person]", "{{PERSON_ID}}"); var client = new StripeClient("<>"); var service = client.V1.Identity.VerificationSessions; Stripe.Identity.VerificationSession verificationSession = service.Create(options); ``` After you create the VerificationSession, use the returned `client_secret` to [show the Identity modal to the user](https://docs.stripe.com/identity/verify-identity-documents.md?platform=web&type=modal#show-modal) or redirect the user to the `url`. Verification completion automatically updates the account. We send an `account.updated` event to your webhook endpoint when the account completes the identity check and updates their information. ## Handle identity verification In some cases, depending on how much of an account’s identity information Stripe has been able to verify, we might ask you to upload one or more documents. Required documents appear in the `Account` object’s `requirements` hash. The following entries in `requirements.currently_due` identify documents that you must upload: - `person.verification.document`: Requires a color scan or photo of an acceptable form of ID. - `person.verification.additional_document`: Requires a color scan or photo of a document verifying the user’s address, such as a utility bill. - `company.verification.document`: Requires a proof of entity document establishing the business’ entity ID number, such as the company’s articles of incorporation. When `requirements.alternatives.alternative_fields_due` contains `verification.document` requirements, they can be used as an alternative to `requirements.alternatives.original_fields_due`. For security reasons, Stripe doesn’t accept ID documents through email. Uploading a document is a two-step process: 1. Upload the file to Stripe 1. Attach the file to the account ### Upload a file To upload a file, use the [Create File](https://docs.stripe.com/api/files/create.md) API by using a POST to send the file data as part of a `multipart/form-data` request. The uploaded file must meet these requirements: - Color image (8,000 pixels by 8,000 pixels or smaller) - 10 MB or less - Identity documents are JPG or PNG format - Address or legal entity documents are JPG, PNG, or PDF format Pass the file data in the `file` parameter and set the [purpose](https://docs.stripe.com/api/files/create.md#create_file-purpose) parameter according to the `Account` or `Person` object that will hold the document. To identify the purpose, look up the property in the API Reference. #### curl ```bash curl https://files.stripe.com/v1/files \ -u <>: \ -H "Stripe-Account: {{CONNECTED_STRIPE_ACCOUNT_ID}}" \ -F "purpose"="identity_document" \ -F "file"="@/path/to/a/file" ``` #### Ruby ```ruby # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key = '<>' Stripe::File.create({ purpose: 'identity_document', file: File.new('/path/to/a/file.jpg'), }, { stripe_account: '{{CONNECTED_ACCOUNT_ID}}', }) ``` #### Python ```python # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys stripe.api_key = '<>' with open("/path/to/a/file.jpg", "rb") as fp: stripe.File.create( purpose='identity_document', file=fp, stripe_account='{{CONNECTED_ACCOUNT_ID}}', ) ``` #### PHP ```php // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys \Stripe\Stripe::setApiKey('<>'); \Stripe\File::create([ 'purpose' => 'identity_document', 'file' => fopen('/path/to/a/file.jpg', 'r'), ], [ 'stripe_account' => '{{CONNECTED_ACCOUNT_ID}}', ]); ``` #### Java ```java // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys Stripe.apiKey = "<>"; FileCreateParams params = FileCreateParams.builder() .setPurpose(FileCreateParams.Purpose.IDENTITY_DOCUMENT) .setFile(new java.io.File("/path/to/a/file.jpg")) .build(); RequestOptions requestOptions = RequestOptions.builder() .setStripeAccount(""{{CONNECTED_ACCOUNT_ID}}"") .build(); File file = File.create(params); ``` #### Node.js ```javascript // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const fs = require('fs'); const file = await stripe.files.create({ purpose: 'identity_document', file: { data: fs.readFileSync('/path/to/a/file.jpg'), name: 'file_name.jpg', type: 'application/octet-stream', }, }, { stripeAccount: '{{CONNECTED_ACCOUNT_ID}}', }); ``` #### Go ```go // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys stripe.Key = "<>" fp, _ := os.Open("/path/to/a/success.png") params := &stripe.FileParams{ FileReader: fp, Filename: stripe.String("success.png"), Purpose: stripe.String(string(stripe.FileUploadPurposeIdentityDocument)), } params.SetStripeAccount(""{{CONNECTED_ACCOUNT_ID}}"") f, _ := file.New(params) ``` #### .NET ```dotnet // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys StripeConfiguration.ApiKey = "<>"; var filename = "/path/to/a/success.png"; using (FileStream stream = System.IO.File.Open(filename, FileMode.Open)) { var options = new FileCreateOptions { File = stream, Purpose = FilePurpose.IdentityDocument, }; var service = new FileService(); var upload = service.Create(options); } ``` This request uploads the file and returns a token: ```json { "id": ""{{FILE_ID}}"", "created": 1403047735, "size": 4908 } ``` Use the token’s `id` value to attach the file to a connected account for identity verification. ### Attach the file After you upload the file and receive a representative token, update the `Account` or `Person` object and provide the file ID in the appropriate parameter. Below is an example for a government-issued ID document: ```curl curl https://api.stripe.com/v1/accounts/{{CONNECTEDACCOUNT_ID}}/persons/{{PERSON_ID}} \ -u "<>:" \ -d "verification[document][front]"="{{FILE_ID}}" ``` ```cli stripe persons update {{CONNECTEDACCOUNT_ID}} {{PERSON_ID}} \ -d "verification[document][front]"="{{FILE_ID}}" ``` ```ruby # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = Stripe::StripeClient.new("<>") person = client.v1.accounts.persons.update( '{{CONNECTEDACCOUNT_ID}}', '{{PERSON_ID}}', {verification: {document: {front: '{{FILE_ID}}'}}}, ) ``` ```python # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = StripeClient("<>") # For SDK versions 12.4.0 or lower, remove '.v1' from the following line. person = client.v1.accounts.persons.update( "{{CONNECTEDACCOUNT_ID}}", "{{PERSON_ID}}", {"verification": {"document": {"front": "{{FILE_ID}}"}}}, ) ``` ```php // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys $stripe = new \Stripe\StripeClient('<>'); $person = $stripe->accounts->updatePerson( '{{CONNECTEDACCOUNT_ID}}', '{{PERSON_ID}}', ['verification' => ['document' => ['front' => '{{FILE_ID}}']]] ); ``` ```java // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys StripeClient client = new StripeClient("<>"); AccountPersonUpdateParams params = AccountPersonUpdateParams.builder() .setVerification( AccountPersonUpdateParams.Verification.builder() .setDocument( AccountPersonUpdateParams.Verification.Document.builder() .setFront("{{FILE_ID}}") .build() ) .build() ) .build(); // For SDK versions 29.4.0 or lower, remove '.v1()' from the following line. Person person = client.v1().accounts().persons().update( "{{CONNECTEDACCOUNT_ID}}", "{{PERSON_ID}}", params ); ``` ```node // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const person = await stripe.accounts.updatePerson( '{{CONNECTEDACCOUNT_ID}}', '{{PERSON_ID}}', { verification: { document: { front: '{{FILE_ID}}', }, }, } ); ``` ```go // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys sc := stripe.NewClient("<>") params := &stripe.PersonUpdateParams{ Verification: &stripe.PersonUpdateVerificationParams{ Document: &stripe.PersonUpdateVerificationDocumentParams{ Front: stripe.String("{{FILE_ID}}"), }, }, Account: stripe.String("{{CONNECTEDACCOUNT_ID}}"), } result, err := sc.V1Persons.Update(context.TODO(), "{{PERSON_ID}}", params) ``` ```dotnet // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys var options = new AccountPersonUpdateOptions { Verification = new AccountPersonVerificationOptions { Document = new AccountPersonVerificationDocumentOptions { Front = "{{FILE_ID}}", }, }, }; var client = new StripeClient("<>"); var service = client.V1.Accounts.Persons; Person person = service.Update( "{{CONNECTEDACCOUNT_ID}}", "{{PERSON_ID}}", options); ``` Below is an example for a company document: ```curl curl https://api.stripe.com/v1/accounts/{{CONNECTEDACCOUNT_ID}} \ -u "<>:" \ -d "company[verification][document][front]"="{{FILE_ID}}" ``` ```cli stripe accounts update {{CONNECTEDACCOUNT_ID}} \ -d "company[verification][document][front]"="{{FILE_ID}}" ``` ```ruby # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = Stripe::StripeClient.new("<>") account = client.v1.accounts.update( '{{CONNECTEDACCOUNT_ID}}', {company: {verification: {document: {front: '{{FILE_ID}}'}}}}, ) ``` ```python # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = StripeClient("<>") # For SDK versions 12.4.0 or lower, remove '.v1' from the following line. account = client.v1.accounts.update( "{{CONNECTEDACCOUNT_ID}}", {"company": {"verification": {"document": {"front": "{{FILE_ID}}"}}}}, ) ``` ```php // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys $stripe = new \Stripe\StripeClient('<>'); $account = $stripe->accounts->update( '{{CONNECTEDACCOUNT_ID}}', ['company' => ['verification' => ['document' => ['front' => '{{FILE_ID}}']]]] ); ``` ```java // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys StripeClient client = new StripeClient("<>"); AccountUpdateParams params = AccountUpdateParams.builder() .setCompany( AccountUpdateParams.Company.builder() .setVerification( AccountUpdateParams.Company.Verification.builder() .setDocument( AccountUpdateParams.Company.Verification.Document.builder() .setFront("{{FILE_ID}}") .build() ) .build() ) .build() ) .build(); // For SDK versions 29.4.0 or lower, remove '.v1()' from the following line. Account account = client.v1().accounts().update("{{CONNECTEDACCOUNT_ID}}", params); ``` ```node // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const account = await stripe.accounts.update( '{{CONNECTEDACCOUNT_ID}}', { company: { verification: { document: { front: '{{FILE_ID}}', }, }, }, } ); ``` ```go // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys sc := stripe.NewClient("<>") params := &stripe.AccountUpdateParams{ Company: &stripe.AccountUpdateCompanyParams{ Verification: &stripe.AccountUpdateCompanyVerificationParams{ Document: &stripe.AccountUpdateCompanyVerificationDocumentParams{ Front: stripe.String("{{FILE_ID}}"), }, }, }, } result, err := sc.V1Accounts.Update( context.TODO(), "{{CONNECTEDACCOUNT_ID}}", params) ``` ```dotnet // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys var options = new AccountUpdateOptions { Company = new AccountCompanyOptions { Verification = new AccountCompanyVerificationOptions { Document = new AccountCompanyVerificationDocumentOptions { Front = "{{FILE_ID}}", }, }, }, }; var client = new StripeClient("<>"); var service = client.V1.Accounts; Account account = service.Update("{{CONNECTEDACCOUNT_ID}}", options); ``` This update changes `verification.status` to `pending`. If an additional person needs to be verified, use the [Persons](https://docs.stripe.com/api/persons.md) API to update them. ### Confirm ID verification Satisfying all identity verification requirements for a person or company triggers a `v2.core.account_person.updated` or `v2.core.account[identity].updated` webhook notification, signaling that the verification process is complete. Stripe can take anywhere from a few minutes to a few business days to verify an image, depending on its readability. If the verification attempt fails, the associated requirement entry contains an error with a `code` and `description` describing the cause. The `description` contains a human-readable message such as “The image supplied isn’t readable,” which is safe to present to your account user but isn’t localized. The `code` value is a string such as `verification_document_not_readable`, which you can use to localize error messages for your account users. Verification failure also triggers a `v2.core.account_person.updated` or `v2.core.account[identity].updated` webhook notification. ### Hosted document collection with Stripe Identity You can use [Stripe Identity](https://docs.stripe.com/identity.md) to fulfill a `person.verification.document` requirement by collecting a document and attaching it directly to the account. However, you can’t use Stripe Identity to fulfill `person.verification.additional_document` or `company.verification.document` requirements. [Create a VerificationSession](https://docs.stripe.com/api/identity/verification_sessions/create.md). Specify the `related_person` parameter to associate the collected verification data with the `Person` object requiring the `document`, as shown in the following example. ```curl curl https://api.stripe.com/v1/identity/verification_sessions \ -u "<>:" \ -d type=document \ -d "related_person[account]"="{{CONNECTEDACCOUNT_ID}}" \ -d "related_person[person]"="{{PERSON_ID}}" ``` ```cli stripe identity verification_sessions create \ --type=document \ -d "related_person[account]"="{{CONNECTEDACCOUNT_ID}}" \ -d "related_person[person]"="{{PERSON_ID}}" ``` ```ruby # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = Stripe::StripeClient.new("<>") verification_session = client.v1.identity.verification_sessions.create({ type: 'document', related_person: { account: '{{CONNECTEDACCOUNT_ID}}', person: '{{PERSON_ID}}', }, }) ``` ```python # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys client = StripeClient("<>") # For SDK versions 12.4.0 or lower, remove '.v1' from the following line. verification_session = client.v1.identity.verification_sessions.create({ "type": "document", "related_person": { "account": "{{CONNECTEDACCOUNT_ID}}", "person": "{{PERSON_ID}}", }, }) ``` ```php // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys $stripe = new \Stripe\StripeClient('<>'); $verificationSession = $stripe->identity->verificationSessions->create([ 'type' => 'document', 'related_person' => [ 'account' => '{{CONNECTEDACCOUNT_ID}}', 'person' => '{{PERSON_ID}}', ], ]); ``` ```java // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys StripeClient client = new StripeClient("<>"); VerificationSessionCreateParams params = VerificationSessionCreateParams.builder() .setType(VerificationSessionCreateParams.Type.DOCUMENT) .setRelatedPerson( VerificationSessionCreateParams.RelatedPerson.builder() .setAccount("{{CONNECTEDACCOUNT_ID}}") .setPerson("{{PERSON_ID}}") .build() ) .build(); // For SDK versions 29.4.0 or lower, remove '.v1()' from the following line. VerificationSession verificationSession = client.v1().identity().verificationSessions().create(params); ``` ```node // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const verificationSession = await stripe.identity.verificationSessions.create({ type: 'document', related_person: { account: '{{CONNECTEDACCOUNT_ID}}', person: '{{PERSON_ID}}', }, }); ``` ```go // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys sc := stripe.NewClient("<>") params := &stripe.IdentityVerificationSessionCreateParams{ Type: stripe.String(stripe.IdentityVerificationSessionTypeDocument), RelatedPerson: &stripe.IdentityVerificationSessionCreateRelatedPersonParams{ Account: stripe.String("{{CONNECTEDACCOUNT_ID}}"), Person: stripe.String("{{PERSON_ID}}"), }, } result, err := sc.V1IdentityVerificationSessions.Create(context.TODO(), params) ``` ```dotnet // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys var options = new Stripe.Identity.VerificationSessionCreateOptions { Type = "document" }; options.AddExtraParam("related_person[account]", "{{CONNECTEDACCOUNT_ID}}"); options.AddExtraParam("related_person[person]", "{{PERSON_ID}}"); var client = new StripeClient("<>"); var service = client.V1.Identity.VerificationSessions; Stripe.Identity.VerificationSession verificationSession = service.Create(options); ``` After you create the VerificationSession, use the returned `client_secret` to [show the Identity modal to the user](https://docs.stripe.com/identity/verify-identity-documents.md?platform=web&type=modal#show-modal) or redirect the user to the `url`. Verification completion automatically updates the account. We send an `account.updated` event to your webhook endpoint when the account completes the identity check and updates their information. ## Handle risk verifications Stripe reports risk and compliance requirements in the [accounts.requirements](https://docs.stripe.com/api/accounts/object.md#account_object-requirements) attribute. These requirements follow the schema: `..`: - `id` uniquely identifies information needed by Stripe or our financial partners. This identifier is always prefixed with `interv_` to indicate that it is a risk verification requirement. - `requirement_description` specifically describes the information needed to complete the requirement, such as `identity_verification`, `rejection_appeal`, and so on. - `resolution_path` specifies how you or your connected account can provide the requested information: - `challenge`: Connected accounts must directly respond to challenge prompts. They often require sensitive information, such as a bank account, or information that only the account owner can provide, such as a selfie. - `form`: Connected accounts can complete form requests, or you can complete them on their behalf. - `support`: The requirement isn’t directly actionable. Contact [Stripe Support](https://support.stripe.com/). ```json { "id": ""{{CONNECTED_ACCOUNT_ID}}"", "object": "account", "requirements": { "current_deadline": 1234567800, "currently_due": [ "{{REQUIREMENT_ID}}.restricted_or_prohibited_industry_diligence.form" ], "pending_verification": [], ... }, ... } ``` After satisfying a resolution path, the value of the requirement’s resolution path might change to `support` and the requirement also appears in the `pending_verification` section of the requirements hash. Stripe verifies the submitted information and either dismisses the requirement as resolved or posts a new currently due requirement. ```json { "id": ""{{CONNECTED_ACCOUNT_ID}}"", "object": "account", "requirements": { "current_deadline": 1234567800, "currently_due": [], "pending_verification": [ "{{REQUIREMENT_ID}}.restricted_or_prohibited_industry_diligence.support" ], ... }, ... } ``` You can remediate risk and compliance requirements in any of the following ways, depending on the type of requirement: - **Connect embedded components:** You can [embed Connect components](https://docs.stripe.com/connect/get-started-connect-embedded-components.md) directly into your website. When a requirement surfaces, direct your users to the [account onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) embedded component, where they’re prompted to complete outstanding requirements directly in your UI. Alternatively, use the [Notification banner](https://docs.stripe.com/connect/supported-embedded-components/notification-banner.md) embedded component to prompt your users for any outstanding requirements. - **Stripe hosted onboarding:** You can generate links to direct your connected accounts to complete outstanding requirements programmatically through account links or manually in your [platform Dashboard](https://docs.stripe.com/connect/dashboard/review-actionable-accounts.md). - **Complete on behalf of your accounts:** You can use your [platform Dashboard](https://docs.stripe.com/connect/dashboard/review-actionable-accounts.md) to identify and complete form-based risk requirements from connected account detail on behalf of your accounts. The following table provides more detail about possible descriptions of risk- and compliance-related requirements. | Value | Description | | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `business_model_verification` | We require additional information about the nature of the business to verify that we can support the account. | | `restricted_or_prohibited_industry_diligence` | The business might operate in [a restricted category](https://stripe.com/legal/restricted-businesses) (for example, selling alcohol, insurance, or financial products). Stripe might require more information about the nature of the business or licensing information to verify that we can support the account. | | `intellectual_property_usage` | The business might be selling products or services that are protected by copyright. We require additional information to verify that the account is authorized to sell those products. | | `supportability_rejection_appeal` | The Stripe terms of service prohibit supporting the business. The account can appeal this determination. | | `other_supportability_inquiry` | We require additional information to verify that we can support the account. | | `credit_review` | We require additional information about the nature of the business to verify that we can support the account. | | `reserve_appeal` | We’ve applied a reserve to the account. The reserve doesn’t impact the account’s ability to accept payments with Stripe. The account can appeal our determination. | | `identity_verification` | The person responsible for the account must verify their identity by uploading a government-issued ID document and a selfie. | | `url_inquiry` | The business URL must reflect the products and services that it provides. Stripe might require them to change the URL before we can support the account. | | `address_verification` | We need to verify the address of the business through document upload. | | `domain_verification` | We need to verify that the account owner controls the URL or domain that they provided. | | `bank_account_verification` | We need to verify bank account details associated with the business. | | `customer_service_contact` | We need to verify customer service contact information associated with the business. | | `fulfillment_policy` | We need to verify the business’s fulfillment policy. | | `product_description` | The business’s Stripe account must include an accurate product description. | | `statement_descriptor` | We need a statement descriptor that accurately reflects the business. | | `capability_disable_appeal` | The Stripe Terms of Service prohibit supporting specific capabilities associated with this business. The account can appeal this determination. | | `rejection_appeal` | The Stripe Terms of Service prohibit supporting the business due to the level of risk it presents. The account can appeal this determination. | | `platform_concern` | The platform initiated an intervention on its own connected account. It can be a real intervention or an API integration test. | | `other_compliance_inquiry` | We require additional compliance information that doesn’t fit any of the other descriptions. | | `other_business_inquiry` | We require additional business information that doesn’t fit any of the other descriptions. | ## See also - [Identity verification for connected accounts](https://docs.stripe.com/connect/identity-verification.md) - [Account tokens](https://docs.stripe.com/connect/account-tokens.md) - [Testing Connect](https://docs.stripe.com/connect/testing.md) - [Testing account identity verification](https://docs.stripe.com/connect/testing-verification.md) - [Required verification information](https://docs.stripe.com/connect/required-verification-information.md)