Přeskočit na hlavní obsah

KYC flow

KYC has four ownership models — pick once per tenant; the model dictates which sides the contract supports.

Model: platform_owns

Most common for casinos. The platform handles all of KYC; CRM mirrors state via webhooks.

You implement: webhook senders for the lifecycle below.

You DON'T implement: outbound KYC endpoints (CRM never calls you for KYC).

Webhook events

POST /v1/adapters/<slug>/webhooks/kyc-events

// kyc.submitted
{
"event": "kyc.submitted",
"event_id": "evt-uuid",
"ts": "2026-05-08T...",
"player_external_id": "casino_player_001",
"level": 1,
"data": { "documents": ["passport", "selfie"] }
}

// kyc.approved
{
"event": "kyc.approved",
"level": 2,
"data": { "expires_at": "2027-05-08T..." } // optional TTL
}

// kyc.rejected
{
"event": "kyc.rejected",
"level": 1,
"data": { "reason": "document_expired" }
}

// kyc.expired
{ "event": "kyc.expired", "level": 2, "data": {} }

// kyc.level_upgraded — player started higher-level submission
{ "event": "kyc.level_upgraded", "level": 3, "data": {} }

State transitions enforced server-side: none → pending → {verified, rejected}, verified → expired, etc.

Model: crm_owns

Less common — typical for operators with custom KYC workflows. CRM admin initiates; platform listens for verdicts.

You implement: outbound KYC endpoints.

You DON'T implement: webhook senders. UCRM rejects inbound kyc.* webhooks with 403 for crm_owns tenants.

POST <your-base>/v1/kyc/initiate

{
"tenant_id": "...",
"player_external_id": "casino_player_001",
"level": 1,
"requirements": ["passport", "address_proof"]
}

Response: { ok: true, provider_ref?: "..." } or { ok: false, code, message }.

POST <your-base>/v1/kyc/update

CRM admin approves / rejects on the CRM side; UCRM informs your platform:

{
"tenant_id": "...",
"player_external_id": "casino_player_001",
"status": "verified", // or "rejected"
"level": 1,
"reason": "documents validated"
}

GET <your-base>/v1/kyc/:player_external_id

Reconciliation lookup:

{
"status": "verified",
"level": 2,
"last_updated": "..."
}

Model: dual

Both flows simultaneously — typical for MGA / UK where:

  • Platform does ID verification (level 1, level 2)
  • CRM does affordability checks (level 3)

You implement BOTH the outbound endpoints AND the webhook senders. Each KYC level falls naturally to one side or the other; UCRM doesn't enforce which side handles which level.

Model: none

No KYC tracked at this tenant. UCRM rejects all KYC webhooks AND doesn't expose KYC admin actions. Useful for unregulated test envs only.

State storage

Regardless of model, CRM stores:

players.kyc_status -- none | pending | verified | rejected | expired
players.kyc_level -- 0..10
players.kyc_history -- append-only audit JSONB
players.kyc_last_event_at
players.kyc_expires_at

Admin UI renders the timeline directly off kyc_history.