---
title: "ATP Immersive Betting Integration"
canonical_url: "https://apidocs.sportradar.com/resources/widgets/docs/immersive-betting/immersive-betting-integration"
markdown_url: "https://apidocs.sportradar.com/resources/widgets/docs/immersive-betting/immersive-betting-integration.md"
last_updated: "2026-10-02T07:53:33Z"
---

# ATP Immersive Betting Integration

Immersive Betting is a live, in-play micro-betting experience for tennis, delivered through the **`immersiveBetting`** widget. It brings together a
live scoreboard, betting panel, bet history, and an embedded **LMT Plus** court visualization or AV stream.

Markets, odds, bet placement, settlement, and bet history are handled by the **ATP backend**, while odds and market state are sourced from **UOF**. \*
*Limits*\* are configured on the ATP backend. The **wallet** remains with the bookmaker.

Integration consists of two parts: **embed the widget** and **supply a punter `sessionToken`**.

***

## Overview

Integration consists of two parts:

1. **Embedding the widget** — load the widgetloader and mount `immersiveBetting` with `matchId`, `sportId`, and target `environment`.
2. **Authenticating the punter** — supply an ATP `sessionToken` so the widget can load the punter profile, place bets, stream bet updates, and show
   bet history.

These two workstreams can run in parallel. A typical end-to-end integration takes **1–3 weeks**, depending on whether session/auth already exists and
whether optional features (AV stream, theming) are in scope — see [Typical Timeline](#typical-timeline).

***

## Part 1 — Embedding the Widget

Before a punter can place a bet, the widget must be mounted on the client's page with a valid `matchId`. Sportradar supplies markets, odds, live match
data, court visualization, bet placement, and settlement through the widget and ATP backend.

**Do not mount a separate `match.lmtPlus` for the same match.** The widget embeds LMT Plus internally for the court view. Only one `immersiveBetting`
instance per match per page is recommended.

### Bootstrap

Replace `YOUR_CLIENT_ID` with your licensed widgets client id:

```html
<script>
    var clientId = 'YOUR_CLIENT_ID';

    (function (a, b, c, d, e, f, g, h, i) {
        a[e] ||
            ((i = a[e] =
                function () {
                    (a[e].q = a[e].q || []).push(arguments);
                }),
            (i.l = 1 * new Date()),
            (i.o = f),
            (g = b.createElement(c)),
            (h = b.getElementsByTagName(c)[0]),
            (g.async = 1),
            (g.src = d),
            g.setAttribute('n', e),
            h.parentNode.insertBefore(g, h));
    })(window, document, 'script', 'https://widgets.sir.sportradar.com/' + clientId + '/widgetloader', 'SIR', { language: 'en' });
</script>
```

### Mount

```javascript
SIR('addWidget', '#sr-widget', 'immersiveBetting', {
    matchId: 12345678,
    sportId: 5,
    environment: 'prod',
});
```

| Parameter     | Required | Description                                                                                                                                         |
| ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Widget name   | Yes      | `immersiveBetting`                                                                                                                                  |
| `matchId`     | Yes      | Match id. Obtain via Sports API, Coverage Feed, UOF, or Mapping Feed.                                                                               |
| `sportId`     | Yes      | `5` for tennis                                                                                                                                      |
| `environment` | Yes      | `stage` or `prod` for client integrations. Additional non-production values (`demo`, `dev1`–`dev3`) are used during Sportradar-led onboarding only. |

### Update and remove

```javascript
SIR('updateWidget', '#sr-widget', { matchId: 87654321 });
SIR('removeWidget', '#sr-widget');
```

Changing `environment` after mount is not supported.

### What the widget includes

| Area                | Description                                                                |
| ------------------- | -------------------------------------------------------------------------- |
| Scoreboard          | Player names, sets/games/points, serve indicator                           |
| Betting panel       | Market, stake control, bet confirmation, users' betting statistics         |
| Court visualization | Embedded `match.lmtPlus` (rear court view, momentum/tabs disabled)         |
| AV visualization    | Alternative to the court visualization; embedded `match.lmtPlus`           |
| Bet history         | Per-set timeline of bets placed by authorized users over the last 24 hours |
| Settlement          | Win/loss/cancel notifications synced with live rally state                 |
| Match overlays      | Set break, tiebreak, match ended, unauthorized state                       |

Court surface theme (hard, clay, grass) is resolved automatically from tournament data.

### Markets and lifecycle

| Item                  | Value                                                                                                       |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| Primary market        | **Next Point** (point winner)                                                                               |
| Market id             | `218`                                                                                                       |
| Odds and market state | **UOF**, streamed to the widget via the ATP backend (NATS + REST)                                           |
| Betting window        | Market must be **open** in UOF (outcomes available); the widget also suspends the UI during an active rally |
| Settlement            | ATP backend: results are pushed directly to the widget.                                                     |

When a UOF market is open and outcomes are streamed, the widget enables acceptance. When the market is suspended or disabled, placement is blocked
regardless of punter session state. See [UOF](#uof) for details.

### UOF

ATP Immersive Betting integrates exclusively with Sportradar's **Unified Odds Feed (UOF)**. UOF is the single supported source for betting markets,
outcomes, odds, and market availability. It provides real-time odds updates through XML messages and exposes additional market metadata through its
API.

For the current **Next Point** market (`marketId: 218`), the ATP backend consumes the relevant UOF data and makes it available to the widget. The
widget does not connect directly to third-party odds providers and does not support alternative odds feeds or client-side odds adapters.

The widget enables betting only when:

- the UOF market is active and open;
- valid outcomes and odds are available;
- the relevant UOF producer and event-handling services are available; and
- the punter has a valid ATP session.

Third-party data providers are out of scope for this integration. Supporting another odds provider would require a separate integration and is not
covered by the ATP Immersive Betting product. All market definitions, odds updates, availability states, and related mappings must therefore follow
the UOF model and lifecycle.

Sportradar configures ATP backend connectivity and per-client identifiers (`customerId`, API/WSS URLs) in the widgets license during onboarding.

### Optional configuration

Additional behaviour can be passed under `sportProps` (tennis only today):

```javascript
SIR('addWidget', '#sr-widget', 'immersiveBetting', {
    matchId: 12345678,
    sportId: 5,
    environment: 'prod',
    sportProps: {
        withPlayerInfo: true,
        withMatches: true,
        withBetSizeOptions: false,
        stakeControlView: 'standard',
        avStreamJwtToken: 'JWT',
    },
});
```

| Prop                 | Default    | Description                                                            |
| -------------------- | ---------- | ---------------------------------------------------------------------- |
| `withPlayerInfo`     | `false`    | Player labels on court; highlight the player linked to the active bet  |
| `withMatches`        | `false`    | Live match switcher when the current match ends; fires `onMatchChange` |
| `withBetSizeOptions` | `false`    | Preset stake amount chips                                              |
| `stakeControlView`   | `standard` | Stake control layout: `standard` or `compact`                          |
| `avStreamJwtToken`   | —          | JWT for live video inside the embedded court visualization             |

***

## Part 2 — Authenticating the Punter

When the punter places a bet, the widget submits the ticket to the **ATP backend** using the supplied `sessionToken`. The token identifies the punter
session for profile loading, bet placement, WebSocket bet updates, and bet history.

Two integration approaches are available:

- **Pre-authenticated session** — pass `sessionToken` when mounting the widget.
- **Sign-in on demand** — mount without a token; use the `onSignIn` callback and `updateWidget` after login.

### Option A: Pre-authenticated Session

The client passes `sessionToken` at mount. The widget loads the punter profile (limits from the ATP backend, currency) and
enables bet placement when the market is open and rally is active.

Use when the immersive betting page is behind login or the client already has an ATP session before the widget loads.

```javascript
SIR('addWidget', '#sr-widget', 'immersiveBetting', {
    matchId: 12345678,
    sportId: 5,
    environment: 'prod',
    sessionToken: 'ATP_SESSION_TOKEN',
});
```

On token refresh or logout, update the widget:

```javascript
SIR('updateWidget', '#sr-widget', { sessionToken: 'NEW_ATP_SESSION_TOKEN' });
// To log out:
SIR('updateWidget', '#sr-widget', { sessionToken: undefined });
```

### Option B: Sign-in on Demand

The widget mounts without `sessionToken`. Match and market data still load; the betting panel shows an unauthorized state. The client handles login
through the `onSignIn` callback and passes the token once available.

Use when the client wants to show the live experience before login or authentication completes asynchronously after the page loads.

```javascript
SIR('addWidget', '#sr-widget', 'immersiveBetting', {
    matchId: 12345678,
    sportId: 5,
    environment: 'prod',
    onSignIn: function () {
        // Open client login; after success, call updateWidget with sessionToken
    },
});
```

```javascript
// After successful login
SIR('updateWidget', '#sr-widget', { sessionToken: 'ATP_SESSION_TOKEN' });
```

### Bet placement

The full bet acceptance flow runs inside the widget:

1. Select the stake amount.
2. Select an outcome from the market.
3. Confirm the selected market outcome.
4. The widget calls the ATP backend to place the bet.
5. Bet status updates are received over WebSocket, and settlement is displayed in the widget.

### Wallet, limits, and acceptance rules

Responsibility is split across **UOF**, the **ATP backend**, and the **bookmaker**:

| Concern                   | Owner                   | In the widget                                                                                                                                                                                                                            |
| ------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Odds and market state** | UOF (via ATP backend)   | Odds and whether a market is open or suspended come from UOF, streamed to the widget through the ATP backend.                                                                                                                            |
| **Wallet**                | Bookmaker               | Balance is owned and funded by the bookmaker. The widget displays balance from the punter profile API. Errors such as insufficient funds come back from placement via the ATP backend.                                                   |
| **Limits**                | ATP backend             | Min/max stake, max win, max odds, and preset amounts are configured on the ATP backend and returned with the punter profile.                                                                                                             |
| **Placement**             | ATP backend + bookmaker | When the UOF market is open, the widget submits to the ATP backend, which accepts or rejects with the bookmaker. Widget-side rules (e.g. no bets during an active rally, current-point market only) apply on top of market availability. |
| **Settlement**            | ATP backend             | Results are pushed to the widget over WebSocket; no client resulting feed.                                                                                                                                                               |

### Page callbacks

| Callback        | When fired                                                                                   |
| --------------- | -------------------------------------------------------------------------------------------- |
| `onSignIn`      | User should log in (no valid `sessionToken`, or user taps sign-in in the unauthorized panel) |
| `onClose`       | User closed the widget (header close action)                                                 |
| `onMatchChange` | User switched to another live match (`sportProps.withMatches: true`)                         |

```javascript
SIR('addWidget', '#sr-widget', 'immersiveBetting', {
    matchId: 12345678,
    sportId: 5,
    environment: 'prod',
    sessionToken: 'ATP_SESSION_TOKEN',
    onClose: () => {
        /* navigate away or hide container */
    },
    onMatchChange: (matchId) => {
        /* update page URL/state */
    },
});
```

### Choosing an Authentication Approach

|                           | Pre-authenticated session                 | Sign-in on demand                               |
| ------------------------- | ----------------------------------------- | ----------------------------------------------- |
| `sessionToken` at mount   | Required                                  | Omitted                                         |
| Betting before login      | No                                        | No — markets visible, placement disabled        |
| Client's main deliverable | Issue ATP `sessionToken` before page load | `onSignIn` handler + `updateWidget` after login |
| Token refresh             | `updateWidget` with new token             | Same                                            |
| Typical effort            | \~3–5 days                                | \~5–7 days                                      |

***

## Identifiers and Data Sources

| Identifier     | Role                                                        |
| -------------- | ----------------------------------------------------------- |
| `clientId`     | Widgetloader URL path — licensed widgets client             |
| `matchId`      | Target tennis match                                         |
| `sportId`      | `5` (tennis)                                                |
| `sessionToken` | ATP punter session — issued by the client via ATP auth APIs |

**Nomenclature note:** widget `tournamentId` / `uniqueTournamentId` (on other widgets) map to `simpleTournamentId` / `tournamentId` in Sports API
responses. `immersiveBetting` only requires `matchId` at mount.

Match ids can be resolved from Sports API, Coverage Feed, UOF, or Mapping Feed depending on the integration scenario.

***

## Typical Timeline

| Step                | Description                                                    | Estimate                                     |
| ------------------- | -------------------------------------------------------------- | -------------------------------------------- |
| Kick-off            | Shared channel; client id, `environment`, ATP session contract | < 1 week                                     |
| Widget embed        | Widgetloader, `immersiveBetting` mount, `matchId` wiring, QA   | \~2–3 days                                   |
| Session integration | ATP `sessionToken` issuance and `updateWidget` lifecycle       | Pre-auth: \~3–5 days · On-demand: \~5–7 days |
| Optional AV stream  | `avStreamJwtToken` issuance and QA                             | \~3–5 days                                   |
| Theming             | Client theme CSS                                               | \~2–3 days                                   |

Embed and session work can largely run in parallel. End-to-end integrations typically require **1–3 weeks** when session/auth already exists.
Greenfield ATP auth adds time depending on the client's identity stack.

***

## Client Prerequisites

- Licensed widgets **`clientId`** with `immersiveBetting` enabled
- **`matchId`** for the target tennis match
- Ability to issue and refresh ATP **`sessionToken`** for logged-in punters (coordinate with ATP onboarding — not the widgets team alone)
- For sign-in on demand: handler for **`onSignIn`** and post-login **`updateWidget`**
- For live video: AV entitlement and **`avStreamJwtToken`** generation
- A point of contact for the shared integration channel
