Stripe Identity Fresh
Verify your users' identity documents. Collect and verify passports, driver's licenses, and national IDs using a modal flow embedded in your site.
How it works
- Your server creates a
VerificationSession - You return the
client_secretto the frontend - The user clicks a verify button that opens the Stripe document upload modal
- The user captures/uploads their document
- Stripe verifies the document and returns results via webhook
- You show a confirmation page while verification processes
Before you begin
- Fill out the Stripe Identity application in Dashboard → Identity
- Optionally customize brand settings in Dashboard → Settings → Branding
Step 1: Create a VerificationSession (server-side)
Create the session on your server — never on the client. This prevents users from overriding verification options or incurring charges on your account.
javascript
const stripe = require('stripe')('sk_test_YOUR_KEY');
// POST /create-verification-session
const verificationSession = await stripe.identity.verificationSessions.create({
type: 'document',
provided_details: {
email: 'user@example.com',
},
metadata: {
user_id: 'USER_ID',
},
});
// Return only the client_secret to the frontend
const clientSecret = verificationSession.client_secret;The client_secret is single-use and expires after 24 hours. Send only the client secret to the frontend — never the full session object.
Test your endpoint:
bash
curl -X POST -is "http://localhost:4242/create-verification-session" -d ""Expected response:
bash
HTTP/1.1 200 OK
Content-Type: application/json
{ "id": "vs_QdfQQ6xfGNJR7ogV6", "client_secret": "vs_QdfQQ6xfGNJR7ogV6_secret_live_..." }Step 2: Show the document upload modal (client-side)
Add Stripe.js to your page and initialize it with your publishable key:
html
<html>
<head>
<title>Verify your identity</title>
<script src="https://js.stripe.com/v3/"></script>
</head>
<body>
<button id="verify-button">Verify</button>
<script>
var stripe = Stripe('pk_test_YOUR_PUBLISHABLE_KEY');
var verifyButton = document.getElementById('verify-button');
verifyButton.addEventListener('click', function() {
fetch('/create-verification-session', { method: 'POST' })
.then(function(response) { return response.json(); })
.then(function(session) {
return stripe.verifyIdentity(session.client_secret);
})
.then(function(result) {
if (result.error) {
alert(result.error.message);
}
})
.catch(function(error) {
console.error('Error:', error);
});
});
</script>
</body>
</html>Step 3: Show a confirmation page
After the user submits their document, redirect to a confirmation page:
javascript
verifyButton.addEventListener('click', function() {
fetch('/create-verification-session', { method: 'POST' })
.then(response => response.json())
.then(session => stripe.verifyIdentity(session.client_secret))
.then(result => {
if (result.error) {
alert(result.error.message);
} else {
window.location.href = '/confirmation';
}
});
});Modal error codes
| Error code | Description |
|---|---|
consent_declined | User declined verification by Stripe. May need to offer an alternative non-biometric means. |
device_unsupported | Verification requires a camera and the device doesn't have one. |
under_supported_age | Stripe doesn't verify users under the age of majority. |
phone_otp_declined | User unable to verify the provided phone number. |
email_verification_declined | User unable to verify the provided email address. |
Handle verification results
Listen for webhooks to receive verification outcomes:
javascript
const express = require('express');
const stripe = require('stripe')('sk_test_YOUR_KEY');
const app = express();
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, process.env.WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'identity.verification_session.verified':
const session = event.data.object;
// Verification succeeded — access verified outputs
const verifiedOutputs = session.verified_outputs;
console.log('Verified name:', verifiedOutputs.name);
console.log('Verified DOB:', verifiedOutputs.dob);
break;
case 'identity.verification_session.requires_input':
// Verification failed — check last_error
console.log('Verification failed:', event.data.object.last_error.reason);
break;
}
res.json({ received: true });
});Webhook events
| Event | When it fires |
|---|---|
identity.verification_session.created | Session created |
identity.verification_session.processing | Document submitted, processing |
identity.verification_session.verified | Verification succeeded |
identity.verification_session.requires_input | Verification failed — user input needed |
identity.verification_session.canceled | Session canceled |
VerificationSession object
Key fields on the VerificationSession object:
| Field | Description |
|---|---|
id | Session ID (vs_...) |
client_secret | Single-use secret for the frontend modal |
status | requires_input, processing, verified, canceled |
type | Verification type — document or id_number |
verified_outputs | Verified data (name, DOB, address, ID number) |
last_error | Error code and reason if verification failed |
metadata | Your key-value data attached to the session |
Testing
Use these test values in sandbox:
| Test scenario | How to trigger |
|---|---|
| Successful verification | Use any test document in sandbox modal |
| Failed verification | Close the modal without submitting |
| Processing delay | Verification briefly shows processing status before completing |
Sandbox sessions are never charged.