Onfido SDK Casino Integration: 12 Steps to Live KYC [2026]

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

What This Guide Covers (and What Competitors Miss)

Most tutorials on casino identity verification stop at “call the API, show a widget, handle a webhook.” That covers maybe 30 percent of a real production integration. What they skip – Onfido Studio workflow JSON, Atlas AI decisioning thresholds, AML watchlist tiering, dynamic feature module bundling, and the exact error codes that blow up mobile onboarding – is what this guide is built around.

As of June 2026, Onfido is an Entrust Company following its acquisition, and the product stack has been consolidated into four pillars: Onfido Smart Capture (frontend SDK), Onfido Studio (no-code workflow orchestration), Onfido Atlas (AI decisioning engine), and Onfido Verification Suite (the full backend check layer). Each has specific configuration hooks that matter enormously for a regulated casino environment.

The benchmark you are building toward: 95% of KYC checks completed in under 10 seconds, with a 54% improvement in fraud accuracy year over year, and a 90%+ first-time pass rate via Smart Capture – all figures from Onfido’s 2025 Real Identity Platform announcement. Soft2Bet, one of Europe’s larger B2B casino operators, publicly cited this stack as the reason it moved Onfido to its primary identity layer in 2025.

This is a 12-step implementation guide. You will leave with working Node.js backend code, a React frontend integration, a production webhook handler, an Onfido Studio workflow configuration, and an AML screening setup that satisfies UKGC and MGA requirements.

Prerequisites: Versions, Dependencies, and Accounts

Before touching a line of code, confirm every dependency is at the correct version. Mismatched SDK versions are the single most common cause of “it works in sandbox but not production” failures.

Required Accounts

  • Onfido account with API v3.6+ access – request sandbox credentials at onfido.com; production keys require a signed DPA (Data Processing Agreement) and AML questionnaire
  • Stripe or PSP account for the player wallet (you will link Onfido checks to payment unlock logic)
  • Gambling regulator sandbox – UKGC’s test environment or MGA’s Sandbox Portal for end-to-end compliance testing

SDK and Runtime Versions

Component Version (June 2026) Notes
onfido-node (official SDK) v7.x Node.js 18+ required
onfido-sdk-ui (React component) v14.x React 18 compatible
Onfido Android SDK v20.x minSdkVersion 21+, dynamic feature modules supported
Onfido iOS SDK v29.x iOS 13+, Swift Package Manager or CocoaPods
Onfido REST API v3.6 Base URL: https://api.eu.onfido.com/v3.6/ (EU) or https://api.us.onfido.com/v3.6/ (US)
Node.js 18.x LTS or 20.x ES modules supported
React 18.x 18.3+ for concurrent rendering hooks

Regulatory Context You Must Read First

UK Gambling Commission requires operators to verify customer identity before allowing a deposit or a promotional claim. The UKGC Customer Due Diligence guidance (updated March 2026) now mandates a risk-based approach where higher-deposit players trigger enhanced due diligence (EDD) automatically. MGA’s MGA Common Regulatory Framework similarly requires AML screening against PEP and sanctions lists at registration, not just at threshold events. FATF Recommendation 10 underpins both – see the FATF guidance on customer due diligence for the international baseline.

Onfido covers 2,500+ ID document types across 195 countries. That makes it viable for multi-jurisdiction casino deployments, but you still need to configure which jurisdictions your Onfido Studio workflow accepts – not all coverage is equal for regulated gambling markets.

Step 1: Create Applicant and Generate SDK Token

Every Onfido flow begins with an Applicant object. This is a server-side resource you create before the player touches the UI. The applicant ID links the SDK session to your backend check request and to your own player record.

Install the Backend SDK

npm install @onfido/api@7 dotenv express

Create an Applicant (Node.js)

// server/onfido/createApplicant.js
import { Onfido, Region } from "@onfido/api";
import dotenv from "dotenv";
dotenv.config();

const onfido = new Onfido({
  apiToken: process.env.ONFIDO_API_TOKEN,
  region: Region.EU, // Use Region.US for US-licensed casinos
});

/**
 * Creates an Onfido applicant tied to a casino player record.
 * Call this at registration before loading the KYC widget.
 */
export async function createApplicant(player) {
  const applicant = await onfido.applicant.create({
    firstName: player.firstName,
    lastName: player.lastName,
    email: player.email,
    dob: player.dateOfBirth, // ISO 8601: "1990-05-14"
    address: {
      street: player.addressStreet,
      town: player.addressTown,
      postcode: player.postcode,
      country: player.country, // ISO 3-letter: "GBR"
    },
  });

  // Persist applicantId in your player table – you need it for every check
  await db.query(
    "UPDATE players SET onfido_applicant_id = $1 WHERE player_id = $2",
    [applicant.id, player.id]
  );

  return applicant.id;
}

Generate an SDK Token

The SDK token is a short-lived (90 minutes) JWT that authorizes the frontend SDK to upload documents directly to Onfido’s infrastructure. Your server generates it and passes it to the React component.

// server/onfido/generateSdkToken.js
export async function generateSdkToken(applicantId, referrer) {
  const tokenResponse = await onfido.sdkToken.generate({
    applicantId: applicantId,
    referrer: referrer, // e.g. "https://casino.example.com/*"
  });

  return tokenResponse.token; // Pass this to the client – never log it
}

Expected output: A JWT string starting with eyJ. If you get a 403, your API token lacks sdkToken.write scope – regenerate it in the Onfido Dashboard with the correct permission set.

Step 2: Configure Onfido Studio Workflow

Onfido Studio replaces the old manual check-type selection. Instead of hard-coding which checks to run, you define a workflow in Studio’s no-code editor and reference the workflow ID in your API call. For a casino, you will want at minimum two workflows: a standard onboarding flow and an enhanced due diligence (EDD) flow for high-value players.

Standard Casino Onboarding Workflow

In the Onfido Dashboard → Studio → Workflows, create a new workflow with these nodes in sequence:

  1. Document Capture – accept passport, national ID, driving licence; require front + back for non-passport documents
  2. Facial Similarity (Biometric) – liveness check with motion challenge; do not use static photo match for gambling due to replay attack risk
  3. Data Comparison – cross-reference extracted document data against the applicant record you created in Step 1
  4. Watchlist AML – configure for sanctions, PEP, and adverse media (see Step 7)
  5. Decision – Atlas AI auto-approves if all signals green; flags if any signal amber; rejects if any signal red

Export the Workflow ID

After saving, Studio gives you a workflow ID like wf_xxxxxxxxxxxxxxxx. Store it in your environment:

ONFIDO_STANDARD_WORKFLOW_ID=wf_xxxxxxxxxxxxxxxx
ONFIDO_EDD_WORKFLOW_ID=wf_yyyyyyyyyyyyyyyy

Trigger the Workflow Run via API

// server/onfido/startWorkflow.js
export async function startWorkflowRun(applicantId, workflowId) {
  const run = await onfido.workflowRun.create({
    applicantId: applicantId,
    workflowId: workflowId,
    // Custom data passed into the workflow for rule evaluation
    customData: {
      casinoPlayerId: applicantId, // Your internal reference
      onboardingTier: "standard",
    },
  });

  return run.id; // workflowRunId – used in webhook payloads
}

For players who deposit over £2,000 in the first 24 hours (a common EDD trigger under UKGC’s enhanced monitoring guidance), call this function again with ONFIDO_EDD_WORKFLOW_ID instead. Onfido supports multiple workflow runs per applicant, so you do not need to create a new applicant – just start a second run.

Step 3: Initialize Onfido Smart Capture on the Frontend

Smart Capture is the React component (also available as a vanilla JS bundle) that handles document upload, camera access, image quality validation, and NFC chip reading. The key integration point is that it uploads directly to Onfido – your server never handles raw document images, which keeps you out of the PCI/PII storage scope.

Install the Frontend SDK

npm install onfido-sdk-ui@14

React Integration

// components/KycVerification.jsx
import { useEffect, useRef, useState } from "react";
import { init } from "onfido-sdk-ui";

export default function KycVerification({ playerId }) {
  const onfidoRef = useRef(null);
  const [status, setStatus] = useState("idle"); // idle | loading | complete | error

  useEffect(() => {
    let onfidoInstance = null;

    async function startKyc() {
      setStatus("loading");

      // Fetch SDK token from your server – never expose your API token to the client
      const res = await fetch("/api/onfido/sdk-token", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ playerId }),
      });
      const { sdkToken } = await res.json();

      onfidoInstance = init({
        token: sdkToken,
        containerId: "onfido-mount",
        // Use workflowRunId if triggering a Studio workflow
        // workflowRunId: "...", // for Onfido Studio flows
        steps: [
          {
            type: "welcome",
            options: { title: "Verify your identity to start playing" },
          },
          {
            type: "document",
            options: {
              documentTypes: {
                passport: true,
                driving_licence: { sides: { front: true, back: true } },
                national_identity_card: { sides: { front: true, back: true } },
              },
              // Enable NFC for passports on supported devices
              useWebcam: true,
              useLiveDocumentCapture: true,
            },
          },
          { type: "face", options: { requestedVariant: "video" } },
          { type: "complete" },
        ],
        onComplete: async (data) => {
          setStatus("complete");
          // Trigger backend check after SDK upload completes
          await fetch("/api/onfido/trigger-check", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ playerId }),
          });
        },
        onError: (error) => {
          console.error("Onfido SDK error:", error);
          setStatus("error");
        },
        onUserExit: (userExitCode) => {
          // USER_CONSENT_DENIED, USER_CANCEL, etc.
          setStatus("idle");
        },
      });
    }

    startKyc();

    return () => {
      if (onfidoInstance) onfidoInstance.tearDown();
    };
  }, [playerId]);

  return (
    <div>
      {status === "loading" && <p>Loading identity verification...</p>}
      {status === "complete" && <p>Verification submitted. You will receive a decision shortly.</p>}
      {status === "error" && <p>Something went wrong. Please refresh and try again.</p>}
      <div id="onfido-mount" />
    </div>
  );
}

Expected output: A full-screen modal-style capture flow. Smart Capture’s intelligent feedback system detects blurry images, poor lighting, and cropped documents in real time, providing inline guidance that drives the 90%+ first-time pass rate. Players do not submit until the image quality bar is met.

Step 4: Backend Check Request (Non-Studio Path)

If you are not yet using Onfido Studio workflows – or need to trigger checks programmatically outside of a Studio run – use the Checks API directly. This is also the pattern for batch or back-office EDD triggers.

// server/onfido/createCheck.js
export async function createCheck(applicantId, checkType = "standard") {
  const reportNames =
    checkType === "edd"
      ? [
          "document",
          "facial_similarity_video",
          "watchlist_aml",
          "right_to_work",
        ]
      : ["document", "facial_similarity_video", "watchlist_aml"];

  const check = await onfido.check.create({
    applicantId: applicantId,
    reportNames: reportNames,
    // Async webhook delivery – do not poll; use webhooks (Step 8)
    asynchronous: true,
    // Suppress sending result emails to applicant (handle in your casino UI)
    suppressFormEmails: true,
  });

  await db.query(
    "INSERT INTO kyc_checks (player_id, onfido_check_id, status) VALUES ($1, $2, $3)",
    [applicantId, check.id, "pending"]
  );

  return check.id;
}

For operations that need real-time KYC decisions – for example, blocking a deposit button until the check resolves – the 95% sub-10-second completion benchmark means you can use short-poll (every 2 seconds, max 5 attempts) before falling back to the webhook path. For the remaining 5% of complex cases, the webhook always delivers the final result.

If you are comparing Onfido to alternatives for your casino stack, the iGaming KYC vendor comparison covering Sumsub, Onfido, Jumio, and Veriff breaks down per-check pricing from $0.80 to $3.80 and verification accuracy by document type.

Step 5: AML Watchlist Screening Configuration

This is the step most tutorials skip entirely. The Watchlist AML report within Onfido covers four distinct data sources, and you need to configure which ones your compliance team requires. Getting this wrong produces either false positives that block legitimate players or missed hits that expose you to regulatory fines.

AML Report Types and Their Purpose

Watchlist Category What It Screens Required By False Positive Risk
Sanctions OFAC, EU, UN, HMT consolidated lists UKGC, MGA, all EU jurisdictions Low (narrow matching)
PEP (Politically Exposed Person) Global government officials, state-owned entity executives UKGC, MGA, AMLD6 Medium – common names match
Adverse Media Financial crime news from 8,000+ sources Risk-based approach (recommended) High without fuzzy-match tuning
Monitored Lists Law enforcement watch lists, Interpol notices Jurisdiction-specific Low

Triggering Watchlist Check via API

// server/onfido/amlScreening.js
export async function runAmlScreening(applicantId) {
  // Standalone watchlist report – can run independently of document check
  const report = await onfido.report.create({
    checkId: existingCheckId, // or create a new check with watchlist_aml only
    name: "watchlist_aml",
    options: {
      // PEP and sanctions are always on; opt-in to adverse media
      watchlistAml: {
        sanctionsList: true,
        pepList: true,
        adverseMedia: true, // Enable if your compliance team requires it
        monitoredList: true,
      },
    },
  });

  return report;
}

Ongoing Monitoring (Continuous AML)

UKGC and MGA both require ongoing monitoring – not just a one-time check at registration. Onfido’s Continuous Monitoring feature re-runs watchlist checks daily against your existing applicant pool and triggers a webhook if a previously clean player appears on a new sanctions list or adverse media source. Enable it per applicant:

// Enable ongoing watchlist monitoring for a player
await onfido.applicant.update(applicantId, {
  // Ensure data stays accurate for re-screening
  consentToMonitoring: true,
});

Store a flag in your player table (aml_monitoring_active = true) so your ops team knows which players are enrolled in continuous monitoring and can review alerts through the Onfido Dashboard or via webhook.

Step 6: Implement the Webhook Handler

Onfido delivers check and report results asynchronously via webhook. Your handler must verify the signature (HMAC SHA-256), parse the event type, update your database, and trigger the appropriate casino action – unlock the deposit button, escalate to a compliance officer, or restrict the account.

// server/routes/onfidoWebhook.js
import crypto from "crypto";
import express from "express";

const router = express.Router();

// Use raw body for signature verification – do NOT use express.json() before this
router.post(
  "/webhook/onfido",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const signature = req.headers["x-sha2-signature"];
    const rawBody = req.body;

    // Verify HMAC SHA-256 signature
    const expectedSig = crypto
      .createHmac("sha256", process.env.ONFIDO_WEBHOOK_SECRET)
      .update(rawBody)
      .digest("hex");

    if (signature !== expectedSig) {
      console.warn("Onfido webhook signature mismatch – possible replay attack");
      return res.status(401).json({ error: "Invalid signature" });
    }

    const event = JSON.parse(rawBody.toString());

    switch (event.payload.action) {
      case "check.completed": {
        const checkId = event.payload.object.id;
        const result = event.payload.object.result; // "clear", "consider", or "unidentified"

        await handleCheckCompleted(checkId, result);
        break;
      }

      case "report.completed": {
        const reportId = event.payload.object.id;
        const reportName = event.payload.object.name;
        const reportResult = event.payload.object.result;

        // Handle individual report results (useful for granular AML vs document decisions)
        await handleReportCompleted(reportId, reportName, reportResult);
        break;
      }

      case "workflow_run.completed": {
        const workflowRunId = event.payload.object.id;
        const output = event.payload.object.output; // Studio decision output

        await handleWorkflowComplete(workflowRunId, output);
        break;
      }

      default:
        console.log(`Unhandled Onfido event: ${event.payload.action}`);
    }

    res.status(200).json({ received: true });
  }
);

async function handleCheckCompleted(checkId, result) {
  const player = await db.query(
    "SELECT player_id FROM kyc_checks WHERE onfido_check_id = $1",
    [checkId]
  );

  if (result === "clear") {
    await db.query(
      "UPDATE players SET kyc_status = 'approved', deposit_enabled = true WHERE player_id = $1",
      [player.rows[0].player_id]
    );
    // Send welcome email, enable deposit UI
  } else if (result === "consider") {
    await db.query(
      "UPDATE players SET kyc_status = 'manual_review' WHERE player_id = $1",
      [player.rows[0].player_id]
    );
    // Create compliance task for manual review
    await createComplianceTask(player.rows[0].player_id, checkId);
  } else {
    await db.query(
      "UPDATE players SET kyc_status = 'rejected', deposit_enabled = false WHERE player_id = $1",
      [player.rows[0].player_id]
    );
  }
}

export default router;

Expected output: A 200 response within 200ms. Onfido retries webhooks with exponential backoff for up to 24 hours on non-2xx responses, so idempotency is critical – check if you have already processed a given checkId before running database writes.

Step 7: Age Verification and Right to Work

Under UKGC Social Responsibility Code Provision 3.2.11, operators must prevent underage gambling. Onfido’s document check extracts the date of birth from the MRZ (Machine Readable Zone), barcode, or OCR, and the Data Comparison report cross-references it against the applicant DOB you submitted in Step 1. The system flags mismatches automatically. For broader market context, see our coverage of best sweepstakes casino sites.

Enforce Age Gate in Business Logic

// server/onfido/ageVerification.js
export async function enforceAgeGate(playerId) {
  const player = await db.query(
    "SELECT dob, kyc_status, onfido_applicant_id FROM players WHERE player_id = $1",
    [playerId]
  );

  const { dob, kyc_status, onfido_applicant_id } = player.rows[0];

  if (kyc_status !== "approved") {
    throw new Error("KYC not yet approved");
  }

  const age = calculateAge(dob); // Your own utility function

  if (age < 18) {
    // Immediately freeze account; log for UKGC reporting
    await db.query(
      "UPDATE players SET account_status = 'frozen', freeze_reason = 'underage' WHERE player_id = $1",
      [playerId]
    );
    await reportUnderageAttempt(playerId, onfido_applicant_id);
    throw new Error("Underage player detected");
  }

  return { verified: true, age };
}

Right to Work reports (for markets that require proof of eligibility to gamble, such as proof of UK residency) can be added to the check reportNames array as "right_to_work". This is separate from the document verification check and often missed in standard integrations.

Step 8: Atlas AI Decisioning and Confidence Thresholds

Onfido Atlas is the AI layer that assigns confidence scores to each document and biometric signal. In the Onfido Dashboard → Settings → Atlas Decisioning, you can configure the thresholds at which Atlas auto-approves vs. flags for manual review. For regulated gambling, you want a tighter threshold than fintech.

Recommended Casino Threshold Configuration

Signal Recommended Auto-Approve Threshold Manual Review Trigger Auto-Reject Trigger
Document authenticity score >= 0.90 0.70–0.89 < 0.70
Facial similarity score >= 0.85 0.70–0.84 < 0.70
Liveness challenge pass Pass N/A Fail
AML watchlist hit No hit PEP hit (requires EDD) Sanctions hit
Data comparison match Full match Partial match (minor typo) DOB mismatch or name mismatch

These thresholds are configurable via the Onfido Dashboard per workflow. Document them in your compliance manual – regulators expect you to be able to justify your risk appetite during an audit. The MGA and UKGC casino API audit requirements for 2026 detail what inspectors specifically look for in decision-making system configuration.

Step 9: Mobile Integration – Android Dynamic Feature Modules

For casino mobile apps, bundle size is a conversion bottleneck. Players who wait 4+ seconds for the app to load the KYC module at registration churn at higher rates. Onfido’s Android SDK (v20.x) supports Android’s Dynamic Feature Modules, letting you defer the ~8MB KYC SDK download until the player actually reaches the verification step.

Gradle Configuration

// In your dynamic feature module's build.gradle
android {
    dynamicFeatures = [":onfido_kyc"]
}

// :onfido_kyc/build.gradle
plugins {
    id 'com.android.dynamic-feature'
}

dependencies {
    implementation project(':app')
    implementation 'com.onfido.sdk.capture:onfido-capture-sdk:20.+'
    // Or lite build for smaller footprint (excludes NFC):
    // implementation 'com.onfido.sdk.capture:onfido-capture-sdk-core:20.+'
}

Request and Install at Runtime

// MainActivity.kt
import com.google.android.play.core.splitinstall.*

class MainActivity : AppCompatActivity() {

    private val splitInstallManager = SplitInstallManagerFactory.create(this)

    fun loadKycModule() {
        val request = SplitInstallRequest.newBuilder()
            .addModule("onfido_kyc")
            .build()

        splitInstallManager.startInstall(request)
            .addOnSuccessListener {
                launchKycFlow()
            }
            .addOnFailureListener { e ->
                showError("KYC module failed to load: ${e.message}")
            }
    }

    private fun launchKycFlow() {
        // Launch Onfido SDK after module installed
        val onfidoConfig = OnfidoConfig.builder(this)
            .withSDKToken(sdkToken)
            .withCustomFlow(
                FlowStep.WELCOME,
                FlowStep.CAPTURE_DOCUMENT,
                FlowStep.CAPTURE_FACE,
                FlowStep.FINAL
            )
            .build()

        Onfido.startActivityForResult(this, onfidoConfig, REQUEST_CODE_ONFIDO)
    }
}

The core SDK without NFC support (onfido-capture-sdk-core) is roughly 30% smaller. Reserve the full SDK for markets where ePassport NFC chip reading is required – currently recommended for UK players with biometric passports, as NFC verification eliminates the possibility of printed photo substitution attacks.

Step 10: Sandbox Testing with Onfido Test Documents

Onfido provides a library of sandbox test documents that return specific outcomes without consuming production API credits. Use these to cover every branch of your webhook handler before going live.

Key Sandbox Document Codes

# Document that returns "clear" on all reports
ONFIDO_SANDBOX_DOC_CLEAR="test_document_01"

# Document that returns "consider" on document authenticity
ONFIDO_SANDBOX_DOC_CONSIDER="test_document_consider_01"

# Document that triggers AML watchlist hit (PEP match)
ONFIDO_SANDBOX_DOC_PEP="test_document_pep_01"

# Document that triggers sanctions hit (auto-reject scenario)
ONFIDO_SANDBOX_DOC_SANCTIONS="test_document_sanctions_01"

# Document with DOB mismatch (data comparison fail)
ONFIDO_SANDBOX_DOC_DOB_MISMATCH="test_document_dob_mismatch_01"

Test each document code with your full flow – Smart Capture submission → backend check creation → webhook receipt → database state → player UI update. The most commonly missed test case is the idempotency path: what happens when the webhook fires twice for the same check ID. Your handler must be safe to call multiple times for the same event.

Webhook Testing with ngrok

# In development: expose your local webhook endpoint
npx ngrok http 3000

# Register the ngrok URL in Onfido Dashboard → Webhooks
# https://xxxx.ngrok.io/webhook/onfido
# Select events: check.completed, report.completed, workflow_run.completed

Step 11: Go-Live Production Checklist

Before switching from sandbox to production API keys, run through this checklist. Each item has caused production incidents for casino operators that skipped it.

Pre-Launch Verification

  • DPA signed – Onfido (as an Entrust Company) acts as a data processor under GDPR Article 28; your DPA must be executed before handling real player PII
  • API key scope audit – production keys should have only the scopes your app needs; never use a master key in application code
  • Webhook secret rotated – do not reuse your sandbox webhook secret in production
  • Rate limit handling – production tier limits vary by contract; implement exponential backoff with jitter on 429 responses
  • EU vs US region – confirm your api.eu.onfido.com vs api.us.onfido.com routing matches your data residency obligations; mixing them exposes you to GDPR Chapter V cross-border transfer issues
  • Atlas thresholds documented – compliance requires your decisioning logic to be auditable; export your Studio workflow version and store it in your compliance document management system
  • GDPR deletion endpoint implemented – Onfido exposes DELETE /v3.6/applicants/{applicant_id}; your Right to Erasure flow must call this and delete your local copy of the Onfido applicant ID
  • Session timeout tested – SDK tokens expire after 90 minutes; test that mobile players who pause mid-flow get a graceful “session expired, please restart” UI rather than a raw 401

Load Testing Targets

Under UKGC responsible gambling requirements, KYC must not become a bottleneck that delays player access to safer gambling tools. For a new casino launch, load test your Onfido integration at 2× expected peak concurrent registrations. Onfido’s SLA at enterprise tier guarantees 99.9% API uptime – ensure your integration queues check requests rather than dropping them on Onfido API degradation.

Step 12: Monitor KYC Conversion and Optimize

KYC drop-off is one of the top three causes of player acquisition waste in iGaming. A conversion rate below 75% at the document capture step signals a UX or document guidance problem. Below 85% at the biometric step suggests lighting or camera permission friction. Onfido’s Analytics dashboard surfaces per-step funnel metrics, but you also want your own instrumentation.

Key Metrics to Track

// server/analytics/kycMetrics.js
export async function recordKycEvent(playerId, event, metadata = {}) {
  await db.query(
    `INSERT INTO kyc_events (player_id, event_type, metadata, created_at)
     VALUES ($1, $2, $3, NOW())`,
    [playerId, event, JSON.stringify(metadata)]
  );
}

// Events to track:
// kyc_started, document_submitted, face_submitted, check_pending,
// check_cleared, check_consider, check_rejected,
// sdk_error, session_timeout, user_exit

For casinos that handle crypto deposits, where player anonymity expectations are high, clear UX copy explaining why verification is required improves conversion by 10–15%. Frame it as “required by our gaming licence” rather than “to prevent fraud” – the former is factual and builds trust. For broader payment processing context within iGaming, the Stripe vs Worldpay vs Nuvei licensed gambling payment processing guide covers the KYC gate requirements that each PSP imposes before enabling player deposits.

5 Critical Pitfalls and How to Avoid Them

Pitfall 1: Using express.json() Before the Webhook Route

If express.json() middleware is applied globally before your Onfido webhook route, it consumes the raw request body. HMAC verification computes against the raw bytes – once parsed to JSON and re-serialized, the signature never matches. Solution: apply express.raw({ type: 'application/json' }) specifically to the webhook route before any JSON middleware, as shown in Step 6. For broader market context, see our coverage of leading online casino sites.

Pitfall 2: Creating a New Applicant on Every KYC Attempt

Players who fail their first document attempt and retry create duplicate applicants, which fragments their check history and breaks your AML audit trail. Always look up the existing Onfido applicant ID from your player table before creating a new one. If the player has no applicant ID yet, create one. Otherwise, start a new check or workflow run against the existing applicant.

Pitfall 3: SDK Token Region Mismatch

Onfido has separate EU and US regions with different API base URLs. If your backend creates applicants at api.eu.onfido.com but generates SDK tokens specifying the US region (or vice versa), the frontend SDK will fail to find the applicant and return a cryptic 404. Set the region once in your Onfido constructor and never override it per-call.

Pitfall 4: Skipping Liveness Video in Favour of Photo Face Match

The facial_similarity_photo report type (static photo comparison) is faster but does not detect printed photo substitution attacks, which are common in bonus-abuse fraud on casino platforms. For gambling, always use facial_similarity_video (liveness with motion challenge). The performance cost is minimal – it adds 2–3 seconds to the user flow – and it is the only option that satisfies UKGC’s anti-impersonation requirements in their October 2025 guidance update.

Pitfall 5: Not Handling the “consider” Result

Many implementations treat Onfido results as binary: “clear” = approve, anything else = reject. The consider result – which fires when Atlas flags something for human review rather than definitively rejecting – should route to your compliance team, not auto-reject the player. Auto-rejecting on consider results will generate disproportionate false negatives and player complaints, and may itself attract regulatory scrutiny if the rejection rate is anomalously high.

8-Item Troubleshooting Guide

1. SDK throws “Invalid token” on first load

Cause: SDK token generated before the applicant record was committed to Onfido’s database (race condition on async applicant creation). Fix: Await the applicant create response, verify you get back an id field, then call generateSdkToken. Add a 200ms delay if you observe this intermittently in high-load scenarios.

2. Webhook never fires in sandbox

Cause: Your ngrok tunnel URL is not registered, or the webhook URL includes a trailing slash that does not match your route. Fix: In Onfido Dashboard → Webhooks, verify the URL exactly matches your route, including protocol. Check the webhook delivery log in the Dashboard – it shows recent delivery attempts and HTTP response codes.

3. Document check returns “unidentified” for valid UK passport

Cause: The document type sent by Smart Capture was not matched against the expected documentTypes allowlist you configured. If you only allow passport: true for the UK but the player uploaded a driving licence, the check returns unidentified. Fix: Expand your documentTypes configuration or provide clearer UI guidance for which documents are accepted.

4. “consider” rate is above 20%

Cause: Atlas confidence thresholds may be misconfigured, or your player acquisition channel is sending low-quality document images (common with screenshot-based submissions from mobile email flows). Fix: Review the breakdown field in the check API response – it shows which specific sub-check triggered the consider. Enable Smart Capture’s enhanced image quality prompts by ensuring useLiveDocumentCapture: true is set.

5. AML watchlist returning too many false positives

Cause: Adverse media screening with fuzzy name matching produces high false positive rates for common names (John Smith, Mohammed Ahmed). Fix: In Atlas Decisioning settings, configure adverse media to require both name AND DOB match before flagging. Alternatively, treat adverse media as a manual review trigger rather than a block, and only auto-reject on hard sanctions hits.

6. iOS SDK crashes on device without camera permission

Cause: NSCameraUsageDescription key missing from your app’s Info.plist. Fix: Add the key with a clear casino-context explanation: “We need camera access to verify your identity as required by our gaming licence.” Without this, iOS terminates the app when the SDK requests camera access.

7. GDPR deletion call returns 400 even though applicant exists

Cause: Applicant has an in-progress check that has not resolved. Onfido does not allow deletion while a check is pending. Fix: Cancel the in-progress check first (DELETE /v3.6/checks/{check_id}), wait for the cancellation to propagate (verify via GET /v3.6/checks/{check_id} returning status withdrawn), then delete the applicant.

8. Studio workflow returns no output after completion

Cause: Your Onfido Studio workflow does not have a Terminal node configured. Studio requires an explicit approved/declined/consider terminal node at the end of each branch; without it, the workflow run completes with null output. Fix: Open the workflow in Studio, verify every branch terminates at an Approved, Declined, or Manual Review terminal node, and re-save.

Advanced Tips: NFC, Proof of Address, and Multi-Jurisdiction Routing

NFC Passport Chip Reading

Onfido Smart Capture supports NFC chip reading from ePassports on Android (NFC-enabled, API 21+) and iOS (iPhone 7+, iOS 13+). Enable it in your SDK configuration with useNfc: true. NFC verification reads the chip directly from the RFID antenna and cryptographically verifies the document-signing certificate against the issuing country’s public key infrastructure. This makes NFC the gold standard for high-value player verification – it eliminates printed document forgery and digital photo-manipulation attacks entirely. For casino VIP players with £10,000+ deposit limits, routing them through NFC verification is an increasingly common EDD choice.

Proof of Address as a Separate Check

Onfido’s document check covers identity documents but not proof of address (bank statements, utility bills). For UK operators under UKGC’s source of funds requirements, you need to collect and verify address documents separately. The Onfido document check accepts document_type: "unknown" for non-standard documents, but you will need your own OCR pipeline or a complementary vendor to extract and verify address data from utility bills.

Multi-Jurisdiction Workflow Routing

If your casino operates under both UKGC and MGA licences (different legal entities, different compliance requirements), use Onfido Studio’s conditional logic to route players to different workflow branches based on their jurisdiction. Pass customData.jurisdiction in the workflow run create call and configure Studio to branch on that value. UK players get the UKGC-required workflow; EU players get the MGA-compliant workflow. This eliminates the need to maintain two separate integrations and two sets of webhook handlers.

What Competitor Guides Don’t Cover

Reviewing the top-ranking content for this topic in June 2026 reveals a consistent pattern: competitor articles describe Onfido’s product features at a marketing level but stop before providing implementation-grade guidance. Specifically, the gaps this article addresses that competitors miss:

  • Onfido Studio workflow configuration – no competitor article explains the JSON structure, node types, or how to export workflow IDs for use in API calls. Most articles still reference the legacy “check type” API that pre-dates Studio.
  • Atlas AI threshold configuration – the specific confidence score thresholds, how to tune them per regulatory environment, and the compliance documentation requirements around them are absent from all competitor content.
  • HMAC webhook signature verification – most competitors either skip webhook security entirely or describe it incorrectly (the raw body requirement with express.raw is consistently missed).
  • Android Dynamic Feature Modules – no competitor article addresses mobile bundle size optimization, despite it being a documented SDK feature that meaningfully affects mobile casino conversion rates.
  • Continuous AML monitoring setup – competitors cover the one-time watchlist check at registration but do not address the ongoing re-screening requirement that both UKGC and MGA impose post-2025.
  • Multi-jurisdiction Studio routing – competitors treat Onfido as a single-market tool; the workflow branching capability for multi-licence operators is not documented anywhere in the competitor set.

Frequently Asked Questions

1. How long does Onfido take to return a KYC decision?

Onfido’s 2025 Real Identity Platform announcement benchmarks 95% of checks completing in under 10 seconds for the automated decision path. The remaining 5% are flagged for manual review and typically resolve within 2–4 business hours. For casino onboarding, design your UX to show a “verification in progress” state and unlock deposit access via webhook rather than polling.

2. What is the difference between Onfido Studio and the classic Checks API?

The classic Checks API requires you to specify document types and report names in code. Onfido Studio lets you build and modify workflows visually in a no-code editor; your code just references a workflow ID and Onfido Studio handles the orchestration. Studio is the recommended path for new integrations as it allows compliance teams to update verification flows without requiring a code deployment.

3. Does Onfido cover age verification automatically?

Yes – the document check extracts date of birth from the document’s MRZ, barcode, or OCR, and the Data Comparison report checks it against the applicant DOB you submitted when creating the applicant. If there is a mismatch, the check returns consider. You still need your own age-gate business logic (see Step 7) to act on that result and freeze underage accounts.

4. Can Onfido handle crypto casino players who prefer not to share ID?

No – Onfido is an identity verification layer, not an anonymity layer. If your casino holds a licence from UKGC, MGA, or any comparable regulator, KYC is mandatory regardless of payment method. Crypto does not exempt players from AML obligations. Unlicensed casinos that skip KYC face regulatory action; licensed ones have no choice in the matter.

5. What does “consider” mean vs. “clear” vs. “unidentified”?

“Clear” means all signals passed Onfido’s automated thresholds and Atlas approved the applicant. “Consider” means one or more signals require human review – common causes include partially legible documents, PEP hits, or borderline biometric scores. “Unidentified” means Onfido could not process the document at all – usually a wrong document type, insufficient image quality, or an unsupported ID format. Your business logic should handle all three states.

6. How much does Onfido cost per check for casino operators?

Onfido (as an Entrust Company post-2025 acquisition) does not publish per-check list pricing publicly. The independently-cited market range for Onfido in iGaming contexts is $0.80–$3.80 per completed verification depending on check type and volume tier. Enterprise casino contracts typically include a minimum monthly commitment in exchange for lower per-check rates. AML watchlist checks may be priced separately from document + biometric checks depending on your contract structure.

7. Is Onfido GDPR-compliant as a data processor?

Onfido acts as a data processor under GDPR Article 28, which means you as the casino operator remain the data controller. You must execute a Data Processing Agreement (DPA) with Onfido before processing player data, ensure your privacy policy discloses Onfido as a sub-processor, and implement the right-to-erasure flow described in Step 11 to delete applicant data from Onfido when a player requests deletion.

8. What Onfido SDK version should I use in June 2026?

For web/React: [email protected]. For Node.js backend: @onfido/[email protected]. For Android: SDK v20.x (Gradle). For iOS: SDK v29.x (Swift Package Manager preferred). Always pin to a minor version in production (@7.2.0 not @7) to avoid breaking changes from patch updates, and subscribe to Onfido’s developer changelog for deprecation notices – the migration from v3.5 to v3.6 API in late 2025 broke several undocumented response field assumptions.

Casey Mitchell

Casey Mitchell

Crypto Casino Editor

Casey Mitchell tracks crypto casinos, on-chain gaming, provably fair RNG, and the regulatory gray zones around US-facing crypto gambling platforms. Reviews wallets, deposit flows, and KYC policies across BTC, ETH, and stablecoin operators.

View all articles