Overview
Identity verification is how a user earns a Sigil — their portable, verified Proof of Personhood. This guide covers the full KYC (Know Your Customer) flow that backs it. Three verification modes are available, controlled per integration via a server-side configuration:- WebSDK mode (default) — the embedded third-party SDK handles everything: document upload, selfie capture, and liveness checks
- Hybrid mode — your app uploads documents via the API, then launches the SDK for a liveness-only step
- Document-only mode — your app uploads documents via the API with no SDK or liveness step required
meta.kyc_mode on the kyc_verification onboarding step. The value is "websdk", "hybrid", or "document_only".
The KYC system is:
- Webhook-updated — verification results arrive asynchronously; your client polls for the final outcome
- GDPR-compliant — only the user’s name is stored locally; all identity documents and PII are held by the verification provider
- Onboarding-integrated — when KYC is approved while the user is on the
kyc_verificationonboarding step, onboarding auto-advances to the next step
Quick Start
WebSDK Mode
No additional CORS configuration is required. The WebSDK runs in a SumSub-hosted iframe — your origin only needs CORS allowed on the Sumvin API itself (handled by the standard API config), not on
id.sumsub.com or api.sumsub.com. The SIS dashboard CORS origin allowlist governs blockchain RPC traffic, not KYC.1. Create the applicant (idempotent)
Before requesting an access token, create the SumSub applicant for the user. This is idempotent — safe to call on every entry into the verification flow."already_existed": true. Either way, proceed to step 2.
You can skip this step and call
/v0/kyc/access-token directly — the WebSDK will create the applicant on first interaction. Calling /v0/kyc/applicant first is preferred because it pins the sumsub_applicant_id server-side immediately, which makes status polling and webhook correlation more reliable.2. Generate an SDK access token
expirationHandler below.
3. Embed the SumSub SDK
Pick the SumSub SDK that matches your platform. Both consume the sameaccess_token from step 2.
- Web (React)
- React Native
The WebSDK runs in a SumSub-hosted iframe; your origin does not need to be allowlisted with SumSub for the iframe to load. The reference integration in our B2C app uses
@sumsub/websdk-react:expirationHandler prop on <SumsubWebSdk> for the Web React SDK, or as the second argument to SNSMobileSDK.init() for React Native. Without it, long-running verification sessions break mid-flow. This is the most common integration mistake.
Do not use
/v0/kyc/sumsub/connect/authorize. That endpoint starts the SumSub ID Connect (OIDC cross-partner identity sharing) flow, which is a separate feature requiring distinct provisioning with SumSub. For the standard onboarding flow described here, you only need /v0/kyc/applicant and /v0/kyc/access-token.4. Advance your UI on completion
When the SDK signals completion, the user has finished their part. Do not poll for approval here. SumSub’s review runs asynchronously (seconds to minutes); the result arrives via webhook to/v0/webhooks/kyc/sumsub/events and updates kyc_verified_at / kyc_rejected_at on the user.
The completion signal differs by platform:
- Web (
@sumsub/websdk-react): theonMessagecallback fires withidCheck.onApplicantSubmitted,idCheck.onApplicantResubmitted, oridCheck.onApplicantActionCompleted. - React Native (
@sumsub/react-native-mobilesdk-module): thelaunch()promise resolves when the user closes the SDK;withHandlers({ onStatusChanged })fires earlier as the user progresses.
kyc_verification onboarding step; the user proceeds to the next step. Subsequent reads of /v0/user/me or /v0/kyc/status reflect the webhook-driven state once review completes.
If you need to display the verification result on a “checking…” screen, call:
5. Onboarding auto-advances
If the user is on thekyc_verification onboarding step when the webhook delivers an approved review, onboarding automatically advances to the next step. Re-fetch onboarding state after webhook delivery (or on the user’s next session):
Hybrid Mode
1. Create an applicant
2. Upload identity documents
3. Check all required documents are uploaded
4. Launch liveness-only SDK
Generate a liveness-scoped access token and initialize the SDK for the liveness step only:5. Submit for review
After the liveness step completes, submit the applicant for review:6. Poll for verification status
Same as WebSDK mode — poll the status endpoint with?refresh=true until a terminal state is reached.
GET /v0/kyc/status — Poll current status.
Document-Only Mode
1. Create an applicant
2. Upload identity documents
3. Check all required documents are uploaded
all_uploaded is true before submitting.
4. Submit for review
No liveness step is required. Submit directly after documents are uploaded:5. Poll for verification status
Same as WebSDK mode — poll the status endpoint with?refresh=true until a terminal state is reached.
GET /v0/kyc/status — Poll current status.
KYC Modes
The verification mode is selected per integration server-side. Your frontend reads the mode frommeta.kyc_mode on the kyc_verification onboarding step and renders the appropriate flow.
POST /v0/kyc/documents — Upload a document image.
In WebSDK mode, you generate an access token and hand control to the SDK. In hybrid mode, you create an applicant, upload documents through the API, launch the SDK for liveness only, then submit for review. In document-only mode, you create an applicant, upload documents, and submit — no SDK initialization is needed.
meta.kyc_mode is always populated for the kyc_verification step. When the server-side configuration is missing, unreachable, or returns an unrecognized value, the API falls back to "websdk". Frontends do not need to defend against meta being null or kyc_mode being absent — branching on the three documented values is sufficient.Per-organisation override
Partner organisations can pin their KYC mode independently of the global default by enabling thekyc feature on an environment:
mode values: websdk, hybrid, document_only. The config object must either be omitted (or empty) or include a recognised mode value — supplying an unknown key (e.g. { "kyc_mode": "websdk" }) or an unknown value returns 422 Unprocessable Entity with error_code: KYC-422-001 and the allowed values listed in the response detail, so silent typos can’t mask an operator’s intent. If the feature row is disabled or no config is supplied, the global default applies.
Operators can change a tenant’s mode at any time. Existing in-flight verification sessions are unaffected — the mode is read at the start of each onboarding state response, so a new selection takes effect from the next meta.kyc_mode your frontend reads.
KYC State
Reading Status
Fetch the current KYC status at any time:?refresh=true to pull the latest result from the verification provider instead of relying on the locally cached state. Use refresh=true during active verification when you need the most current status.
Response Shape
The
_links object includes a start-verification link when the status is pending or retry, guiding the client to begin or restart the verification flow.
KYC Lifecycle
Status transitions happen asynchronously. The API receives webhook events from the verification provider and updates the user’s KYC state. Your client discovers the new state by polling the status endpoint with?refresh=true.
GET /v0/kyc/status — Poll current status.
Endpoints
Generate SDK Access Token
Response: 200 OK
When
level=liveness, the returned token restricts the SDK to the liveness verification step only. Use this in hybrid mode after documents have been uploaded via the API.
Check KYC Status
Response: 200 OK — see Response Shape above.
When
refresh=true:
- If the user has no applicant record, returns 404 Not Found
- If the provider is unreachable, returns 502 Bad Gateway
- If KYC is approved and the user is on the
kyc_verificationonboarding step, onboarding auto-advances
Get KYC Details
Response: 200 OK
Rejection object:
Create Applicant
200 OK response and already_existed: true. A new applicant returns 201 Created.
Response: 200 OK / 201 Created
Upload Document
POST /v0/kyc/applicant — Create a KYC applicant.
Request: multipart/form-data
Response: 201 Created
Check Required Documents
POST /v0/kyc/applicant — Create a KYC applicant.
Response: 200 OK
When
all_uploaded is true, the _links object includes a submit link:
Submit for Review
404 Not Found (KYC-404-001). When required-document state can be fetched from the provider, the API checks all_uploaded first and returns 409 Conflict (KYC-409-001) if any required step is still missing documents — the same gate that backs the _links.submit field on GET /v0/kyc/documents/required. Always confirm all_uploaded is true before calling submit. If the required-docs lookup itself fails (provider unreachable), the API logs a warning and forwards the submission anyway — the provider then enforces its own completeness check and the call surfaces as 500 Internal Server Error (KYC-500-004) if the submission is rejected.
Response: 200 OK
After submission, poll the status endpoint with
?refresh=true for the verification result.
GET /v0/kyc/status — Poll current status.
Integration Patterns
WebSDK Flow
A typical frontend integration using WebSDK mode follows this sequence:- Check current status.
- If
pendingorretry, create the applicant (idempotent), then generate an SDK access token. - Initialize the verification SDK with the token.
- When the SDK signals completion, advance your UI. Approval is delivered asynchronously by webhook — do not block on it here.
- On subsequent reads of
/v0/kyc/status(or/v0/user/me), the new state is reflected once the webhook has been processed. If you need an immediate read-through to the provider, call/v0/kyc/status?refresh=true.
GET /v0/kyc/status— Check current status.POST /v0/kyc/applicant— Create the SumSub applicant (idempotent).POST /v0/kyc/access-token— Generate an SDK access token.
/v0/kyc/status?refresh=true which fetches live state from the provider on demand.
Hybrid Flow
A frontend integration using hybrid mode follows this sequence:- Check current status.
- If
pendingorretry, create an applicant. - Upload each required document.
- Check required documents to confirm
all_uploadedistrue. - Generate a liveness-scoped access token (
?level=liveness). - Initialize the SDK with the liveness token — the SDK opens directly to the liveness step.
- When the SDK signals completion, submit for review.
- Poll status with
?refresh=truefor the final result.
GET /v0/kyc/status— Check current status.POST /v0/kyc/applicant— Create a KYC applicant.POST /v0/kyc/documents— Upload a document image.GET /v0/kyc/documents/required— Check required documents status.POST /v0/kyc/access-token— Generate an SDK access token.POST /v0/kyc/submit— Submit for review.
Document-Only Flow
A frontend integration using document-only mode follows this sequence:- Check current status.
- If
pendingorretry, create an applicant. - Upload each required document.
- Check required documents to confirm
all_uploadedistrue. - Submit for review.
- Poll status with
?refresh=truefor the final result.
GET /v0/kyc/status— Check current status.POST /v0/kyc/applicant— Create a KYC applicant.POST /v0/kyc/documents— Upload a document image.GET /v0/kyc/documents/required— Check required documents status.POST /v0/kyc/submit— Submit for review.
Polling for Approval
After the user completes the verification flow, the verification result arrives asynchronously via webhook. Poll the status endpoint withrefresh=true at a reasonable interval until a terminal state is reached:
Handling Rejection
There are two rejection outcomes with different behaviours: Retry (status: "retry") — the user can fix and resubmit. The reject_reason field explains what needs correction. In WebSDK or hybrid mode, generate a new access token and let the user re-enter the SDK flow. In document-only mode, re-upload corrected documents and submit again.
Rejected (status: "rejected") — terminal. The user cannot retry with the same identity documents. Display the reject_reason and direct them to contact support.
retry, the _links object includes start-verification so your client can initiate a new verification attempt.
Onboarding Integration
KYC verification is one step in the onboarding flow. When KYC is approved and the user is on thekyc_verification onboarding step, the step auto-advances without requiring an explicit submit call. This happens in two scenarios:
- Webhook-driven: The verification provider sends a webhook with an approval, and the API advances onboarding automatically.
- Poll-driven: Your client polls the status endpoint with
?refresh=true, the API detects approval, and advances onboarding.
GET /v0/kyc/status — Poll current status.
In both cases, re-fetch onboarding state afterward to render the next step:
Error Handling
All error responses follow the RFC 7807 Problem Details format:Examples by status
401 Unauthorized — missing or invalid auth headers (any KYC endpoint):
403 Forbidden — endpoint requires KYC and the user has not verified yet:
400 Bad Request — file exceeds the 10 MB limit on POST /v0/kyc/documents:
400 Bad Request — unsupported MIME type on POST /v0/kyc/documents:
409 Conflict — POST /v0/kyc/submit called before all required documents uploaded:
502 Bad Gateway — provider unreachable on GET /v0/kyc/status?refresh=true or GET /v0/kyc/documents/required:
POST /v0/kyc/access-token— Generate an SDK access token.POST /v0/kyc/applicant— Create a KYC applicant.POST /v0/kyc/documents— Upload a document image.GET /v0/kyc/documents/required— Check required documents status.
Reference Tables
KYC Statuses
Polling stop conditions:
approved and rejected are the only terminal states. Stop polling when status is one of those two values. pending (verification not started), in_progress (verification underway), and retry (additional info requested) are all non-terminal — keep polling until the status becomes terminal or the user takes a corrective action.
retry requires user action. It is not auto-recoverable — show the reject_reason to the user and prompt them to resubmit. Polling will continue to return retry until the user re-enters the verification flow.
Required Step Types
Thestep_type field on each entry of GET /v0/kyc/documents/required is set by the verification provider and depends on the configured KYC level. Treat the value as an open string — the same level can return additional step types over time without an API version change.
The values below are the ones the integration currently emits:
In WebSDK mode, the SDK fulfils these steps internally — your client typically only inspects this list in hybrid and document-only modes, where you upload the documents yourself.
Document Types
Document Sides
Some document types require both sides to be uploaded separately.
Passports typically require only a single upload (no side). ID cards and driver’s licenses typically require both
FRONT_SIDE and BACK_SIDE.
Document Upload Constraints
Validated server-side before forwarding to the verification provider. A request that violates any of these constraints fails with a400 Bad Request (no provider call is made — see Error Handling).
Uploading a
document_type=DRIVERS or document_type=ID_CARD typically requires both FRONT_SIDE and BACK_SIDE uploads to satisfy the corresponding IDENTITY step. Passports require a single upload with no side.