KYC tracking
UCRM supports four KYC ownership models — pick once per tenant, override per-player when needed.
Models
kyc_model | Who initiates | Who decides | Webhooks accepted |
|---|---|---|---|
platform_owns | Casino platform | Platform | Yes — kyc.* lifecycle webhooks update CRM state |
crm_owns | CRM admin (POST /v1/players/:id/kyc/request) | CRM admin (POST .../approve) | No — UCRM rejects inbound kyc.* webhooks (403) |
dual | Either side | Either side (split by KYC level — basic via platform, source-of-funds via CRM) | Yes |
none | Nobody (test envs only) | — | No |
State machine
none → pending → verified
→ rejected
verified → expired (TTL refresh)
verified → pending (level upgrade)
rejected → pending (resubmit)
expired → pending (re-verify)
Forward-only. No verified → rejected (admin uses POST .../revoke which transitions to expired).
Levels
Operator-defined integer 0..10:
0— no KYC1— basic ID (passport / driver's license)2— address verification (utility bill)3— source of funds4+— operator-specific (enhanced due diligence, etc.)
Storage
players.kyc_status, players.kyc_level, players.kyc_history (append-only JSONB), players.kyc_last_event_at, players.kyc_expires_at (TTL for verified KYC).
Admin actions
POST /v1/players/:id/kyc/request — only available when tenant.kyc_model in {crm_owns, dual} AND adapter supports kyc.initiate. Transitions none → pending.
POST /v1/players/:id/kyc/approve — only available when tenant.kyc_model in {crm_owns, dual} AND adapter supports kyc.update. Transitions pending → verified. Optional expires_at for TTL.
POST /v1/players/:id/kyc/revoke — always available (admin override). Transitions current → expired.
Webhook events
| Event | from → to |
|---|---|
kyc.submitted | none/rejected/expired → pending |
kyc.approved | pending → verified |
kyc.rejected | pending → rejected |
kyc.expired | verified → expired |
kyc.level_upgraded | verified → pending (new higher level submission) |
History entry shape:
{
"ts": "2026-05-08T12:00:00Z",
"from": "pending",
"to": "verified",
"level": 1,
"actor_kind": "platform" | "crm" | "admin",
"actor_id": "user_abc",
"reason": "documents accepted",
"data": { "provider_ref": "..." }
}
Capability mismatch
If your tenant picks crm_owns but the adapter doesn't support kyc.update, the admin UI displays a warning + disables the Approve button. Operators see the trade-off + can either change the adapter or switch to platform_owns.