Identity Verification (Stripe)
Levy Fleets integrates with Stripe Identity to provide robust identity verification for customers. This helps prevent fraud, comply with regulations, and ensure rider safety. This guide covers verification modes, triggers, the verification flow, and management options.
Overview
Identity verification confirms that a customer is who they claim to be by validating government-issued identification documents. The system uses Stripe Identity, which provides:
- Document scanning: Photos of ID documents (driver's license, passport, ID card)
- Selfie matching: Compares selfie to ID photo
- Liveness detection: Prevents photo fraud
- Data extraction: Pulls verified name, DOB, address from ID
- Risk scoring: Integrates with Stripe Radar for fraud detection
Verification Modes
Each subaccount can configure one of three verification modes:
Disabled Mode
identity_verification_mode: 'disabled'
- No identity verification is required
- All customers can ride without verification
- Useful for low-risk environments or initial launch
- No verification costs incurred
All Users Mode
identity_verification_mode: 'all_users'
- Every new customer must verify identity before first ride
- Verification prompted during signup or first ride attempt
- Strictest setting for maximum security
- Higher verification costs but maximum fraud protection
Risk-Based Mode
identity_verification_mode: 'risk_based'
- Verification only required when risk threshold exceeded
- Uses Stripe Radar risk scoring from first payment
- Configurable threshold determines trigger point
- Balances security with customer experience
Recommended Setting
Risk-based verification is recommended for most operations as it balances security with customer experience and cost efficiency.
How Risk-Based Verification Works
Risk Score Calculation
When a customer makes their first payment, Stripe Radar analyzes:
- Payment method characteristics
- IP address and location
- Device fingerprint
- Historical patterns
- Card issuer country
Stripe returns a risk_level which the system converts to a score:
| Risk Level | Numeric Score | Description |
|---|---|---|
normal | 10 | Low risk, trusted payment |
elevated | 50 | Some risk indicators present |
highest | 75 | Multiple high-risk signals |
Risk Threshold Configuration
Subaccounts configure a threshold score (default: 50):
identity_verification_risk_threshold: 50
Verification is required when: customer_risk_score >= threshold
Example Scenarios:
| Customer Score | Threshold | Verification Required? |
|---|---|---|
| 10 (normal) | 50 | No |
| 50 (elevated) | 50 | Yes |
| 75 (highest) | 50 | Yes |
| 75 (highest) | 80 | No |
When Risk Scoring Occurs
Risk score is captured during the first successful card charge:
- Customer adds payment method
- Customer starts first ride
- At ride end, payment is processed
- Stripe Radar evaluates the charge
- Risk score is stored on customer record
- If threshold exceeded, verification is flagged
Subaccount Policy Override
Some subaccounts may require verification for all users regardless of risk:
identity_verification_mode: 'all_users'
When this is set, verification is required before the first ride, bypassing risk-based logic.
Verification Flow
Customer Experience
- Trigger: Customer attempts action requiring verification
- Notification: Push notification or in-app prompt appears
- Launch Stripe Identity: Native verification UI opens
- Document Scan: Customer photographs ID document
- Selfie: Customer takes selfie for comparison
- Processing: Stripe processes and verifies
- Result: Immediate pass/fail result displayed
- Ride Access: If passed, customer can proceed with rides
Technical Flow
Customer Action
↓
Check identity_verification_required
↓
If required → API call to /api/customers/{id}/identity-session
↓
Stripe Identity session created
↓
Client secret returned to mobile app
↓
Stripe Identity SDK launched
↓
Verification completed
↓
Webhook received: identity.verification_session.verified
↓
Customer record updated with verified data
↓
identity_verification_required = false
Verification Statuses
Customer identity status is tracked in the identity_status field:
| Status | Meaning | Customer Can Ride? |
|---|---|---|
null | Never attempted verification | Depends on policy |
pending | Verification in progress | No (if required) |
verified | Successfully verified | Yes |
requires_input | Needs to retry | No |
failed | Verification failed | No |
canceled | Session was canceled | No |
Dashboard Indicators
Identity Verification Banner
When a customer requires verification, an amber banner appears on their detail page:
Banner Content:
- "Identity Verification Required" heading
- Reason for requirement (subaccount policy or risk threshold)
- Date verification was flagged
- Current risk score and level
- Current verification status
- Clear Requirement button
Identity Verification Section
The customer detail page includes a dedicated section showing:
| Field | Description |
|---|---|
| Status | Current verification status |
| Verified At | Date/time of successful verification |
| Risk Score | Stripe Radar risk score (0-100) |
| Risk Level | normal, elevated, highest |
| ID Type | Driver's license, passport, etc. |
| Verified Name | Name extracted from ID |
| DOB | Date of birth from ID |
| Address | Address from ID (if available) |
Status Badges
Customers may display verification-related badges:
| Badge | Color | Meaning |
|---|---|---|
| Identity Required | Amber | Awaiting verification |
| Verified | Green | Successfully verified |
| Verification Failed | Red | Verification attempt failed |
Managing Verification
Viewing Verification Requirement Reason
On the customer detail page, the Identity Verification banner shows why verification was required:
Subaccount Policy:
Reason: Subaccount requires verification for all users
Risk Threshold Exceeded:
Reason: Risk score 75 >= threshold 50
Risk Score: 75/100 (highest)
Clearing Verification Requirement
To remove the verification requirement without the customer verifying:
- Navigate to customer detail page
- Locate the amber verification banner
- Click Clear Requirement
- Confirm the action
- Customer can now ride without verifying
When to Use:
- Known trusted customer incorrectly flagged
- Customer verified identity through other means
- Business decision to waive requirement
- Risk assessment was incorrect
Important
Clearing the requirement does NOT mark the customer as verified—it only removes the current requirement. Future risk triggers may require verification again.
Manual Verification
To mark a customer as verified without using Stripe Identity:
- Navigate to customer detail page
- In the Identity Verification section, click Manually Verify
- Add notes explaining how identity was confirmed
- Confirm the action
When to Use:
- In-person verification at a physical location
- Customer provided ID documents via email/support
- Legacy customer migration
- Verification via other identity provider
Manual Verification Database Fields:
| Field | Description |
|---|---|
identity_manual_verification | Boolean flag indicating manual verification |
identity_manual_verification_notes | Operator's notes explaining verification method |
identity_manual_verification_by | User ID of the operator who performed the verification |
identity_manual_verification_at | Timestamp when manual verification was recorded |
Manual verification bypasses Stripe Identity completely. The customer's identity_status is set to verified and identity_verification_required is cleared.
Requesting New Verification
To require a customer to verify again:
- Navigate to customer detail page
- Click Actions > Require Verification
- Select a reason
- Confirm the action
The customer will be blocked from rides until they complete verification.
Verified Data
When a customer successfully verifies, Stripe Identity extracts:
Always Captured
| Field | Database Column | Description |
|---|---|---|
| First Name | identity_first_name | Given name from ID |
| Last Name | identity_last_name | Family name from ID |
| Date of Birth | identity_dob | DOB from ID document |
| ID Type | identity_id_type | Document type used |
When Available
| Field | Database Column | Description |
|---|---|---|
| Address | identity_address | Street address from ID |
| ID Number | identity_id_number | License/passport number |
| Issuing Country | identity_issuing_country | Document issuing country |
| Expiration Date | identity_expiration_date | Document expiry |
Verification Metadata
| Field | Description |
|---|---|
identity_status | Final verification status |
identity_verified_at | Timestamp of verification |
identity_session_id | Stripe session reference |
identity_attempt_count | Number of attempts |
Notifications
The system sends push notifications for identity-related events:
Verification Required
When verification is first flagged:
- Title: "Identity Verification Required"
- Body: "Please verify your identity to continue using [App Name]"
- Type:
identity - Action: Opens verification flow in app
Verification Successful
After successful verification:
- Title: "Identity Verified"
- Body: "Your identity has been verified. You're all set to ride!"
- Type:
identity
Requirement Cleared
When an operator clears the requirement:
- Title: "Good to Go!"
- Body: "Your account has been verified and you can now ride."
- Type:
identity
Configuration
Subaccount Settings
Each subaccount configures verification settings:
| Setting | Type | Description |
|---|---|---|
identity_verification_mode | enum | 'disabled', 'all_users', 'risk_based' |
identity_verification_risk_threshold | integer | Score threshold (0-100) |
Recommended Configurations
High-Security Markets (regulated, high-value assets):
mode: 'all_users'
Standard Operations (balanced approach):
mode: 'risk_based'
threshold: 50
Low-Risk Markets (trusted communities, low-value assets):
mode: 'disabled'
or
mode: 'risk_based'
threshold: 75 # Only flag highest risk
Phone Lookup Risk Data
In addition to Stripe Radar, the system may capture phone-based risk data:
Phone Lookup Fields
| Field | Description |
|---|---|
phone_type | Mobile, landline, VOIP |
phone_carrier | Carrier name |
phone_is_prepaid | Prepaid status |
phone_risk_score | Risk assessment |
phone_country | Phone number country |
Risk Indicators
Certain phone characteristics indicate higher risk:
- VOIP numbers (Google Voice, etc.)
- Recently ported numbers
- Prepaid carriers in certain regions
- Mismatch between phone country and signup location
Signup Location Tracking
The system tracks where customers signed up for additional context:
IP-Based Location
| Field | Description |
|---|---|
signup_ip | IP address at signup |
signup_ip_city | City from IP lookup |
signup_ip_region | State/region from IP |
signup_ip_country | Country from IP |
GPS Location
| Field | Description |
|---|---|
signup_latitude | GPS latitude |
signup_longitude | GPS longitude |
signup_location_accuracy | GPS accuracy meters |
Location Risk Indicators
- IP location significantly different from GPS
- Signup from known VPN/proxy
- Location far from any operational zones
API Reference
Create Verification Session
Endpoint: POST /api/customers/{id}/identity-session
Response:
{
"client_secret": "vs_client_secret_xxx",
"session_id": "vs_xxx"
}
Get Customer Identity Status
Endpoint: GET /api/customers/{id}
Returns customer with identity fields:
{
"id": "...",
"identity_verification_required": true,
"identity_verification_required_at": "2024-01-15T10:30:00Z",
"identity_verification_required_reason": "risk_threshold_exceeded:75>=50",
"identity_status": "pending",
"stripe_risk_score": 75,
"stripe_risk_level": "highest"
}
Manual Verification
Endpoint: POST /api/customers/{id}/identity-manual-verify
Request:
{
"notes": "Verified in person at downtown location"
}
Integration with Ride System
Ride Start Verification Check
When a customer attempts to start a ride (either from the app or via dashboard "Start Trip"), the system checks:
- Is verification required? → Check
identity_verification_requiredflag - If required, is verification complete? → Check
identity_status === 'verified' - Block ride if not verified → Return error prompting verification
Dashboard Behavior: When starting a trip from the dashboard for a customer who requires verification, the operation will fail with an error message indicating verification is needed.
Mobile App Behavior: The app prompts the customer to complete verification before allowing ride scan.
Automatic Verification Requirement Triggers
Verification may be automatically required when:
- First Payment Risk Score: Stripe Radar evaluates the first charge
- Risk Threshold Exceeded: Score >= configured threshold
- Subaccount Policy: Some subaccounts require all users to verify
Verification Status Impact on Rides
| Status | Can Start Ride? |
|---|---|
null (never required) | Yes |
verified | Yes |
pending | No (if identity_verification_required is true) |
requires_input | No |
failed | No |
canceled | No |
Best Practices
For Operators
- Choose appropriate mode: Match verification level to your risk tolerance
- Monitor flagged customers: Don't leave customers stuck in verification limbo
- Train support staff: Ensure team knows how to handle verification issues
- Document manual verifications: Always add notes when bypassing flow
- Review thresholds: Adjust based on actual fraud rates
For Customer Support
- Explain the requirement: Tell customers why verification is needed
- Guide through process: Help with document selection and photo tips
- Escalate appropriately: Manual verify only when justified
- Check before clearing: Verify the clear is appropriate for the case
For Technical Teams
- Monitor webhook delivery: Ensure verification callbacks are processing
- Handle edge cases: Account for timeout and retry scenarios
- Log verification attempts: Maintain audit trail
- Test regularly: Verify flow works in all environments
Troubleshooting
Customer can't complete verification
- Check if Stripe Identity is enabled for your Stripe account
- Verify mobile app has camera permissions
- Try different ID document type
- Ensure adequate lighting for document photos
- Check for Stripe API errors in logs
Verification passed but customer still blocked
- Check if
identity_verification_requiredwas cleared - Verify webhook processed correctly
- Look for additional blocks (payment issue, manual block)
- Check for database update failures
Risk score seems incorrect
- Risk score comes from Stripe Radar, not controlled by Levy
- Review the charge that triggered scoring
- Check for VPN/proxy usage by customer
- Consider threshold adjustment if consistently incorrect
Customer verified but appears as "pending"
- Check webhook processing for errors
- Manually refresh customer data from Stripe
- Verify session ID matches current verification
- Check for multiple overlapping sessions