KYC | AML
Player identity verification and suspicious financial activity monitoring
Purpose
Identity verification (KYC)
Confirm the player is who they say they are before allowing financial activity above thresholds. Required by gambling regulation in every licensed market.
Age verification
Confirm player is 18+ (or legal gambling age in their jurisdiction) before first deposit. Non-negotiable.
Sanctions screening
Screen player name and date of birth against OFAC, EU, and UN sanctions lists at registration and periodically. Synchronous block on hit.
AML transaction monitoring
Detect suspicious patterns: rapid deposit--withdrawal cycles, structuring, large transactions. Flag for compliance review -- almost never auto-block.
Regulatory reporting
File mandatory Currency Transaction Reports (CTR) for large transactions and Suspicious Activity Reports (SAR) when warranted. Jurisdiction-specific.
PEP screening
Identify Politically Exposed Persons for enhanced due diligence. PEP status alone does not block -- triggers Level 3 KYC requirement.
Where it sits and how information flows
Position in the stack
Invocation flow
Input data and sources
user_id, amount, direction, cumulative deposit total
from Limits & Rules engine
Current KYC level, document status, verification date
user_kyc_profiles table (Redis cache TTL 60s)
Brand KYC thresholds: anonymous cap, L1 cap, EDD threshold
brand_limits table (cached)
Transaction history: deposit totals, methods used, cycle patterns
transactions table (aggregate, Redis)
Sanctions + PEP screening result
External provider (ComplyAdvantage / WorldCheck) — cached per user
Required at launch
KYC levels
Requirements
- None — player just registered
Allows
Blocks
Requirements
- Email confirmed
- Phone number confirmed
- Date of birth provided + age 18+ confirmed
- Country of residence declared
Allows
Blocks
Requirements
- Government-issued ID (passport / national ID / driver's licence)
- Proof of address (utility bill or bank statement, max 3 months old)
- Selfie / liveness check (optional for Phase 3, recommended)
Allows
Blocks
Requirements
- All Level 2 documents
- Source of funds declaration
- Source of wealth evidence (bank statement, payslips, tax return)
- Manual review by compliance officer
Allows
Blocks
AML rules
| Trigger | Action | Blocks? |
|---|---|---|
Single transaction ≥ CTR threshold (e.g. $10 000) Value set by regulator, not configurable. Transaction proceeds normally. | File Currency Transaction Report (CTR) — mandatory | No (monitor) |
Rapid deposit--withdrawal cycle (deposit then withdraw > 80% within 24h) Classic layering pattern — funds enter and exit quickly without gameplay | Flag for AML queue + notify compliance officer | No (monitor) |
Structuring: multiple deposits just below CTR threshold in short window Intentional breaking of large amounts into smaller ones to avoid reporting | Flag for AML queue + escalate to compliance | No (monitor) |
Sanctions hit: player name / DOB matches OFAC, EU, UN list This is the only AML check that synchronously blocks a transaction | INSTANT BLOCK — freeze account, notify compliance immediately | Yes (instant) |
PEP (Politically Exposed Person) detected PEP status alone is not a reason to block; requires enhanced due diligence | Flag for enhanced review — do not auto-block | No (monitor) |
Deposits from 3+ different payment methods in 7 days | Flag for review — potential money mule or account sharing | No (monitor) |
Error codes
| Code | HTTP | Description | Player message |
|---|---|---|---|
KYC_REQUIRED | 422 | Operation requires higher KYC level than user has | "Verify your identity to continue" |
KYC_DOCUMENTS_PENDING | 422 | User submitted documents but review is in progress | "Your documents are under review (up to 24h)" |
KYC_DOCUMENTS_REJECTED | 422 | Submitted documents were rejected — resubmission needed | "Documents rejected — please re-submit" |
SANCTIONS_HIT | 403 | Player matches a sanctions list — account frozen | "Your account has been restricted. Contact support." |
EDD_REQUIRED | 422 | Transaction triggers Enhanced Due Diligence threshold | "Please provide source of funds documentation" |
{
"error": "KYC_REQUIRED",
"message": "Identity verification is required to continue.",
"required_level": 2,
"current_level": 1,
"kyc_redirect_url": "/kyc/verify?return_to=/deposit"
}Minimum DB schema for Phase 3
-- Current KYC status per user per brand
user_kyc_profiles (
user_id, brand_id,
kyc_level, -- 0 | 1 | 2 | 3
verified_at,
provider, -- 'manual' | 'onfido' | 'sumsub'
sanctions_status, -- 'clear' | 'hit' | 'pending'
sanctions_checked_at,
pep_status -- 'clear' | 'hit' | 'pending'
)
-- Uploaded verification documents
kyc_documents (
id, user_id, brand_id,
type, -- 'passport' | 'id_card' | 'driving_licence'
-- 'proof_of_address' | 'source_of_funds' | 'selfie'
status, -- 'pending' | 'approved' | 'rejected'
provider_ref,
rejection_reason,
created_at, reviewed_at
)
-- Append-only AML flags
aml_flags (
id, user_id, brand_id, transaction_id,
flag_type, -- 'ctr' | 'rapid_cycle' | 'structuring'
-- 'sanctions_hit' | 'pep' | 'multi_method'
status, -- 'open' | 'reviewed' | 'escalated' | 'closed'
reviewed_by, reviewed_at,
created_at
)Best practices
KYC vs AML -- keep them separate
- KYC = synchronous gate: "is this user verified enough for this operation?" Always blocks if not.
- AML = async monitoring: "does this transaction look suspicious?" Almost never blocks synchronously, except sanctions.
- Do not conflate the two -- mixing them leads to over-blocking legitimate transactions or under-flagging suspicious ones.
Phase 3 scope -- what to build now
- Manual document review is acceptable for launch -- no need for automated provider (Onfido, Sumsub) in Phase 3.
- Build the KYC level model and gates now; swap in automated provider later without changing the gate logic.
- AML for Phase 3: sanctions screening (blocking) + basic transaction pattern flags (non-blocking). Full monitoring rules in Phase 6.
Data, privacy, compliance
- KYC documents: encrypted at rest, access-logged, retention policy per jurisdiction (typically 5--7 years after account closure).
- AML flags must never be visible to the player -- they go to an internal compliance queue only.
- GDPR: player can request data deletion, but AML/KYC records are exempt from erasure (legal obligation overrides right to erasure).
- Sanctions re-screening: run at registration AND on a scheduled basis (e.g. weekly batch) -- lists change over time.
Performance
- Cache KYC level per user in Redis (TTL 60s) -- it is read on every payment request.
- Cache sanctions screening result per user (TTL 24h) -- external API calls are slow and expensive.
- AML pattern checks run asynchronously via a job queue after the transaction is committed -- never on the hot path.
- KYC status webhook from provider should update DB and invalidate Redis cache immediately.