Contract overview
Three flow categories
1. Event ingestion (you → UCRM)
Every player action becomes an event. Use the SDK or call /v1/events directly:
POST https://api.casinocrm.io/v1/events
Authorization: Bearer ucrm_pk_prod_TJxd_<secret>
Content-Type: application/json
{
"event": "deposit_confirmed",
"external_id": "casino_player_001",
"anonymous_id": "anon_xyz",
"timestamp": "2026-05-08T10:00:00Z",
"properties": {
"amount": 5000,
"currency": "EUR",
"transaction_id": "tx_abc",
"payment_method": "card"
}
}
Returns 202 with ingestion_id. Event lands in ClickHouse + the player is upserted in Postgres within seconds.
For batch ingestion (recommended for high-volume operators):
POST https://api.casinocrm.io/v1/events/batch
Content-Type: application/json
{
"events": [ ... up to 100 events ... ]
}
2. Outbound adapter calls (UCRM → you)
When CRM issues a bonus / cancels a bonus / queries platform status, it calls your adapter endpoint. You must expose:
POST <your-base>/v1/bonuses/grant - issue bonus on platform
POST <your-base>/v1/bonuses/cancel - cancel active bonus
GET <your-base>/v1/bonuses/:id - status query (reconciliation)
Auth: HMAC-SHA256 signed by UCRM, shared secret known to both ends. See Security.
3. Webhook callbacks (you → UCRM)
After your platform credits / wagers / completes a bonus, fire webhooks:
POST https://api.casinocrm.io/v1/adapters/acme-prod/webhooks/bonus-events
X-UCRM-Signature: <hex-hmac-sha256>
X-UCRM-Timestamp: <unix-seconds>
Content-Type: application/json
{
"event": "bonus.completed",
"event_id": "evt_unique_uuid",
"ts": "2026-05-08T12:00:00Z",
"grant_id": "ucrm_grant_uuid",
"platform_bonus_id": "platform_bonus_id",
"data": { "completed_at": "...", "total_wagered": 30000 }
}
UCRM dedupes on (adapter_slug, event_id), verifies HMAC, then updates bonus_grants.status.
Idempotency
Every mutating call should be idempotent at your end too:
- Event ingestion — UCRM dedupes via
Idempotency-Keyheader on/v1/events(optional). Without it, replays produce multiple ingestion_ids — your event timestamp + transaction_id should be unique. - Adapter calls (UCRM → you) — UCRM passes our
grant_idas the correlation key. Your platform should store this + reject duplicates (return 409 withcode: "DUPLICATE_GRANT"). - Webhooks (you → UCRM) — UCRM dedupes on
event_id. If you accidentally fire the same event twice, the replay returns 200 +applied: false+replay: true.
Retries
- UCRM → you (adapter calls): 3 retries with exponential backoff (1s / 4s / 16s) on 5xx + network errors. Non-retriable on 4xx +
code: "DUPLICATE_GRANT". - You → UCRM (webhooks): you should retry on non-2xx. Recommended 5 retries with exponential backoff up to 1 hour. UCRM's response is always fast (<200ms) so timeouts are rare.
Error semantics
UCRM returns errors with a typed code:
{
"error": {
"code": "PLATFORM_REJECTED",
"message": "Player is self-excluded",
"details": { "platform_code": "PLAYER_BLOCKED" }
}
}
When your adapter returns errors back to UCRM, use the same shape. Distinguish:
- Retriable: 5xx, network errors, rate limits (429). UCRM will retry.
- Non-retriable: 4xx with config / validation errors. UCRM logs + surfaces in admin UI.