Skip to Content
GuidesKYC Integration

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

Integration Modes

Solidus Verify supports two integration patterns:

ModeHow it worksBest for
Hosted flowRedirect users to the Solidus-hosted verification pageFastest integration, no UI work needed
API flowUpload documents directly via API callsCustom UI, mobile apps, embedded experiences

Base URL

All API requests use the following base URL:

https://verify.solidus.network/v1

Authenticate every request with your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Hosted Flow

Step 1: Create a Verification Session

There is no webhook or prefill field on session creation. Webhooks are a separate resource — register an endpoint once via POST /webhooks and it receives events for every future session (see the Webhooks guide). Solidus doesn’t collect pre-fill contact fields at session-creation time.

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', }), }) const session = await response.json()

The response nests the session fields under session, alongside the hosted-flow sessionUrl:

{ "session": { "id": "ver_2xK9mP4qR7nL", "organizationId": "org_3Vn9wLkDfT", "status": "pending", "level": 1, "sandbox": false, "subjectDid": "did:solidus:testnet:7Hk3mRtQZv...", "redirectUrl": "https://myapp.com/verification-complete", "sessionToken": "sess_tok_7Hk3mRtQZv...", "createdAt": "2026-05-07T12:00:00Z", "expiresAt": "2026-05-07T13:00:00Z" }, "sessionUrl": "https://verify.solidus.network/v/s/sess_tok_7Hk3mRtQZv..." }

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.sessionUrl

Step 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=completed

Step 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 (nested under session, same shape as GET /verifications/:id in the Verify API reference):

{ "session": { "id": "ver_2xK9mP4qR7nL", "status": "completed", "level": 1, "subjectDid": "did:solidus:testnet:7Hk3mRtQZv...", "documents": [ { "type": "passport", "side": "front", "status": "verified", "extractedData": { "firstName": "Jane", "lastName": "Doe", "dateOfBirth": "1990-03-15", "documentNumber": "***redacted***", "nationality": "US" } } ], "liveness": { "status": "passed", "score": 0.97, "completedAt": "2026-05-07T12:05:00Z" }, "credentialId": "vc_3Kn8rTpXm2", "completedAt": "2026-05-07T12:05:30Z", "createdAt": "2026-05-07T12:00:00Z" } }

Fetch the issued credential separately from the SDK or chain by its credentialId — the session response above doesn’t embed the full credential object.

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, }), }) const { session } = await response.json() const sessionId = session.id

Step 2: Upload Documents

Upload identity documents to the session. Each upload needs side and type fields — there is no document_front/document_back/selfie type enum. Mark the last document upload final: 'true' to trigger processing (there is no separate submit step — see Step 3).

// Upload the front of an identity document const frontUpload = new FormData() frontUpload.append('file', frontImageFile) frontUpload.append('side', 'front') frontUpload.append('type', 'passport') // or driving_license | national_id | residence_permit frontUpload.append('final', 'false') 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). // final: 'true' marks the document step complete and triggers processing. const backUpload = new FormData() backUpload.append('file', backImageFile) backUpload.append('side', 'back') backUpload.append('type', 'passport') backUpload.append('final', 'true') await fetch( \`https://verify.solidus.network/v1/verifications/${sessionId}/documents\`, { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, body: backUpload, } )

Document upload fields:

FieldTypeRequiredDescription
sidefront | backYesWhich side of the document
typepassport | driving_license | national_id | residence_permitYesDocument type
final'true' | 'false'NoIf 'true', marks the document step complete and triggers processing

Liveness (face match) is a separate flow — request a challenge with GET /verifications/:id/liveness-challenge, then upload the response frames with POST /verifications/:id/liveness. There is no selfie document type or proof_of_address document type.

Step 3: Processing Starts Automatically

There is no /submit endpoint. Uploading the final required document with final: 'true' (Step 2) triggers processing directly — poll or wait for a webhook as shown in Step 4.

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.session.status if (status === 'processing') { await new Promise((r) => setTimeout(r, 3000)) } }

Webhook Events

Configure webhooks to receive real-time notifications about verification status changes.

EventDescription
verification.completedVerification finished successfully, credential issued
verification.failedVerification failed (document issues, face mismatch, etc.)
verification.expiredSession expired before the user completed verification
credential.issuedA new Verifiable Credential was issued
credential.revokedAn existing credential was revoked

Default events (if events is omitted when creating the endpoint): verification.completed, credential.issued.

Webhook payload example:

{ "id": "evt_8nR3kL5mQ2", "type": "verification.completed", "createdAt": "2026-05-07T12:05:30Z", "data": { "verificationId": "ver_2xK9mP4qR7nL", "status": "completed", "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', 'timeout', or 'document_rejected' 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:

OutcomeBehavior
passVerification succeeds, test credential issued
failVerification fails with simulated document issues
timeoutSimulates a processing timeout for testing error handling
document_rejectedSimulates a rejected document for testing error handling

Verification Statuses

A session moves through these statuses:

pending ──> processing ──> completed ├──> failed └──> expired
StatusDescription
pendingSession created, waiting for user to start
processingDocuments uploaded, verification in progress
completedVerification passed, credential issued
failedVerification failed
expiredSession 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:

CodeDescription
invalid_documentDocument image is blurry, cropped, or unreadable
face_mismatchSelfie does not match the document photo
document_expiredThe identity document has expired
unsupported_documentDocument type or country not supported
session_expiredVerification session has expired
rate_limit_exceededToo many requests (limit: 100 requests/minute)

Next Steps

Last updated on