Developer docs

Connect once. Get only what you bought.

Partners authenticate with an API key. Access is limited by module grants (odds / video packs) issued in admin — not by inventing req/hour hobby tiers.

Hosts

api.oddsrail.comREST
ws.oddsrail.comWebSocket
play.oddsrail.comPlay / HLS proxy
docs.oddsrail.comThis site
admin.oddsrail.comKeys & grants (ops)

Authentication

Keys look like or_live_…. Pass on every partner request:

Authorization: Bearer or_live_…
# or
X-Api-Key: or_live_…

Inspect your grants:

GET https://api.oddsrail.com/v1/me
Authorization: Bearer or_live_…
{
  "ok": true,
  "key": {
    "id": "…",
    "mode": "full",
    "modules": ["odds_football", "video_football"],
    "oddsSports": ["soccer", "cyber-football"],
    "videoSports": ["soccer", "cyber-football"],
    "scopes": ["feed:read", "ws:feed", "stream:read", "ws:stream", "play:read"]
  }
}

Module grants

When a partner buys packs on the constructor, ops create (or later auto-provision) a key with those modules. The API then returns only sports from those packs.

Modes (api_ws / ws_stream / full) set scopes. Modules set coverage. Prefer modules for every paid key.

REST

Live events

GET https://api.oddsrail.com/v1/events?locale=en&limit=40
Authorization: Bearer or_live_…

Requires scope feed:read. Response events are already filtered by the key’s odds packs.

Public demo (no key)

GET https://api.oddsrail.com/v1/demo/live?locale=en&limit=6
GET https://api.oddsrail.com/v1/demo/stream
GET https://api.oddsrail.com/v1/meta/plans

WebSocket

wss://ws.oddsrail.com/v1/ws?locale=en&api_key=or_live_…

Or connect without a key (public demo), then:

{ "type": "auth", "apiKey": "or_live_…" }
{ "type": "subscribe", "channels": ["live", "streams"], "locale": "en", "limit": 24 }

Play

Demo player: https://play.oddsrail.com/v1/demo/player/{id} (also used on the marketing site). Partner play follows the same proxy once the event is in a granted video sport — stream URLs appear on events with stream.available=true.

Checkout (USDT TRC-20)

Same wallet + unique-amount matching as imba.bet deposits. Partner taps Pay USDT on the site → exact payAmount → send TRC-20 → monitor credits → API key + cabinet link.

POST https://api.oddsrail.com/v1/checkout
{ "modules": ["odds_football", "video_football"], "billingPeriod": "month"|"year" }

GET https://api.oddsrail.com/v1/checkout/{id}
GET https://api.oddsrail.com/v1/cabinet?t=or_cab_…
POST /v1/checkout {
  "kind":"renew"|"upgrade",
  "cabinetToken":"or_cab_…",
  "modules":[...],
  "billingPeriod":"month"|"year"
}

Month = 30 days. Year = 365 days at 10× monthly (2 months free). After period end: 3-day grace (API still works), then soft-lock → 402 SUBSCRIPTION_EXPIRED until renew. Cabinet: Connect snippets, rotate key, renew month/year, upgrade packs.

Client panel & Sportsbook silos

Portal: oddsrail.com/panel. Sportsbooks use the imba engine under Oddsrail brand — hosts are always *.oddsrail.com (never imba.bet in customer URLs). Provision job writes silo config + advances to SANDBOX_READY. Real-money LIVE is compliance-gated.

POST /v1/auth/register { "email", "password", "name?" }
POST /v1/auth/login
GET  /v1/auth/me
GET  /v1/panel/overview
POST /v1/panel/link-cabinet { "token": "or_cab_…" }
POST /v1/panel/instances { "name", "slug?" }
# → status PROVISIONING → SANDBOX_READY
# artifacts: data/silos/{slug}/imba.env + compose.silo.yml

Ops

  1. Self-serve checkout is preferred (auto key + grants + cabinet link).
  2. Client panel for account / BK stubs / billing history.
  3. Manual fallback: admin → API keys → Grants.
  4. Admin → Billing: MRR, renew-soon, suspended, recent orders.
  5. GET /admin/api/metrics for the same snapshot.