Integration
The betting frame allows seamless integration into partner websites, providing a complete betting experience with customization options for theming, localization, and event handling. The integration can be done either as a Single Page Application (SPA) or with Server-Side Rendering (SSR) or through the integration of individual widgets(Outside Widgets - only client), or you can integrate your own widget into betting (Custom Widget - only client), depending on your needs.
Configuration options
AppInitOptions
AppInitOptions is the top-level configuration object, which has the following structure:
| interface AppInitOptions {
token: string;
rootElement: string;
url: BettingUrl;
features: Record<FeatureKeys, boolean>;
theme: Theme;
defaultSettings: DefaultSettings;
}
|
| Property | Description | Type | Required |
token | Token of the current player Token | string | yes |
rootElement | Identifier of the DOM element in which betting will be embedded | string | yes |
url | All endpoints for betting | BettingUrl | yes |
features | A set of Features that are responsible for connecting various SPA functionality. | Record<FeatureKeys, boolean> | no |
defaultSettings | Default settings for the SPA. | DefaultSettings | yes |
BettingURL contains all the necessary endpoints for the SPA:
| interface BettingUrl {
/** @deprecated This property is deprecated and will be removed in a future version. It may be omitted. */
gqlEndpoint: string;
staticEndpoint: string;
basename: string;
}
|
| Property | Description | Type | Required |
gqlEndpoint | Path to the GraphQL server (Deprecated) | string | no |
staticEndpoint | Path to the server where all statistics are stored (js/css/assets) | string | yes |
basename | The basename field is used to specify the base URL for all relative links within the embedded betting. This is particularly useful when the application is deployed within a subdirectory of a domain. | string | yes |
Default settings
DefaultSetting options are used to set the initial configuration of the application.
They define default values for various settings, such as display odds, which can later be changed by gambler.
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | interface DefaultSettings {
oddFormat: OddFormat;
oddAcceptStrategy: OddAcceptStrategy;
}
type OddFormat =
| "Decimal"
| "Fractional"
| "US"
| "HongKong"
| "Indo"
| "Malay";
type OddAcceptStrategy = "acceptAll" | "acceptHigher" | "alwaysAsk";
|
| Property | Description | Type | Default value | Required |
oddFormat | Default odd format for the SPA. | OddFormat | Decimal | false |
oddAcceptStrategy | Default odd accept strategy for the SPA. | OddAcceptStrategy | acceptAll | false |
Integration Bootstrap script
Bootstrap script is needed for integrations (SPA) or Outside Widgets into your product.
1. Add the bootstrap script
Include the script for the appropriate environment. Use the staging version during development and testing, and production for live deployment.
- Production:
| <script
type="module"
defer
src="https://spa.databet.cloud/v2/<project-name>/bootstrap.js"
></script>
|
- Staging:
| <script
type="module"
defer
src="https://spa.int.databet.cloud/v2/<project-name>/bootstrap.js"
></script>
|
Warning
type="module" is required attribute
Note
This links will be provided by DATA.BET
SPA Integration
To integrate the betting SPA into your product, follow these steps:
1. Add DOM containers
You must include the following elements in your HTML layout:
- Main container — this is where the entire betting application will be rendered:
| <div id="betting__container"></div>
|
- Betslip containers — separate element for betslip views:
| <div id="betting-betslip"></div>
|
Note
These elements must exist in the DOM before initializing the SPA
2. Add the bootstrap script
You need to integrate the bootstrap script.
3. Initialize the application
Once the script has loaded, a global object bettingLoader becomes available on the window. Use its load method to start the SPA:
| interface BettingLoader {
load: (
options: AppInitOptions,
onLoad: (bettingAPI: BettingAPI) => void
) => void;
}
|
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 | <div id="betting__container"></div>
<div id="betting-betslip"></div>
<script
type="module"
async
src="https://spa.databet.cloud/v2/<project-name>/bootstrap.js"
onload="loadBetting()"
></script>
<script>
function loadBetting() {
bettingLoader.load(
{
url: {
staticEndpoint: `<link to static>`, // will be provided by DATA.BET
basename: "/",
},
token: `<token>`,
rootElement: "betting__container",
theme: {
offsetTop: 64, // Offset for sticky elements when scrolling (useful for fixed headers)
palette: {
colorsPrimary1: "#000000",
colorsPrimary2: "#ffffff",
colorsSecondary1: "#f3f3f3",
colorsSecondary2: "#e3e3e3",
colorsSecondary3: "#9999a1",
colorsAccent1: "#ff6b00",
colorsAccent2: "#ffb700",
colorsAccent3: "#a900d9",
textButton: "#ffffff",
textPrimary: "#000000",
textSecondary: "#656565",
notificationBlocked: "#b5b5b5",
notificationError: "#ff3e33",
notificationInfo: "#0852ff",
notificationSuccess: "#05BC0B",
notificationWarning: "#ff9000",
},
},
defaultSettings: {
oddFormat: "Decimal",
},
},
(bettingAPI) => {
// use bettingAPI to interact with spa on runtime
}
);
}
</script>
|
Important
Changes to the token require a page reload to take effect. This limitation is expected to be resolved in future updates.
4.Style the betslip containers
The SPA renders betslip view into dedicated container, depending on screen size:
React Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 | <script>
type IStypeBetslip = Pick<ToggleWidgetBetslipPayload, "breakpoint" | "isOpen">;
const BetslipStyleGetter: Record<
"static" | "island",
(data: IStypeBetslip) => string
> = {
static: ({ isOpen, breakpoint }: IStypeBetslip): string => {
if (breakpoint === "mobile") {
if (isOpen) {
document.body.classList.add("overflow-hidden");
return "fixed bottom-0 left-0 top-0 z-40 lg:hidden w-full h-full";
} else {
document.body.classList.remove("overflow-hidden");
return "fixed bottom-0 left-0 -z-40 w-full";
}
}
if (breakpoint === "tablet") {
return "fixed top-0 left-auto right-0 z-40 h-[100vh]";
}
return "";
},
island: ({ isOpen, breakpoint }): string => {
if (breakpoint === "mobile") {
if (isOpen) {
document.body.classList.add("overflow-hidden");
return "fixed bottom-0 left-0 top-0 z-40 lg:hidden w-full h-full max-h-full";
} else {
document.body.classList.remove("overflow-hidden");
return "fixed bottom-0 left-0 z-40 w-full";
}
}
if (breakpoint === "tablet") {
return "fixed inset-x-1/2 bottom-0 z-[999999] w-full max-w-[320px] max-h-[100vh] -translate-x-1/2";
}
return "fixed inset-x-1/2 bottom-0 z-[999999] w-full max-w-[320px] max-h-[80vh] -translate-x-1/2";
},
};
const BetslipContainer: FC = () => {
const [state, setState] = useState<ToggleWidgetBetslipPayload | undefined>();
const initBetting = useCallback(() => {
if (!window.bettingAPI) return;
window.bettingAPI.subscribe("toggle-widget-betslip", setState);
}, []);
useEffect(() => {
document.addEventListener("betting-init", initBetting);
return () => document.removeEventListener("betting-init", initBetting);
}, [initBetting]);
const getter = state
? BetslipStyleGetter[state.widgetType] || BetslipStyleGetter["static"]
: undefined;
const className = state && getter
? getter({ isOpen: state.isOpen, breakpoint: state.breakpoint })
: "";
return <div id="betting-betslip" className={className}></div>;
};
</script>
|
Again, feel free to adapt this styling for your layout — e.g., adjusting height or position to avoid overlapping your app’s header or sidebar.
SSR Integration
The SSR (Server-Side Rendering) integration with the DATA.BET platform consists of two parts:
- Server-side integration — generating betting HTML fragments on the server.
- Client-side integration — initializing interactivity via the
bettingAPI.
1. Server-side integration
DATA.BET provides an SSR server that returns pre-rendered betting application HTML for a specific user route.
To integrate it, send a POST request to the SSR endpoint. The request URL must exactly match the path and query parameters the user is currently visiting (i.e., pathname + query).
The server endpoint depends on the environment:
- Production:
https://ssr.databet.cloud - Staging/Integration:
https://ssr.int.databet.cloud
Below is an example used on the demo site (Next.js):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 | import { headers } from "next/headers";
import { userAgent } from "next/server";
const BettingPage = async ({
params,
}: {
params: { locale: string };
}) => {
const locale = params.locale;
const requestHeaders = headers();
const { device } = userAgent({ headers: requestHeaders });
let requestUrl;
try {
requestUrl = new URL(currentHeaders.get("x-url") || "");
} catch {
return null;
}
const appInitOptions = {
rootElement: "root",
defaultLayout: device.type || "desktop",
token,
url: {
staticEndpoint: process.env.CDN_URL || "<your_static_endpoint>",
basename: `${locale}/${userTokenLabel}/ssr`,
},
theme: { ... }
};
// Insert a link to the SSR server based on the current environment.
const response = await fetch(`https://ssr.int.databet.cloud/${requestUrl.patchname}${requestUrl.search}`, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
bettingOptions: appInitOptions,
}),
});
const bettingHtml = await response.text();
return (
<>
<main
suppressHydrationWarning={true} // Important for SSR content that might differ slightly on hydration
dangerouslySetInnerHTML={{ __html: bettingHtml }}
/>
<BettingIntegration />
</>
);
|
⚠️ Important Notes
1. Betting Token Management
The token field in AppInitOptions is required.
You are fully responsible for the management of this token in your application — including:
- Generation (e.g., via guest or login flow)
- Validation (e.g., signature or expiration)
- Storage (e.g., HTTP-only cookie or server session)
- Refresh or renewal (if applicable)
2. Device Layout Detection
The layout field in AppInitOptions defines how the betting UI should render: 'mobile', 'tablet', or 'desktop'.
This should be determined based on the user's device, typically by inspecting the User-Agent header In a Next.js App Router environment, use the userAgent utility to extract device info safely on the server:
| import { userAgent } from "next/server";
import { headers } from "next/headers";
const requestHeaders = await headers();
const layout = userAgent({ header: requestHeaders }).device.type || "desktop";
|
3. Request url
The SSR request must target the exact URL that corresponds to the user’s current location, including both the pathname and search components. This ensures that the returned betting content is accurate and relevant for the current route.
In a Next.js application, the current URL should be captured and forwarded via custom headers (e.g., x-url) using middleware. This allows the server component to reconstruct the user's location and perform the SSR request accordingly.
| export function middleware(request: NextRequest) {
const headers = new Headers(request.headers);
headers.set("x-url", request.url);
return NextResponse.next({ headers });
}
|
2. Client-side integration
Once the server-rendered HTML is loaded and hydrated on the client-side, the betting-init event is dispatched, signaling that window.bettingAPI is ready.
This API enables interaction with the embedded betting application: event subscriptions, register callback, etc.
Example (Client Component in Next.js):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | "use client";
import { useCallback, useEffect, useState } from "react";
export const BettingIntegration = () => {
const initBetting = useCallback(() => {
window.bettingAPI.subscribe(
"toggle-widget-betslip",
({ isOpen: isOpenState }: { isOpen: boolean }) => {
...
}
);
// Add additional subscriptions or actions with the BettingAPI if required
}, []);
useEffect(() => {
// Check if bettingAPI is already available (in case betting-init fired earlier)
if (window.bettingAPI) {
initBetting();
} else {
document.addEventListener("betting-init", initBetting);
}
return () => {
document.removeEventListener("betting-init", initBetting);
};
}, [initBetting]);
return null;
};
|
3. Fallback to SPA integration if SSR fails
If you are using SSR (Server-Side Rendering) integration and the SSR server responds with an error (for example, a 4xx/5xx HTTP status or a network failure), it is recommended to gracefully fall back to the SPA integration. This ensures that users can still access the betting application even if SSR is temporarily unavailable.
Steps to implement fallback:
-
Attempt SSR first:
Try to fetch the SSR-rendered HTML from your SSR endpoint as usual.
-
Detect SSR failure:
If the SSR request fails (e.g., non-200 response, network error, or invalid HTML), proceed to load the SPA client-side.
-
Initialize SPA via bettingLoader:
Use the same bettingLoader.load method as described above to bootstrap the SPA in the client browser.
1. Add the custom elements
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | interface BaseWidgetStyle {
"--bet-font-sans"?: string;
"--bet-base-font-size"?: string;
"--bet-colors-primary-1"?: string;
"--bet-colors-primary-2"?: string;
"--bet-colors-secondary-1"?: string;
"--bet-colors-secondary-2"?: string;
"--bet-colors-secondary-3"?: string;
"--bet-colors-accent-1"?: string;
"--bet-colors-accent-2"?: string;
"--bet-colors-accent-3"?: string;
"--bet-text-button"?: string;
"--bet-text-primary"?: string;
"--bet-text-secondary"?: string;
"--bet-notification-blocked"?: string;
"--bet-notification-error"?: string;
"--bet-notification-info"?: string;
"--bet-notification-success"?: string;
"--bet-notification-warning"?: string;
"--bet-rounded-2"?: string;
"--bet-rounded-4"?: string;
"--bet-rounded-6"?: string;
"--bet-rounded-8"?: string;
"--bet-rounded-12"?: string;
"--bet-rounded-24"?: string;
}
interface IDefaultWidgetProps {
style?: BaseWidgetStyle; // Required to go beyond the theme you pass to loadWidgets.
"global-size"?: true; // Default false, take the dimensions of the document for resizing and not the integration block.
}
|
Top events widget
| interface IntrinsicElements {
"top-events-outside-widget": IDefaultWidgetProps & {
gap?: number,
"sport-type"?: "sports" | "esports" | "all",
};
}
|
| <top-events-outside-widget sport-type="sports" />
|
Island Betslip Widget
| interface IntrinsicElements {
"island-betslip-widget": IDefaultWidgetProps;
}
|
| <island-betslip-widget
className="You need to specify classes similar to SPA integration"
/>
|
2. Add the bootstrap script
You need to integrate the bootstrap script.
3. Initialize the application
Once the script has loaded, a global object bettingLoader becomes available on the window. Use its loadWidgets method to load custom elements:
| interface BettingLoader {
loadWidgets: (
options: AppInitOptions,
onLoad: (bettingAPI: BettingAPI) => void
) => void;
}
|
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 | <script
type="module"
async
src="https://spa.databet.cloud/v2/<project-name>/bootstrap.js"
onload="loadWidgets()"
></script>
<script>
function loadWidgets() {
bettingLoader.loadWidgets(
{
url: {
staticEndpoint: `<link to static>`, // will be provided by DATA.BET
basename: "/",
},
token: `<token>`,
rootElement: "betting__container",
theme: {
offsetTop: 64, // Offset for sticky elements when scrolling (useful for fixed headers)
palette: {
colorsPrimary1: "#000000",
colorsPrimary2: "#ffffff",
colorsSecondary1: "#f3f3f3",
colorsSecondary2: "#e3e3e3",
colorsSecondary3: "#9999a1",
colorsAccent1: "#ff6b00",
colorsAccent2: "#ffb700",
colorsAccent3: "#a900d9",
textButton: "#ffffff",
textPrimary: "#000000",
textSecondary: "#656565",
notificationBlocked: "#b5b5b5",
notificationError: "#ff3e33",
notificationInfo: "#0852ff",
notificationSuccess: "#05BC0B",
notificationWarning: "#ff9000",
},
},
defaultSettings: {
oddFormat: "Decimal",
},
},
(bettingAPI) => {
// use bettingAPI to interact with spa on runtime
// window.bettingAPI = bettingAPI;
// document.dispatchEvent(new Event("betting-init"));
}
);
}
</script>
|
Important
Changes to the token require a page reload to take effect. This limitation is expected to be resolved in future updates.
The Betting Frame supports custom widgets, allowing you to integrate your own content into the betting interface. Custom widgets subscribe to mount events from the betting API, enabling you to render custom content in designated containers using any framework or vanilla JavaScript.
Implementation
Custom widgets require:
- Event Subscription: Subscribe to
widget:сustom_widget:mount events from the betting API - DOM Manipulation: Use the provided DOM element to render custom content
- Event Handling: Handle mount events to get container information
Example (React)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | "use client";
import { useBettingApi } from "@/hooks/useBettingApi";
import { useEffect, useCallback, useState } from "react";
import { createPortal } from "react-dom";
interface ICustomWidgetMountEvent {
containerId: string;
element: HTMLElement;
}
const CustomWidget = () => {
const bettingApi = useBettingApi();
const [portalData, setPortalData] = useState<ICustomWidgetMountEvent | null>(null);
const handleCustomWidgetMount = useCallback(
(data: ICustomWidgetMountEvent) => {
setPortalData(data);
},
[],
);
useEffect(() => {
if (!bettingApi) return;
(bettingApi as any).subscribe(
"widget:сustom_widget:mount",
handleCustomWidgetMount,
);
}, [bettingApi, handleCustomWidgetMount]);
if (!portalData) return null;
const content = (
// Your react component
);
return createPortal(content, portalData.element);
};
export default CustomWidget;
|
Note
This example shows a React implementation using createPortal. You can use any framework (Vue, Angular, vanilla JavaScript) to manipulate the DOM element provided in the event. The key is to use the element property from the event to render your custom content.
For more details about betting events, see Betting Events.