Skip to main content

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-Key header 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_id as the correlation key. Your platform should store this + reject duplicates (return 409 with code: "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.