# Verify your users’ identity documents Create sessions and collect identity documents. This guide explains how to use Stripe Identity to securely collect and verify identity documents. > To get access to the Identity iOS SDK, visit the [Identity Settings](https://dashboard.stripe.com/settings/identity) page and click **Enable**. To verify the identity of your users on iOS, present a verification sheet in your application. This guide includes the following steps: 1. Set up Stripe. 2. Add a server endpoint. 3. Present the verification sheet. 4. Handle verification events. ## Before you begin 1. [Set up your Stripe account and verify your business](https://dashboard.stripe.com/account/onboarding). 2. Fill out your [Stripe Identity application](https://dashboard.stripe.com/identity/application). The steps in this guide are fully implemented in the [example app](https://github.com/stripe/stripe-ios/tree/master/Example/IdentityVerification%20Example) and [example backend server](https://codesandbox.io/p/devbox/compassionate-violet-gshhgf). ## Set up [Server-side] [Client-side] > If you intend to use this SDK with Stripe’s Identity service, you must not modify this SDK. Using a modified version of this SDK with Stripe’s Identity service, without Stripe’s written authorization, is a breach of your agreement with Stripe and might result in your Stripe account being shut down. ### Install the SDK (Client-side) The [Stripe iOS SDK](https://github.com/stripe/stripe-ios) is open source, [fully documented](https://stripe.dev/stripe-ios/index.html), and compatible with apps supporting iOS 13.0 or above. #### Swift Package Manager To install the SDK, follow these steps: 1. In Xcode, select **File** > **Add Package Dependencies…** and enter `https://github.com/stripe/stripe-ios-spm` as the repository URL. 2. Select the latest version number from our [releases page](https://github.com/stripe/stripe-ios/releases). 3. Add the **StripeIdentity** product to the [target of your app](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app). #### CocoaPods 1. If you haven’t already, install the latest version of [CocoaPods](https://guides.cocoapods.org/using/getting-started.html). 2. If you don’t have an existing [Podfile](https://guides.cocoapods.org/syntax/podfile.html), run the following command to create one: ```bash pod init ``` 3. Add this line to your `Podfile`: ```podfile pod 'StripeIdentity' ``` 4. Run the following command: ```bash pod install ``` 5. Don’t forget to use the `.xcworkspace` file to open your project in Xcode, instead of the `.xcodeproj` file, from here on out. 6. In the future, to update to the latest version of the SDK, run: ```bash pod update StripeIdentity ``` #### Carthage 1. If you haven’t already, install the latest version of [Carthage](https://github.com/Carthage/Carthage#installing-carthage). 2. Add this line to your `Cartfile`: ```cartfile github "stripe/stripe-ios" ``` 3. Follow the [Carthage installation instructions](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos). Make sure to embed all of the required frameworks listed [here](https://github.com/stripe/stripe-ios/tree/master/StripeIdentity#manual-linking). 4. In the future, to update to the latest version of the SDK, run the following command: ```bash carthage update stripe-ios --platform ios ``` #### Manual Framework 1. Head to our [GitHub releases page](https://github.com/stripe/stripe-ios/releases/latest) and download and unzip **Stripe.xcframework.zip**. 2. Drag **StripeIdentity.xcframework** to the **Embedded Binaries** section of the **General** settings in your Xcode project. Make sure to select **Copy items if needed**. 3. Repeat step 2 for all required frameworks listed [here](https://github.com/stripe/stripe-ios/tree/master/StripeIdentity#manual-linking). 4. In the future, to update to the latest version of our SDK, repeat steps 1–3. > For details on the latest SDK release and past versions, see the [Releases](https://github.com/stripe/stripe-ios/releases) page on GitHub. To receive notifications when a new release is published, [watch releases](https://help.github.com/en/articles/watching-and-unwatching-releases-for-a-repository#watching-releases-for-a-repository) for the repository. ### Set up camera authorization (Client-side) The Stripe Identity iOS SDK requires access to the device’s camera to capture identity documents. To enable your app to request camera permissions: 1. Open your project’s **Info.plist** in Xcode. 2. Add the `NSCameraUsageDescription` key. 3. Add a string value that explains to your users why your app requires camera permissions, something such as: > This app uses the camera to take a picture of your identity documents. See [Apple’s documentation](https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios) to learn more about requesting camera authorization. ### Install Stripe on your server (Server-side) First, [register](https://dashboard.stripe.com/register) for a Stripe account. Then install the libraries for access to the Stripe API from your application: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ## Add a server endpoint [Server-side] ### Create a VerificationSession A [VerificationSession](https://docs.stripe.com/api/identity/verification_sessions.md) is the programmatic representation of the verification. It contains details about the type of verification, such as what [check](https://docs.stripe.com/identity/verification-checks.md) to perform. You can [expand](https://docs.stripe.com/api/expanding_objects.md) the [verified outputs](https://docs.stripe.com/api/identity/verification_sessions/object.md#identity_verification_session_object-verified_outputs) field to see details of the data that was verified. You can use verification flows for re-usable configuration, which is passed to the [verification_flow](https://docs.stripe.com/api/identity/verification_sessions/create.md#create_identity_verification_session-verification_flow) parameter. Read more in the [Verification flows guide](https://docs.stripe.com/identity/verification-flows.md). You need a server-side endpoint to [create the VerificationSession](https://docs.stripe.com/api/identity/verification_sessions/create.md). Creating the `VerificationSession` server-side prevents malicious users from overriding verification options and incurring processing charges on your account. Add authentication to this endpoint by including a user reference in the session metadata or storing the session ID in your database. For security, don’t create a `VerificationSession` object that’s directly accessible from the mobile client. Instead, your server provides the SDK with an ephemeral key—a short-lived API key with restricted access to the [VerificationSession](https://docs.stripe.com/api/identity/verification_sessions.md). You can think of an ephemeral key as a session, authorizing the SDK to retrieve and update a specific `VerificationSession` object for the duration of the session. After successfully creating a `VerificationSession` and ephemeral key, send the `VerificationSession` [ID](https://docs.stripe.com/api/identity/verification_sessions/object.md#identity_verification_session_object-id) and ephemeral key secret to the client to show the document upload sheet. > You can find a running implementation of this endpoint [available here](https://codesandbox.io/p/devbox/compassionate-violet-gshhgf) for quick testing. #### Node.js ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. // Find your keys at https://dashboard.stripe.com/apikeys. const stripe = require('stripe')('<>'); // In the route handler for /create-verification-session: // Authenticate your user. // Create the session. const verificationSession = await stripe.identity.verificationSessions.create({ type: 'document', provided_details: { email: 'user@example.com', }, metadata: { user_id: '{{USER_ID}}', }, }); // Create an ephemeral key for the VerificationSession const ephemeralKey = await stripe.ephemeralKeys.create( {verification_session: verificationSession.id}, {apiVersion: '2026-06-24.dahlia'} ); // Return only the ID and ephemeral key secret to the frontend. const verificationSessionId = verificationSession.id; const ephemeralKeySecret = ephemeralKey.secret; ``` > The ephemeral key secret is bound to the `VerificationSession` and lets your app collect sensitive verification information such as document and selfie image files. It’s single-use and expires after 1 hour. Don’t store it, log it, embed it in a URL, or expose it to anyone other than the user. Make sure that you have TLS enabled on any endpoint that returns the ephemeral key secret. Send only the ephemeral key secret to your app to avoid exposing verification configuration or results. Test your endpoint by starting your web server (for example, `localhost:4242`) and sending a POST request with curl to create a VerificationSession: ```bash curl -X POST -is "http://localhost:4242/create-verification-session" -d "" ``` The response in your terminal looks like this: ```bash HTTP/1.1 200 OK Content-Type: application/json { id: "vs_QdfQQ6xfGNJR7ogV6", ephemeral_key_secret: "ek_YWNjdF8xRm..." } ``` ## Present the verification sheet [Client-side] Set up a button to present a verification sheet. After tapping the button, your user can capture and upload a picture of their passport, driver’s license, or national ID. Before getting started, your verification page should: - Explain to the user why they need to verify their identity. - Include a verify identity button to present Stripe’s UI. ### Add a button Start by creating a view controller with a button that has a tap action and a loading indicator: ```swift import UIKit class VerifyViewController: UIViewController { @IBOutlet weak var verifyButton: UIButton! @IBOutlet weak var activityIndicator: UIActivityIndicatorView! } ``` ### Import the StripeIdentity SDK Import `StripeIdentity` to your view controller: ```swift import UIKit import StripeIdentity class VerifyViewController: UIViewController { @IBOutlet weak var verifyButton: UIButton! @IBOutlet weak var activityIndicator: UIActivityIndicatorView! } ``` ### Add an action to the Verify button Now that you have a button and an endpoint to create a `VerificationSession`, modify the button so that it presents the document upload sheet when tapped the document upload sheet when tapped. Add a call to: - Fetch the `VerificationSession` ID and ephemeral key secret from your endpoint. - Instantiate an `IdentityVerificationSheet` with your brand logo and present it to the user. - Handle the `VerificationResult` to know if the user completed the verification flow. ```swift import UIKit import StripeIdentity class VerifyViewController: UIViewController { @IBOutlet weak var verifyButton: UIButton! @IBOutlet weak var activityIndicator: UIActivityIndicatorView! override func viewDidLoad() { super.viewDidLoad() verifyButton.addTarget(self, action: #selector(didTapVerifyButton), for: .touchUpInside) } @objc func didTapVerifyButton() { // Disable the button while the request is made verifyButton.isEnabled = false activityIndicator.startAnimating() // Make request to your verification endpoint var urlRequest = URLRequest(url: URL(string: "https://{{YOUR_SERVER_BASE_URL}}/create-verification-session")!) urlRequest.httpMethod = "POST" let task = URLSession.shared.dataTask(with: urlRequest) { [weak self] data, response, error in DispatchQueue.main.async { [weak self] in // Re-enable button self?.verifyButton.isEnabled = true self?.activityIndicator.stopAnimating() guard error == nil, let data = data, let responseJson = try? JSONDecoder().decode([String: String].self, from: data), let verificationSessionId = responseJson["id"], let ephemeralKeySecret = responseJson["ephemeral_key_secret"] else { // Handle error print(error as Any) return } self?.presentVerificationSheet(verificationSessionId: verificationSessionId, ephemeralKeySecret: ephemeralKeySecret) } } task.resume() } func presentVerificationSheet(verificationSessionId: String, ephemeralKeySecret: String) { // Configure a square brand logo. Recommended image size is 32 x 32 points. let configuration = IdentityVerificationSheet.Configuration( brandLogo: UIImage(named: "{{YOUR_BRAND_LOGO}}")! ) // Instantiate and present the sheet let verificationSheet = IdentityVerificationSheet( verificationSessionId: verificationSessionId, ephemeralKeySecret: ephemeralKeySecret, configuration: configuration ) verificationSheet.present(from: self, completion: { result in switch result { case .flowCompleted: // The user has completed uploading their documents. // Let them know that the verification is processing. print("Verification Completed!") case .flowCanceled: // The user did not complete uploading their documents. // You should allow them to try again. print("Verification Canceled!") case .flowFailed(let error): // If the flow fails, you should display the localized error // message to your user using error.localizedDescription print("Verification Failed!") print(error.localizedDescription) } }) } } ``` ### Test the verification sheet Test that the verify button presents a document upload sheet: - Tap the **Verify Identity** button. - Ensure no error messages are shown. If your integration isn’t working: 1. Put a breakpoint where you fetch the `VerificationSession` ID and ephemeral key secret. 2. Verify that no network errors exist and that the endpoint is returning a `VerificationSession` ID and ephemeral key secret. ## Handle verification events [Document checks](https://docs.stripe.com/identity/verification-checks.md#document-availability) are typically completed as soon as the user redirects back to your site and you can retrieve the result from the API immediately. In some rare cases, the document verification isn’t ready yet and must continue asynchronously. In these cases, you’re notified through webhooks when the verification result is ready. After the processing completes, the VerificationSession status changes from `processing` to `verified`. Stripe sends the following events when the session status changes: | Event name | Description | Next steps | | --- | --- | --- | | [identity.verification_session.verified](https://docs.stripe.com/api/events/types.md#event_types-identity.verification_session.verified) | Processing of all the [verification checks](https://docs.stripe.com/identity/verification-checks.md) have completed, and they’re all successfully verified. | Trigger relevant actions in your application. | | [identity.verification_session.requires_input](https://docs.stripe.com/api/events/types.md#event_types-identity.verification_session.requires_input) | Processing of all the [verification checks](https://docs.stripe.com/identity/verification-checks.md) have completed, and at least one of the checks failed. | Trigger relevant actions in your application and potentially allow your user to retry the verification. | Use a [webhook handler](https://docs.stripe.com/identity/handle-verification-outcomes.md) to receive these events and automate actions like sending a confirmation email, updating the verification results in your database, or completing an onboarding step. You can also view [verification events in the Dashboard](https://dashboard.stripe.com/events?type=identity.%2A). ## Receive events and run business actions ### With code Build a webhook handler to listen for events and build custom asynchronous verification flows. Test and debug your webhook integration locally with the Stripe CLI. [Build a custom webhook](https://docs.stripe.com/identity/handle-verification-outcomes.md) ### Without code Use the Dashboard to view all your verifications, inspect collected data, and understand verification failures. [View your test verifications in the Dashboard](https://dashboard.stripe.com/test/identity/verification-sessions)