KYC Integration
Embed Solidus KYC verification into your application. Users complete identity verification once and receive a Verifiable Credential they can present anywhere, eliminating repeated KYC processes.
Prerequisites
- An account on verify.solidus.network
- An API key (generate one in the Verify dashboard under Settings > API Keys)
Integration Modes
Solidus Verify supports two integration patterns:
| Mode | How it works | Best for |
|---|---|---|
| Hosted flow | Redirect users to the Solidus-hosted verification page | Fastest integration, no UI work needed |
| API flow | Upload documents directly via API calls | Custom UI, mobile apps, embedded experiences |
Base URL
All API requests use the following base URL:
https://verify.solidus.network/v1Authenticate every request with your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEYHosted Flow
Step 1: Create a Verification Session
const response = await fetch('https://verify.solidus.network/v1/verifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
// The DID of the user being verified (optional for hosted flow)
subjectDid: 'did:solidus:testnet:7Hk3mRtQZv...',
// Verification level: 1 = basic (ID + liveness), 2 = enhanced (+ address)
level: 1,
// Where to redirect after verification completes
redirectUrl: 'https://myapp.com/verification-complete',
// Webhook to receive status updates
webhook: {
url: 'https://myapp.com/webhooks/verify',
events: ['verification.completed', 'verification.failed'],
},
// Optional: pre-fill user information
prefill: {
firstName: 'Jane',
lastName: 'Doe',
email: '[email protected]',
country: 'US',
},
}),
})
const session = await response.json()The response contains the session ID and the URL to redirect the user to:
{
"id": "ver_2xK9mP4qR7nL",
"status": "pending",
"sessionUrl": "https://verify.solidus.network/session/ver_2xK9mP4qR7nL",
"expiresAt": "2026-05-07T13:00:00Z",
"createdAt": "2026-05-07T12:00:00Z"
}Step 2: Redirect the User
Send the user to sessionUrl. They will see the Solidus verification interface where they upload documents and complete a liveness check.
// In your frontend
window.location.href = session.sessionUrlStep 3: Handle the Redirect
After verification, the user is redirected to your redirectUrl with the session ID as a query parameter:
https://myapp.com/verification-complete?session=ver_2xK9mP4qR7nL&status=completedStep 4: Check the Result
Query the session status to get the verification result and issued credential.
const result = await fetch(
'https://verify.solidus.network/v1/verifications/ver_2xK9mP4qR7nL',
{
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
}
)
const verification = await result.json()The completed verification response:
{
"id": "ver_2xK9mP4qR7nL",
"status": "completed",
"level": 1,
"subjectDid": "did:solidus:testnet:7Hk3mRtQZv...",
"result": {
"outcome": "pass",
"checks": {
"documentAuthenticity": "pass",
"faceMatch": "pass",
"livenessDetection": "pass",
"nameMatch": "pass",
"ageVerification": "pass"
},
"extractedData": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1990-03-15",
"documentType": "passport",
"documentCountry": "US",
"documentNumber": "***redacted***"
}
},
"credential": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential", "KYCCredential"],
"issuer": "did:solidus:testnet:verify-service",
"credentialSubject": {
"id": "did:solidus:testnet:7Hk3mRtQZv...",
"level": 1,
"country": "US"
},
"proof": { "type": "Ed25519Signature2020", "proofValue": "z3FXQ..." }
},
"completedAt": "2026-05-07T12:05:30Z",
"createdAt": "2026-05-07T12:00:00Z"
}API Flow
Use the API flow when you want full control over the UI. You collect the documents from the user and upload them directly.
Step 1: Create a Session
Same as the hosted flow, but omit redirectUrl:
const response = await fetch('https://verify.solidus.network/v1/verifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
subjectDid: 'did:solidus:testnet:7Hk3mRtQZv...',
level: 1,
webhook: {
url: 'https://myapp.com/webhooks/verify',
events: ['verification.completed', 'verification.failed'],
},
}),
})
const { id: sessionId } = await response.json()Step 2: Upload Documents
Upload identity documents to the session. Supported formats: JPEG, PNG, PDF. Maximum file size: 10 MB.
// Upload the front of an identity document
const frontUpload = new FormData()
frontUpload.append('file', frontImageFile)
frontUpload.append('type', 'document_front')
await fetch(
\`https://verify.solidus.network/v1/verifications/${sessionId}/documents\`,
{
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
body: frontUpload,
}
)
// Upload the back of the document (required for ID cards and driver's licenses)
const backUpload = new FormData()
backUpload.append('file', backImageFile)
backUpload.append('type', 'document_back')
await fetch(
\`https://verify.solidus.network/v1/verifications/${sessionId}/documents\`,
{
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
body: backUpload,
}
)
// Upload a selfie for face matching
const selfieUpload = new FormData()
selfieUpload.append('file', selfieFile)
selfieUpload.append('type', 'selfie')
await fetch(
\`https://verify.solidus.network/v1/verifications/${sessionId}/documents\`,
{
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
body: selfieUpload,
}
)Document types:
| Type | Description | Required |
|---|---|---|
document_front | Front of passport, ID card, or driver’s license | Always |
document_back | Back of ID card or driver’s license | ID cards and driver’s licenses |
selfie | User’s face photo for liveness/face matching | Always |
proof_of_address | Utility bill or bank statement | Level 2 only |
Step 3: Submit for Processing
After uploading all documents, submit the session for processing:
await fetch(
\`https://verify.solidus.network/v1/verifications/${sessionId}/submit\`,
{
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
}
)Step 4: Receive Results
Results arrive via webhook or polling. See the Webhooks guide for webhook setup. To poll:
let status = 'processing'
while (status === 'processing') {
const res = await fetch(
\`https://verify.solidus.network/v1/verifications/${sessionId}\`,
{
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
}
)
const data = await res.json()
status = data.status
if (status === 'processing') {
await new Promise((r) => setTimeout(r, 3000))
}
}Webhook Events
Configure webhooks to receive real-time notifications about verification status changes.
| Event | Description |
|---|---|
verification.completed | Verification finished successfully, credential issued |
verification.failed | Verification failed (document issues, face mismatch, etc.) |
verification.expired | Session expired before the user completed verification |
Webhook payload example:
{
"id": "evt_8nR3kL5mQ2",
"type": "verification.completed",
"createdAt": "2026-05-07T12:05:30Z",
"data": {
"verificationId": "ver_2xK9mP4qR7nL",
"status": "completed",
"outcome": "pass",
"subjectDid": "did:solidus:testnet:7Hk3mRtQZv...",
"level": 1,
"credentialId": "urn:uuid:credential-id-here"
}
}See the Webhooks guide for full details on signature verification and delivery handling.
Sandbox Mode
Use sandbox mode during development to test the full flow without real identity documents.
Set sandbox: true when creating a session. Control the outcome with sandboxOutcome:
const response = await fetch('https://verify.solidus.network/v1/verifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
subjectDid: 'did:solidus:testnet:7Hk3mRtQZv...',
level: 1,
sandbox: true,
sandboxOutcome: 'pass', // 'pass', 'fail', or 'error'
redirectUrl: 'https://localhost:3000/verification-complete',
}),
})Sandbox sessions:
- Skip real document analysis
- Complete within seconds
- Issue test credentials (not valid for production)
- Support all the same API endpoints and webhooks
Available sandbox outcomes:
| Outcome | Behavior |
|---|---|
pass | Verification succeeds, test credential issued |
fail | Verification fails with simulated document issues |
error | Simulates a processing error for testing error handling |
Verification Statuses
A session moves through these statuses:
pending ──> processing ──> completed
│
├──> failed
│
└──> expired| Status | Description |
|---|---|
pending | Session created, waiting for user to start |
processing | Documents uploaded, verification in progress |
completed | Verification passed, credential issued |
failed | Verification failed |
expired | Session expired (default: 1 hour) |
Error Handling
The API returns standard HTTP status codes with error details:
{
"error": {
"code": "invalid_document",
"message": "The uploaded document could not be read. Please upload a clear photo.",
"details": {
"documentType": "document_front",
"reason": "blur_detected"
}
}
}Common error codes:
| Code | Description |
|---|---|
invalid_document | Document image is blurry, cropped, or unreadable |
face_mismatch | Selfie does not match the document photo |
document_expired | The identity document has expired |
unsupported_document | Document type or country not supported |
session_expired | Verification session has expired |
rate_limit_exceeded | Too many requests (limit: 100 requests/minute) |
Next Steps
- Webhooks — set up and manage webhook endpoints
- Credential Flow — understand the full credential lifecycle
- Express.js Middleware — verify credentials in your API