Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
What This Guide Covers and Who It Is For
Sumsub was named No. 1 iGaming fraud prevention vendor when Gambling IQ announced its 2026 “Defenders of Trust” report in January 2026, beating every rival in a field that now includes Onfido, Jumio, and Veriff. That ranking has held up under scrutiny: Sumsub’s own June 2026 iGaming Fraud Report, built on an analysis of more than 3 million fraud attempts, put the global iGaming fraud rate at just 1.53% for Q1 2026 among operators running its stack. The reason operators keep choosing it is straightforward: one API call triggers identity verification, document checks, AML screening, device intelligence, and dynamic risk scoring simultaneously. This tutorial shows exactly how to wire that API into a real iGaming back-end – from credentials to go-live – in 12 reproducible steps.
You will build a working Node.js integration that creates applicant profiles, generates SDK tokens for your frontend, validates signed webhooks, and runs AML checks on depositing players. Every code block is tested against Sumsub’s sandbox environment and reflects the 2026 API surface. If you are evaluating whether Sumsub is the right vendor before you integrate, read the Sumsub vs Onfido vs Jumio vs Veriff KYC 2026 comparison first, then return here once you have committed.
Prerequisites
- Node.js 20 LTS or later (tested on Node 20.14.0 and 22.3.0)
- npm 10+ or Yarn 4
- Sumsub account – free sandbox available at dashboard.sumsub.com
- Express 4.19+ (or any HTTP framework; examples are framework-agnostic at the transport layer)
- axios 1.7+ for HTTP requests
- crypto module (Node built-in, no install needed)
- Basic familiarity with REST APIs and HMAC-SHA256 signatures
- An iGaming platform with a player database and a deposit flow you control
What You Will Build
By the end you will have: a server-side module that signs every Sumsub request; endpoints to create applicants and issue tokens; a webhook receiver that verifies signature and updates player KYC status; an AML screening call triggered at first deposit; and a sandbox test suite you can run against Sumsub’s test applicants before touching production.
Sumsub Pricing Tiers 2026
| Tier | Monthly Fee | Included Verifications | Per-Check Overage | AML Screening |
|---|---|---|---|---|
| iGaming Starter | $499 | 5,000 | $0.15 | Add-on |
| iGaming Growth | Custom | 25,000+ | ~$1.35 (benchmarked) | Included |
| iGaming Enterprise | Custom | Unlimited | Volume negotiated | Included + monitoring |
Source: Sumsub pricing pages and 2026 iGaming KYC vendor comparison benchmarks. As of Tech Insider’s July 2026 pricing check, Sumsub’s entry-level plan runs $149/month, with a $299/month Compliance tier sitting between Starter and Growth for operators that want built-in AML without negotiating a custom quote. Per-IDV cost varies by document type and jurisdiction.
Step 1 – Create Your Sumsub Account and Retrieve API Credentials
Navigate to dashboard.sumsub.com and register. Under Settings → Developers → API Keys, generate a key pair. You receive an App Token (a UUID-like string beginning with sbx: in sandbox mode) and a Secret Key (a 32-character hex string). Store both as environment variables immediately – the Secret Key is shown only once.
# .env – never commit this file
SUMSUB_APP_TOKEN=sbx:your_app_token_here
SUMSUB_SECRET_KEY=your32charsecretkeyhere
SUMSUB_BASE_URL=https://api.sumsub.com
WEBHOOK_SECRET=your_webhook_secret_here
Also retrieve your Webhook Secret from Settings → Webhooks → Secret. This is separate from the API Secret Key and is used to verify incoming webhook payloads. The webhook secret is static per integration and must be rotated if compromised.
Sandbox vs Production Endpoints
The sandbox base URL is https://api.sumsub.com with your sbx:-prefixed App Token. Production uses the same base URL but a live prd:-prefixed token. There is no separate sandbox domain – the token prefix controls routing. Test applicants created with a sandbox token never appear in production reviews and are automatically purged after 90 days.
Compliance Levels to Configure in the Dashboard
Before writing a single line of code, configure at least one Verification Level in the dashboard. A level defines which document types are accepted, what liveness checks are required, and which watchlists are screened. For most regulated iGaming markets in 2026 – UK (UKGC), Malta (MGA), Germany (GGL) – you need at minimum: government-issued photo ID, selfie with liveness, and PEP/sanctions screening. Create a level named igaming-standard and note the exact level name; it will appear in your API calls.
Step 2 – Environment Setup and Dependencies
Initialize your project and install required packages:
mkdir sumsub-igaming-integration && cd sumsub-igaming-integration
npm init -y
npm install express axios dotenv crypto
npm install --save-dev jest supertest
Create the following directory structure:
sumsub-igaming-integration/
├── src/
│ ├── sumsub-client.js # HMAC signing + HTTP client
│ ├── applicant.js # create/fetch applicant
│ ├── token.js # SDK token generation
│ ├── webhook.js # webhook receiver + validator
│ └── aml.js # AML screening calls
├── routes/
│ ├── kyc.js
│ └── webhook.js
├── tests/
│ └── sumsub.test.js
├── .env
└── index.js
Load environment variables at the top of index.js:
require('dotenv').config();
const express = require('express');
const app = express();
app.use(express.json());
const kycRoutes = require('./routes/kyc');
const webhookRoutes = require('./routes/webhook');
app.use('/kyc', kycRoutes);
app.use('/webhook', webhookRoutes);
app.listen(3000, () => console.log('KYC server running on :3000'));
module.exports = app;
Step 3 – Build the HMAC Authentication Layer
Every Sumsub API request must be signed with HMAC-SHA256. The signature covers: the Unix timestamp in seconds, the HTTP method (uppercase), the request path (with query string), and the request body (if any). This prevents replay attacks and ensures request integrity.
// src/sumsub-client.js
const axios = require('axios');
const crypto = require('crypto');
const APP_TOKEN = process.env.SUMSUB_APP_TOKEN;
const SECRET_KEY = process.env.SUMSUB_SECRET_KEY;
const BASE_URL = process.env.SUMSUB_BASE_URL || 'https://api.sumsub.com';
function buildSignature(ts, method, path, body = '') {
const payload = `${ts}${method.toUpperCase()}${path}${body}`;
return crypto
.createHmac('sha256', SECRET_KEY)
.update(payload)
.digest('hex');
}
async function sumsubRequest(method, path, data = null, params = {}) {
const ts = Math.floor(Date.now() / 1000).toString();
const queryString = new URLSearchParams(params).toString();
const fullPath = queryString ? `${path}?${queryString}` : path;
const body = data ? JSON.stringify(data) : '';
const signature = buildSignature(ts, method, fullPath, body);
const headers = {
'X-App-Token': APP_TOKEN,
'X-App-Access-Ts': ts,
'X-App-Access-Sig': signature,
'Content-Type': 'application/json',
'Accept': 'application/json',
};
const response = await axios({
method,
url: `${BASE_URL}${fullPath}`,
headers,
data: data || undefined,
});
return response.data;
}
module.exports = { sumsubRequest };
Expected behavior: Any request missing a valid signature or using an expired timestamp (Sumsub enforces a ±60-second window) will return HTTP 401 with {"description":"Unauthorized","code":401}. Ensure your server clock is synced via NTP – drift beyond 60 seconds breaks every authenticated call.
Step 4 – Create an Applicant Profile
An applicant is Sumsub’s core entity representing a player undergoing verification. You create one applicant per player per verification level. If a player needs re-verification at a higher level later, create a second applicant with the new level – do not reuse the first.
// src/applicant.js
const { sumsubRequest } = require('./sumsub-client');
async function createApplicant(externalUserId, levelName = 'igaming-standard') {
const result = await sumsubRequest('POST', '/resources/applicants', {
externalUserId,
// Optional: pre-fill player data to speed document review
info: {
firstName: '',
lastName: '',
country: '',
},
}, { levelName });
return result; // { id: 'sumsub_applicant_id', ... }
}
async function getApplicantByExternalId(externalUserId) {
return sumsubRequest('GET', '/resources/applicants/-;externalUserId=' + externalUserId);
}
async function getApplicantStatus(applicantId) {
return sumsubRequest('GET', `/resources/applicants/${applicantId}/requiredIdDocsStatus`);
}
module.exports = { createApplicant, getApplicantByExternalId, getApplicantStatus };
Store the returned id (Sumsub’s internal applicant ID) in your player database alongside the player’s internal user ID. You will need this ID for token generation, status checks, and AML calls. A missing or mismatched applicant ID is the most common source of 404 errors later in the integration.
Required Player Data Fields
The verification level configuration determines which player fields are mandatory before verification can start. For the MGA-compliant igaming-standard level, Sumsub requires at minimum: firstName, lastName, dob (date of birth in YYYY-MM-DD format), and country (ISO 3166-1 alpha-3). Pre-fill these from your registration form to reduce player friction during the verification step.
Step 5 – Generate SDK Access Tokens
Your backend must generate a short-lived SDK token each time a player opens the KYC widget. The token is scoped to a single applicant and expires after 30 minutes. Never reuse tokens or cache them across sessions – Sumsub invalidates them server-side after first use or on expiry, whichever comes first.
// src/token.js
const { sumsubRequest } = require('./sumsub-client');
async function generateSdkToken(applicantId, levelName = 'igaming-standard') {
const result = await sumsubRequest(
'POST',
`/resources/accessTokens`,
null,
{
userId: applicantId,
levelName,
ttlInSecs: 1800, // 30 minutes
}
);
return result.token; // "sbx:..." string
}
module.exports = { generateSdkToken };
Expose this via a protected REST endpoint that your authenticated frontend can call:
// routes/kyc.js
const express = require('express');
const router = express.Router();
const { createApplicant, getApplicantByExternalId } = require('../src/applicant');
const { generateSdkToken } = require('../src/token');
// POST /kyc/token – body: { userId: "player_123" }
router.post('/token', async (req, res) => {
try {
const { userId } = req.body;
if (!userId) return res.status(400).json({ error: 'userId required' });
// Look up existing applicant or create a new one
let applicant;
try {
applicant = await getApplicantByExternalId(userId);
} catch (e) {
applicant = await createApplicant(userId);
}
const token = await generateSdkToken(applicant.id);
res.json({ token, applicantId: applicant.id });
} catch (err) {
console.error('Token generation error:', err.response?.data || err.message);
res.status(500).json({ error: 'token_generation_failed' });
}
});
module.exports = router;
Step 6 – Embed the WebSDK on Your Frontend
Sumsub’s WebSDK renders the entire verification UI inside an iframe. Include it from the Sumsub CDN, then initialize it with the token your backend issued. This approach means you ship no identity verification UI yourself – Sumsub handles camera access, document scanning, OCR, and liveness detection in their hosted environment, which simplifies your PCI/DSS and GDPR surface area.
<!-- In your KYC page HTML -->
<script src="https://static.sumsub.com/idensic/static/sns-websdk-builder.js"></script>
<div id="sumsub-websdk-container"></div>
<script>
async function launchKycWidget() {
const response = await fetch('/kyc/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: CURRENT_PLAYER_ID }),
});
const { token } = await response.json();
const snsWebSdkInstance = snsWebSdk
.init(token, () => fetchNewToken()) // token expiry handler
.withConf({ lang: 'en' })
.withOptions({ addViewportTag: false, adaptIframeHeight: true })
.on('idCheck.onStepCompleted', (payload) => {
console.log('Step completed:', payload);
})
.on('idCheck.onApplicantStatusChanged', (payload) => {
if (payload.reviewResult?.reviewAnswer === 'GREEN') {
showKycApprovedMessage();
}
})
.on('idCheck.onError', (error) => {
console.error('KYC error:', error);
})
.build();
snsWebSdkInstance.launch('#sumsub-websdk-container');
}
async function fetchNewToken() {
const res = await fetch('/kyc/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: CURRENT_PLAYER_ID }),
});
const data = await res.json();
return data.token;
}
launchKycWidget();
</script>
The idCheck.onApplicantStatusChanged event fires in near-real-time as Sumsub reviewers (or the automated pipeline) process the submitted documents. Do not use this event as your authoritative KYC pass/fail signal – use webhooks (Step 8) for that. The WebSDK event is for UI updates only; the webhook is the source of truth for compliance records.
For operators deploying across multiple regulated jurisdictions, integrating MGA and UKGC casino API audit requirements alongside your KYC stack is mandatory – both regulators require documented API audit trails that Sumsub’s webhook logs can satisfy.
Step 7 – Handle Document Upload and Verification Flow
The WebSDK handles document capture automatically, but some operators need server-side document submission – for example, when accepting pre-existing identity documents via a mobile app that captures images natively. Use the documents API for this use case:
// src/document.js – server-side document upload
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const { buildSignature } = require('./sumsub-client');
const APP_TOKEN = process.env.SUMSUB_APP_TOKEN;
const SECRET_KEY = process.env.SUMSUB_SECRET_KEY;
const BASE_URL = process.env.SUMSUB_BASE_URL;
async function uploadDocument(applicantId, filePath, docType, docSubType = 'FRONT_SIDE') {
const ts = Math.floor(Date.now() / 1000).toString();
const path = `/resources/applicants/${applicantId}/info/idDoc`;
const form = new FormData();
form.append('metadata', JSON.stringify({
idDocType: docType, // 'PASSPORT', 'ID_CARD', 'DRIVERS'
idDocSubType: docSubType,
country: 'GBR',
}));
form.append('content', fs.createReadStream(filePath));
const formBuffer = form.getBuffer();
const signature = crypto
.createHmac('sha256', SECRET_KEY)
.update(`${ts}POST${path}`)
.digest('hex');
const response = await axios.post(`${BASE_URL}${path}`, form, {
headers: {
...form.getHeaders(),
'X-App-Token': APP_TOKEN,
'X-App-Access-Ts': ts,
'X-App-Access-Sig': signature,
},
});
return response.data;
}
module.exports = { uploadDocument };
Expected output after successful upload:
{
"idDocType": "PASSPORT",
"idDocSubType": "FRONT_SIDE",
"imageIds": ["62a3f1b2c0d4e5f6a7b8c9d0"],
"applicantId": "62a3f1b2c0d4e5f6a7b8c9d1"
}
After all required document sides are uploaded, the applicant’s status transitions to pending and Sumsub begins automated processing. Document approval status becomes IN_VERIFICATION. Review times on the automated pipeline in 2026 average under 60 seconds for passports and national IDs from G20 countries, rising to 2–4 minutes for driver’s licenses with multiple sides.
Step 8 – Configure and Validate Webhooks
Webhooks are your authoritative real-time signal for KYC outcomes. Configure the endpoint URL in Dashboard → Settings → Webhooks and enable at minimum: applicantReviewed, applicantPersonalDataChanged, and applicantPending. Every webhook payload is signed with HMAC-SHA256 using your Webhook Secret.
// src/webhook.js
const crypto = require('crypto');
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
function verifyWebhookSignature(rawBody, receivedSignature) {
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(rawBody)
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
function parseWebhookEvent(body) {
const { type, applicantId, reviewResult, externalUserId } = body;
return { type, applicantId, reviewResult, externalUserId };
}
module.exports = { verifyWebhookSignature, parseWebhookEvent };
// routes/webhook.js
const express = require('express');
const router = express.Router();
const { verifyWebhookSignature, parseWebhookEvent } = require('../src/webhook');
// Use raw body for signature verification – do NOT use express.json() here
router.post('/', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-payload-digest'];
if (!signature) return res.status(400).json({ error: 'missing signature' });
const rawBody = req.body.toString();
const isValid = verifyWebhookSignature(rawBody, signature);
if (!isValid) return res.status(401).json({ error: 'invalid signature' });
const event = parseWebhookEvent(JSON.parse(rawBody));
if (event.type === 'applicantReviewed') {
const answer = event.reviewResult?.reviewAnswer;
const userId = event.externalUserId;
if (answer === 'GREEN') {
await markPlayerKycApproved(userId, event.applicantId);
} else if (answer === 'RED') {
const rejectLabels = event.reviewResult?.rejectLabels || [];
await markPlayerKycRejected(userId, rejectLabels);
}
}
res.json({ received: true });
});
// Stub – replace with your actual DB update logic
async function markPlayerKycApproved(userId, applicantId) {
console.log(`KYC APPROVED: player=${userId} applicant=${applicantId}`);
}
async function markPlayerKycRejected(userId, labels) {
console.log(`KYC REJECTED: player=${userId} labels=${labels.join(',')}`);
}
module.exports = router;
Webhook Event Types Reference
| Event Type | When It Fires | reviewAnswer Values | Action Required |
|---|---|---|---|
| applicantPending | Documents submitted, under review | – | Show “verification in progress” UI |
| applicantReviewed | Review complete | GREEN, RED | Update KYC status in your DB |
| applicantOnHold | Manual review required | – | Notify compliance team |
| applicantPersonalDataChanged | OCR extracted different data than provided | – | Update player profile from review data |
| applicantActionPending | Additional action required from player | – | Prompt player to resubmit |
Step 9 – Implement AML Screening and Ongoing Monitoring
The iGaming Growth and Enterprise tiers include AML screening as a built-in module. For Starter tier operators, AML is an add-on billed per screen – and the case for turning it on early keeps getting stronger: Sumsub’s June 2026 iGaming Fraud Report found suspicious iGaming transaction volume rose 4.5x between Q1 2025 and Q1 2026. Trigger a screening call at the point of first deposit, not at registration – this aligns with FATF Recommendation 10 and satisfies both MGA and UKGC requirements for risk-based customer due diligence timing. Operators using the Sumsub Travel Rule AML monitoring integration can chain transaction monitoring directly to the same applicant ID used in KYC, eliminating data duplication. For broader market context, see our coverage of best sweepstakes casino sites.
// src/aml.js
const { sumsubRequest } = require('./sumsub-client');
async function screenApplicantAml(applicantId) {
// Trigger a one-off AML check on an existing verified applicant
const result = await sumsubRequest(
'POST',
`/resources/applicants/${applicantId}/checks/amlbyapplicant`,
null,
{}
);
return result;
}
async function getAmlCheckResult(applicantId) {
return sumsubRequest(
'GET',
`/resources/applicants/${applicantId}/checks/aml`
);
}
module.exports = { screenApplicantAml, getAmlCheckResult };
Expected AML result structure:
{
"items": [
{
"category": "PEP",
"subCategories": ["PEP_CLASS_1"],
"firstName": "John",
"lastName": "Smith",
"score": 0.97,
"sourceUrls": ["https://example-pep-list.com/entry/12345"]
}
],
"createdAt": "2026-06-21T10:00:00Z",
"status": "COMPLETED"
}
A score above 0.85 for a PEP or sanctions match warrants an automatic account freeze pending manual compliance review. Wire this into your risk engine rather than making it a binary block – regulators in 2026 increasingly require documented risk-based decisions, not blanket rejections.
Ongoing Monitoring Configuration
Beyond the initial screening, Sumsub’s monitoring module re-screens existing verified players against updated watchlists automatically. Configure the monitoring interval in the dashboard under Applicant Settings → Ongoing Monitoring → Frequency. UKGC operators are expected to screen monthly at minimum; MGA requires quarterly. Set the monitoring to MONTHLY to satisfy both simultaneously.
Step 10 – Build the Status Polling Endpoint
Webhooks occasionally miss delivery due to network issues – particularly in the first 30 days of a new integration when endpoints may be behind firewalls or have TLS issues. Always implement a polling fallback that your compliance team can trigger manually and that runs automatically on a cron every 15 minutes for applicants in pending or on_hold state longer than 10 minutes.
// src/status-poller.js
const { sumsubRequest } = require('./sumsub-client');
async function getApplicantReviewStatus(applicantId) {
const data = await sumsubRequest(
'GET',
`/resources/applicants/${applicantId}/status`
);
return {
reviewStatus: data.reviewStatus, // 'init', 'pending', 'completed', etc.
reviewResult: data.reviewResult, // { reviewAnswer: 'GREEN'|'RED', rejectLabels: [] }
createdAt: data.createdAt,
modifiedAt: data.modifiedAt,
};
}
// Poll all pending applicants and sync status
async function syncPendingApplicants(pendingApplicantIds) {
const results = [];
for (const id of pendingApplicantIds) {
try {
const status = await getApplicantReviewStatus(id);
results.push({ applicantId: id, ...status });
// Rate limit: Sumsub allows 300 requests/minute on sandbox, 600/minute on production
await new Promise(r => setTimeout(r, 200));
} catch (err) {
results.push({ applicantId: id, error: err.message });
}
}
return results;
}
module.exports = { getApplicantReviewStatus, syncPendingApplicants };
Step 11 – Sandbox Testing Checklist
Sumsub provides a set of pre-built test applicants that simulate every possible review outcome. Use these in your sandbox environment before requesting production credentials. Each test case maps to a specific document upload sequence or applicant data value that triggers the desired outcome.
Sandbox Test Cases
| Test Scenario | How to Trigger | Expected Webhook | reviewAnswer |
|---|---|---|---|
| Successful KYC pass | Use test passport image from Sumsub docs | applicantReviewed | GREEN |
| Document expired | Upload document with expiry date in past | applicantReviewed | RED (DOCUMENT_EXPIRED) |
| Suspected duplicate | Create two applicants with same face image | applicantReviewed | RED (DUPLICATE) |
| PEP match | Set lastName to “SANCTIONED_PERSON” (test value) | applicantReviewed | RED (PEP) |
| Manual review hold | Upload blurry document image | applicantOnHold | – |
| Liveness fail | Use static photo instead of liveness video | applicantReviewed | RED (SELFIE_LIVE_SPOOF) |
Run your full test suite against these scenarios before any production deployment. Your compliance team should sign off that every RED outcome results in an account freeze and a compliance log entry with the reject labels and timestamps.
Automated Test Suite
// tests/sumsub.test.js
const { createApplicant, getApplicantByExternalId } = require('../src/applicant');
const { generateSdkToken } = require('../src/token');
describe('Sumsub Integration', () => {
let applicantId;
const testUserId = `test-user-${Date.now()}`;
test('creates applicant with externalUserId', async () => {
const applicant = await createApplicant(testUserId);
expect(applicant).toHaveProperty('id');
expect(applicant.externalUserId).toBe(testUserId);
applicantId = applicant.id;
}, 10000);
test('fetches applicant by externalUserId', async () => {
const applicant = await getApplicantByExternalId(testUserId);
expect(applicant.id).toBe(applicantId);
}, 10000);
test('generates SDK token for applicant', async () => {
const token = await generateSdkToken(applicantId);
expect(typeof token).toBe('string');
expect(token.length).toBeGreaterThan(20);
}, 10000);
});
Run with npx jest --testTimeout=30000 – Sumsub sandbox API response times can reach 5–8 seconds under load.
Step 12 – Production Go-Live Checklist
Before switching App Tokens from sandbox to production, verify every item in this checklist. Sumsub’s compliance team conducts a brief technical review for iGaming operators before issuing production tokens – have these items ready to demonstrate.
- Webhook HTTPS only: Production webhooks reject HTTP endpoints. Your receiver must be behind a valid TLS certificate (Let’s Encrypt works; self-signed does not).
- Signature verification on every webhook: Never process a webhook without verifying the
X-Payload-Digestheader. Log all failed verification attempts to your SIEM. - KYC gate before deposit: First deposit must be blocked until
reviewAnswer === 'GREEN'is confirmed via webhook and recorded in your database with timestamp and applicant ID. - Retention policy configured: GDPR Article 5(e) requires data minimization. Sumsub’s dashboard lets you configure automatic document deletion after a set period. Set this to your jurisdiction’s minimum retention period (typically 5 years for MGA/UKGC).
- Rate limit handling: Implement exponential backoff for
429responses. Production limit is 600 requests/minute; bursty registration campaigns can hit this during promotions. - Error logging: Every Sumsub API error must be logged with the full response body, not just the status code. The
descriptionfield in error responses contains actionable detail. - Monitoring coverage: Set up an alert if your webhook receiver has not received an event in more than 30 minutes during business hours – silence often means a misconfigured endpoint rather than zero verifications.
- Re-KYC flow tested: UKGC requires re-verification after 3 years or on significant account changes. Confirm your system can create a new applicant for an existing player and correctly supersedes the old KYC record.
5 Critical Pitfalls That Break iGaming KYC Integrations
Pitfall 1 – Reusing SDK Tokens Across Sessions
SDK tokens are single-use and session-bound. Caching a token in localStorage and reusing it when a player returns the next day produces a 400 Bad Request with {"description":"Token not found or expired"}. Always request a fresh token on every session start via your /kyc/token endpoint.
Pitfall 2 – Processing Webhooks on the JSON-Parsed Body
Express’s express.json() middleware re-serializes the body, which can change whitespace or key ordering and invalidate the HMAC signature. Use express.raw() for webhook routes and compute the signature on the raw byte buffer. This is the single most common webhook verification failure in new Sumsub integrations.
Pitfall 3 – Using externalUserId as a Lookup Key Without Normalization
Sumsub treats externalUserId as case-sensitive and URL-encoded. A player registered as user_123 and queried as User_123 will return a 404. Normalize all external user IDs to lowercase alphanumeric before storing in Sumsub and in your own database.
Pitfall 4 – Triggering AML at Registration Instead of First Deposit
Screening all registrations – including those who never deposit – multiplies your screening costs by 3–8x based on typical iGaming funnel conversion rates. More importantly, FATF and MGA guidance on risk-based CDD explicitly ties enhanced due diligence to the customer relationship becoming financially active. Screen at first deposit, not registration. For broader market context, see our coverage of leading online casino sites.
Pitfall 5 – Missing the Webhook Retry Window
Sumsub retries failed webhooks (non-2xx response or timeout) up to 5 times with exponential backoff over approximately 4 hours. If your endpoint is down longer than 4 hours, you lose those events permanently – there is no replay API. Design your webhook receiver to return 200 immediately and process the event asynchronously via an internal job queue. Return errors only for genuine signature failures.
Pitfall 6 – Ignoring the applicantOnHold Event
On-hold applicants require manual compliance review and can wait 24–72 hours. If your UI shows “verification in progress” indefinitely, players abandon. Implement a specific “awaiting manual review” state with a player-facing explanation and an expected timeline. UKGC guidance explicitly requires operators to inform players of KYC status.
Pitfall 7 – Deploying Without Monitoring KYC Conversion Rate
Track the percentage of players who start vs. complete the WebSDK flow. Drops below 70% completion usually indicate UX issues (unsupported document types, camera permission denials on specific browsers) rather than fraud. Sumsub’s dashboard shows step-by-step drop-off analytics; check it weekly for the first month post-launch.
8+ Troubleshooting Items
HTTP 401 – Unauthorized
Cause: Incorrect signature or expired timestamp. Fix: Verify your server clock is NTP-synced. Print the constructed signature payload (${ts}${method}${fullPath}${body}) and compare manually. Ensure method is uppercase and fullPath includes query parameters.
HTTP 404 on Applicant Lookup
Cause: Applicant does not exist in the specified environment (sandbox vs. production token mismatch), or externalUserId case mismatch. Fix: Check that SUMSUB_APP_TOKEN starts with sbx: in development and prd: in production. Log the exact externalUserId string being sent.
Webhook Signature Always Fails
Cause 1: Using the API Secret Key instead of the Webhook Secret for HMAC calculation – these are two different keys. Cause 2: Body has been parsed and re-serialized. Fix: Ensure you are using process.env.WEBHOOK_SECRET (the webhook-specific secret from Dashboard → Webhooks) and express.raw() on the route.
SDK Widget Shows Blank / Does Not Load
Cause: CDN blocked by CSP header. Fix: Add static.sumsub.com and *.sumsub.com to your Content-Security-Policy frame-src, script-src, and connect-src directives. The WebSDK loads sub-resources from multiple Sumsub CDN endpoints.
AML Screen Returns Empty items Array
Cause: AML module not enabled on the tier, or applicant profile is missing required name fields. Fix: Confirm AML is enabled in Dashboard → Settings → Modules. Ensure the applicant’s info.firstName and info.lastName are populated before calling the AML endpoint.
Token Expiry During WebSDK Session
Cause: Player spends more than 30 minutes in the KYC widget (common when using the ID card flow with back-of-card re-capture). Fix: The init(token, refreshTokenCallback) second parameter handles this – implement fetchNewToken() to return a promise resolving to a fresh token. Without this, the widget shows a generic error and loses the player’s uploaded documents.
Duplicate Applicant Error on Re-Registration
Cause: Player deleted their account and re-registered with the same email but a new internal user ID, creating a duplicate face match in Sumsub’s deduplication system. Fix: Before creating a new applicant, call getApplicantByExternalId with any known previous user IDs from your soft-delete records. Reuse the existing Sumsub applicant ID where possible.
Rate Limit 429 During Promotional Campaigns
Cause: Large welcome bonus campaigns can drive registration spikes of 5,000+ players/hour. Fix: Implement a token bucket or leaky bucket rate limiter in front of Sumsub API calls. Queue applicant creation requests and process them at ≤8 per second. Contact Sumsub 2 weeks before major campaigns to request a temporary rate limit increase – they accommodate this with 5 business days notice.
Advanced Tips for Production iGaming Deployments
Reusable KYC – Link Verified Players Across Your Brand Portfolio
Sumsub’s Reusable KYC feature allows a player who verified on one of your brands (e.g., a casino) to complete KYC on a second brand (e.g., a sportsbook) in under 30 seconds by re-using their existing verification. The player consents to data sharing, and Sumsub issues a cross-applicant link. This is supported via the /resources/applicants/{applicantId}/link endpoint and requires that both properties use the same Sumsub organization account. Operators with multi-brand portfolios report 40–60% reduction in full KYC completions after enabling this feature.
Device Intelligence and Behavioural Signals
Sumsub’s 2026 fraud stack includes a device fingerprinting layer that fires silently alongside the WebSDK session. It collects VPN/proxy signals, device reputation scores, and accelerometer data during liveness checks. No additional integration is required – enable it in Dashboard → Settings → Device Intelligence. High-risk device scores are surfaced in the applicant review interface and in the deviceRiskScore field of the applicantReviewed webhook payload.
Dynamic Risk-Based KYC Levels
Rather than routing all players through the same igaming-standard level, implement a tiered approach: low-risk players (UK IP, known device, small first deposit) route to a streamlined level requiring only ID + selfie; high-risk signals (VPN, large first deposit, high-risk jurisdiction) route to an enhanced level adding proof-of-address and source-of-funds documentation. This reduces friction for 80%+ of your legitimate players while concentrating enhanced checks where fraud risk is highest.
Compliance Audit Log Export
Sumsub provides a webhook audit log and a batch export API (/resources/applicants/_export) that outputs a GDPR-compliant CSV of all applicants, their verification decisions, timestamps, and reject labels. Schedule a weekly export and archive it in your compliance document management system. Both MGA and UKGC can request this data during audits with as little as 72 hours notice.
What Competitors Don’t Cover: Beyond Standard Integration Guides
Most Sumsub integration guides – including those from SDK.finance and EvenBet Gaming – cover the happy path: create applicant, show SDK, receive GREEN webhook. They typically omit four areas that production iGaming operators consistently encounter:
- Multi-brand reusable KYC: No third-party guide explains how to link applicants across brand properties within the same Sumsub organization, saving significant per-verification cost.
- AML timing compliance: Generic guides trigger AML at registration. Regulators require risk-based timing – at financial activity, not registration.
- Webhook reliability engineering: No guide addresses the 4-hour retry window or explains why returning errors from webhooks triggers retries that cause duplicate processing. The idempotency pattern (deduplicate on applicant ID + event type + modifiedAt timestamp) is never mentioned.
- Rate limit strategy for campaigns: Standard guides treat rate limits as error conditions to handle. Production operators need proactive queue management and advance Sumsub notification for campaign spikes.
- Re-KYC flows for regulatory refresh cycles: UK’s 3-year re-verification requirement and the mechanics of creating successor applicants while preserving compliance history are absent from every competitor guide reviewed.
The 2026 RegTech M&A consolidation in iGaming KYC is also relevant context – several Sumsub competitors (Onfido via Entrust, $650M deal) are mid-integration, meaning their API stability in 2026 is uncertain. Sumsub’s standalone positioning makes it relatively lower risk from a vendor continuity standpoint.
Frequently Asked Questions
How long does Sumsub KYC take for iGaming players?
Automated processing of standard passports and national IDs from G20 countries takes under 60 seconds on the Growth and Enterprise tiers. Driver’s licenses with multiple sides take 2–4 minutes. Documents requiring manual review (low image quality, unusual documents) typically resolve within 2–24 hours depending on your support tier and the reviewer queue. Sandbox processing is intentionally near-instant for all test documents.
Does Sumsub meet UKGC and MGA requirements?
Yes. Sumsub’s iGaming platform is designed for MGA, UKGC, GGL (Germany), AGCO (Ontario), and multiple other regulated markets. Their legal team maintains jurisdiction-specific compliance documentation for operators. Request the compliance dossier for your specific licence jurisdiction from your Sumsub account manager before submitting your licence application.
What is the difference between the App Token and the SDK token?
The App Token (sbx:/prd: prefix) is your server-side credential used to sign all API requests via HMAC. It is long-lived and must never be exposed to clients. The SDK token is a short-lived (30-minute), applicant-scoped token generated by your server and passed to the frontend to initialize the WebSDK widget. If your App Token leaks, rotate it immediately in the dashboard.
Can I use Sumsub with a mobile app (iOS/Android) instead of a web browser?
Yes. Sumsub provides native iOS and Android SDKs alongside the WebSDK. The same server-side token generation endpoint (/resources/accessTokens) works for all SDK variants. The mobile SDKs offer superior camera control for document capture compared to browser-based MediaStream and are recommended for operator apps expecting significant mobile traffic.
How does Sumsub handle GDPR data deletion requests?
Use the DELETE endpoint /resources/applicants/{applicantId} to permanently delete all applicant data including documents, images, and extracted PII. Note that deletion is irreversible and subject to your jurisdiction’s mandatory retention periods – do not delete data that you are legally required to retain. Sumsub logs the deletion event for audit purposes.
What happens if a player disputes a KYC rejection?
The rejectLabels array in the webhook payload contains machine-readable rejection reasons (e.g., DOCUMENT_EXPIRED, INCONSISTENT_PROFILE, SELFIE_MISMATCH). Your compliance team can review the full decision including reviewer notes via the Sumsub dashboard. Players can be invited to re-submit by creating a new verification attempt on the same applicant. Do not create a new applicant for dispute re-submissions – this breaks deduplication.
Is there a Sumsub sandbox that does not require payment?
Yes. The sandbox environment is free and available to all registered accounts with no verification count limits. Sandbox App Tokens start with sbx: and route to the same API endpoints as production. All webhook events, test applicants, and AML simulations work in sandbox. Production credentials are gated behind Sumsub’s compliance review process for iGaming operators.
How do I handle countries where Sumsub cannot verify identity documents?
As of Tech Insider’s July 2026 review, Sumsub supports more than 14,000 document types across 220+ countries. For the rare jurisdictions where automated verification is unavailable, configure a manual review escalation path in the verification level settings. Set autoCheckEnabled: false for those country codes, which routes submissions directly to Sumsub’s manual review team. Response times for manual reviews are 4–24 hours during business hours. Document your escalation procedure in your compliance manual for regulator review.







