The verification system uses a session-based flow with pluggable identity verification providers.
Mobile App → Start Session → Provider SDK → Webhook → Issue Credential
POST /api/verification/start
Create a new verification session and get auth token for mobile SDK.
// Request (optional)
{
"provider": "mock" | "idenfy" | "stripe_identity"
}
// Response
{
"sessionId": "session_1727890123_abc123",
"authToken": "mock_token_...",
"expiresAt": "2025-10-01T16:30:00.000Z",
"provider": "mock"
}GET /api/verification/status/:sessionId
Poll to check if verification is complete.
// Response
{
"sessionId": "session_1727890123_abc123",
"status": "pending" | "approved" | "rejected" | "expired",
"provider": "mock",
"ready": true, // true if can issue credential
"expiresAt": "2025-10-01T16:30:00.000Z",
"credentialIssued": false
}POST /api/verification/webhook?provider=mock
Verification provider calls this when verification completes.
// Request (provider-specific format)
{
"providerSessionId": "mock_session_xxx",
"status": "approved",
"firstName": "John",
"lastName": "Doe",
"birthDate": "1990-01-01",
"governmentId": "D1234567",
"idType": "government_id",
"state": "CA"
}
// Response
{
"success": true,
"sessionId": "session_1727890123_abc123",
"status": "approved"
}POST /api/credentials
Issue credential after verification is approved.
// Request
{
"verificationSessionId": "session_1727890123_abc123",
"walletAddress": "ALGORAND_ADDRESS_HERE"
}
// Response
{
"success": true,
"credential": { ... },
"personalData": { ... },
"blockchain": { ... },
"duplicateDetection": { ... }
}curl -X POST http://localhost:5173/api/verification/start \
-H "Content-Type: application/json" \
-d '{"provider": "mock"}'Response:
{
"sessionId": "session_1727890123_abc123",
"authToken": "mock_token_...",
"expiresAt": "2025-10-01T16:30:00.000Z",
"provider": "mock"
}curl -X POST 'http://localhost:5173/api/verification/webhook?provider=mock' \
-H "Content-Type: application/json" \
-d '{
"providerSessionId": "mock_session_session_1727890123_abc123",
"status": "approved",
"firstName": "John",
"middleName": "",
"lastName": "Doe",
"birthDate": "1990-01-15",
"governmentId": "D1234567",
"idType": "government_id",
"state": "CA"
}'curl http://localhost:5173/api/verification/status/session_1727890123_abc123Response:
{
"sessionId": "session_1727890123_abc123",
"status": "approved",
"ready": true,
"provider": "mock"
}curl -X POST http://localhost:5173/api/credentials \
-H "Content-Type: application/json" \
-d '{
"verificationSessionId": "session_1727890123_abc123",
"walletAddress": "YOUR_ALGORAND_ADDRESS_HERE"
}'How it works:
- Mobile app requests a verification session from YOUR server
- YOUR server creates a session and gets a token from the verification provider
- Mobile app launches the provider's SDK with the token
- User completes verification in the provider's SDK (may take several minutes)
- Provider sends webhook to YOUR server when verification completes
- Mobile app polls YOUR server to check if webhook was received
- If approved, mobile app requests credential issuance
Important: The mobile app ONLY communicates with YOUR server. It never polls the provider directly. The provider sends a webhook to your server when verification is complete.
Session Timeout: 30 minutes from creation
// 1. Start verification session on YOUR server
const { sessionId, authToken, provider } = await fetch('/api/verification/start', {
method: 'POST',
body: JSON.stringify({ provider: 'idenfy' }) // or 'mock' for testing
}).then(r => r.json());
// 2. Launch provider SDK with the auth token
// User completes ID verification (takes 1-5 minutes typically)
await IdenfyReactNative.start({ authToken });
// SDK closes automatically when user finishes
// 3. Poll YOUR server to check if provider's webhook arrived
// The provider sends a webhook to YOUR server when verification completes
let status;
let attempts = 0;
const maxAttempts = 90; // 3 minutes with 2s intervals
do {
const response = await fetch(`/api/verification/status/${sessionId}`);
status = await response.json();
if (status.status !== 'pending') break;
await new Promise(r => setTimeout(r, 2000)); // Wait 2 seconds
attempts++;
} while (attempts < maxAttempts);
// 4. If approved, issue credential
if (status.ready) {
const credential = await fetch('/api/credentials', {
method: 'POST',
body: JSON.stringify({
verificationSessionId: sessionId,
walletAddress: myWalletAddress
})
}).then(r => r.json());
// Store credential locally in wallet
await saveCredentialToWallet(credential);
} else {
// Handle rejection or timeout
console.error('Verification failed:', status.status);
}- Create provider class in
app/utils/verification-providers/ - Implement
IVerificationProviderinterface - Register in
app/utils/verification-providers/index.ts - Set environment variable:
VERIFICATION_PROVIDER=your_provider
See mock.ts for reference implementation.