Oddin.gg Penalty Arena documentation
Introduction
Penalty Arena is a fast-paced betting product where users can directly affect the gameplay. It offers a unique "Live shots, instant win" experience by letting users vote for the direction of a penalty shot. A machine executes the shot based on the direction with the highest number of votes. Following the voting phase, users can place bets on the outcome of the shot.
Game Structure:
- 1 Round: 1 kick (Voting + Kick + Settlement) ≈ 1 - 2 minutes.
- 1 Series: 5 rounds (5 kicks) ≈ 7 - 15 minutes.
- 1 Session: 5 series (25 rounds) ≈ 30 - 50 minutes (the goalkeeper swaps out after one session).
Penalty Arena is seamlessly integrated with a live betting overlay and betslip. It is integrated via Oddin.gg Esports Betting iFrame (Bifrost) and is accessed through a dedicated route within the iFrame.
1. Integration overview
Penalty Arena is embedded as a special route within the Bifrost iFrame. To integrate Penalty Arena, you must follow these steps:
1.1. Ensure iFrame (Bifrost) integration
First, you must fully support the Oddin.gg Esports Betting iFrame (Bifrost). This includes meeting all the requirements defined in the Bifrost - Esports Betting iFrame Documentation, such as:
- Authentication (point 1.3. of Bifrost documentation)
- iFrame embedding (point 2. of Bifrost documentation)
- Backend API endpoints (point 3. of Bifrost documentation):
/userDetails/debitUser/creditUser
If you are already an Oddin iFrame client, this step is sufficient to ensure the Penalty Arena integration.
1.2. Loading Penalty Arena
To load the Penalty Arena product, initialize the Bifrost iFrame with the specific route parameter set to penalty-arena (or the specific route string provided during integration).
Initial route for init script: eyJyb3V0ZSI6Ii9wZW5hbHR5LWFyZW5hIn0=
Encoded: {"route":"/penalty-arena"}
Example iFrame initialization script:
<script type="text/javascript">
oddin.buildBifrost({
token: "user_session_token",
brandToken: "YOUR_BRAND_TOKEN",
baseUrl: "https://bifrost.oddin.gg",
language: "en",
currency: "EUR",
contentElement: "#bifrost",
route: "eyJyb3V0ZSI6Ii9wZW5hbHR5LWFyZW5hIn0=", // Dedicated route for Penalty Arena
eventHandler: (e) => {
if (e.type === "REQUEST_REFRESH_BALANCE") {
// Handle balance refresh
}
}
});
</script>
<script
src="https://bifrost.oddin.gg/script.js" type="text/javascript"></script>
1.3. Environments
You can find the endpoint for each environment in the following table:
| Name | Script URL |
| Production | https://bifrost.oddin.gg/script.js |
| Integration | https://bifrost.integration.oddin.gg/script.js |
1.3.1. API Authentication
We will perform requests to your API only from these IPs, please whitelist them in your system:
- 108.128.47.233
- 3.1.190.49
You can also set up a security key, which will be included in the X-API-Key header in every request to your server.
Access token: You must use the Bifrost iFrame access token (brandToken) to access the Penalty Arena API. You should have obtained this access token from our back office; if not, please contact us through your direct Oddin.gg communication channels.
2. Wallet transactions & ticket extensions
Penalty Arena utilizes the standard Bifrost wallet transaction flow (/userDetails, /debitUser, /creditUser). However, to support the unique mechanics of the game, the Ticket Selection object has been extended with additional fields.
2.1. New ticket selection parameters
The following fields are added to the selections array within the Ticket object to provide context about the specific Penalty Arena round.
| Name | Type | Description |
marketCreatedAt |
date | Timestamp (ISO format), indicating when the market (voting/betting phase) was opened for the specific round. |
marketDeactivatedAt |
date | Timestamp (ISO format), indicating when the market was closed/deactivated (kick execution started). |
roundInfo |
string | Information about the current match and round (e.g., "1/5" indicating 1st Match and 5th Round). |
2.2. JSON Ticket Example
Below is a JSON example of a ticket payload for Penalty Arena, including the new fields.
"ticket": {
"stake": {
"amount": 10,
"totalAmount": 10
},
"wonAmount": {
"amount": 0,
"totalAmount": 0
},
"date": "2026-09-16T17:34:45Z",
"status": "lost",
"bets": [
{
"id": "b2ccbab0-391e-43b7-a1c4-2b5970a525a1",
"stake": {
"amount": 10,
"totalAmount": 10
},
"selections": [
{
"id": "b3V0Y29tZS9vZDptYXRjaDozMjA3Mjg4LzE3NXxyb3VuZD00LzE3NS1yb3VuZD00LzgyNw==",
"outcome": {
"name": "Save",
"id": 827,
"outcomeId": "827",
"marketId": "175-round=4",
"marketName": "Round Result",
"localizationId": "827"
},
"odds": 1.28,
"event": {
"id": "od:match:3207288",
"teams": [
{
"name": "People",
"isHome": true,
"id": "od:competitor:21767"
},
{
"name": "Goalkeeper (Rui)",
"isHome": false,
"id": "od:competitor:22294"
}
],
"tournamentId": "od:tournament:14845",
"tournamentName": "Penalty Arena 2 2026 Week #38",
"sportName": "Penalty Arena",
"sportId": "od:sport:53",
"dateStart": "2026-09-16T17:26:00Z",
"isLive": true
},
"marketCreatedAt": "2026-09-16T17:34:30Z",
"marketDeactivatedAt": "2026-09-16T17:35:13Z",
"roundInfo": "117/4",
"won": false,
"voided": false
}
],
"won": false,
"voided": false,
"wonAmount": {
"amount": 0,
"totalAmount": 0
},
"totalOdds": 1.28,
"systems": [
1
]
}
],
"possibleWinAmount": {
"amount": 12.8,
"totalAmount": 12.8
},
"deviceInfo": {
"displayResolution": "360x478",
"userAgent": "Mozilla/5.0 (Linux; Android 14; Infinix X6531B Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/151.0.7922.200 Mobile Safari/537.36 NO_JSESSIONID Android NO_JSESSIONID Android"
}
}
3. Q&A
3.1. Will users and limits be shared between betting activities on my OTS/Esports iFrame subscription and Penalty Arena?
Yes, these will be shared. The user wallet, authentication token, and liability limits are unified across all Oddin.gg iFrame products deployed under the same tenant.
3.2. Does Penalty Arena require a separate integration if I already have the Oddin iFrame?
No. If you have a fully functional Oddin.gg iFrame integration, Penalty Arena is available simply by changing the initialization route or using page routing events. No new API endpoints are required, though you must ensure your system can handle the extended ticket fields described in section 2.1.
3.3. How are transactions handled?
Transactions follow the standard Bifrost flow:
- User identification:
/userDetails(at iFrame initialisation) - Bet placement:
/debitUser(transaction type: Block). - Bet settlement (Win):
/creditUser(transaction type: Credit). - Bet settlement (Loss):
/creditUser(transaction type: Lost). - Bet settlement (Void):
/creditUser(transaction type: Void).