Identify
POST /v1/players/identify — the canonical entry point for telling UCRM "this anonymous_id / external_id is actually a real player with these traits".
Auth: API key (project-scoped).
Request
{
"external_id": "casino_player_001", // optional but strongly recommended
"anonymous_id": "anon_xyz", // optional — for pre-signup tracking
"traits": {
"email": "player@example.com",
"phone": "+15551234567",
"telegram_id": "12345",
"first_name": "Anna",
"last_name": "Novak",
"country": "CZ",
"language": "cs",
"wallet": {
"network": "btc",
"address": "bc1q..."
},
"custom": { ... } // free-form additional traits
}
}
Algorithm (week-5 spec)
- Validate — at least one of
external_id,anonymous_id, or any trait must be present. - Find candidates by priority:
- exact
external_idmatch - exact
emailmatch (case-insensitive) - exact
phonematch (E.164 normalised) - exact
telegram_idmatch - wallet
(network, address)match
- exact
- Resolve:
- Zero candidates +
anonymous_idset → look up the anon-only owner row, promote it (set the new traits + external_id). - Zero candidates otherwise → INSERT new player.
- One candidate → MERGE: COALESCE traits, append
anonymous_id, retire any stranded anon-only row. - Two or more candidates with conflicting traits → keep the oldest, mark others
status='conflict'for admin resolution. Returns the canonical player_id.
- Zero candidates +
Response
{
"data": {
"player_id": "uuid",
"matched_by": "external_id", // | "email" | "phone" | "telegram_id" | "wallet" | "created" | "promoted_anonymous"
"is_new": false, // true when we just INSERTed
"merged_anonymous_ids": ["anon_xyz"]
}
}
Error codes
422 VALIDATION_ERROR— empty traits + no identifiers422 VALIDATION_ERROR—phonedoesn't parse as E.164422 VALIDATION_ERROR—wallet.addressdoesn't match the chain pattern409 IDENTITY_CONFLICT— multi-candidate merge produced ambiguous result; details includecandidate_ids
Idempotency
Calling /identify twice with the same body is safe — the second call returns the same player_id. Use this property to make your SDK's signup flow re-runnable without dedup logic.
Related
- Players API — read/update the resolved row
- Identity merge concept — how RLS keeps merges tenant-isolated