---
title: "Getting Started"
canonical_url: "https://apidocs.sportradar.com/resources/virtual-stadium/docs/web/adapter/custom/gettingStarted"
markdown_url: "https://apidocs.sportradar.com/resources/virtual-stadium/docs/web/adapter/custom/gettingStarted.md"
last_updated: "2026-06-18T07:06:52Z"
---

# Getting Started

The Virtual Stadium adapter is the core component you implement to handle all communication between Virtual Stadium widgets and your betting platform. It processes data requests from widgets and integrates with your backend systems. Custom Adapter gives you full control over the integration, allowing you to implement and host all adapter functionality on your own infrastructure. This approach provides maximum flexibility for custom betting workflows while maintaining compatibility with Virtual Stadium's modular architecture.

![Custom Adapter Implementation Timeline](https://apidocs.sportradar.com/resources/virtual-stadium/static/static/img/VS-Own-Implementation.png)

***

## Prerequisites

Before beginning custom adapter implementation, complete these foundational steps:

**Phase 1: Initiation (1-2 Days)**

- **Communication Channel** - Sportradar initiates shared Slack channel
- **RSA Key Generation** - Generate 2048-bit RSA keys for JWT authentication → [Guide](https://apidocs.sportradar.com/resources/virtual-stadium/docs/gettingStarted/authentication.md)
- **Language Requirements** - Specify supported languages via Slack channel
- **Moderation Setup** - Sportradar configures moderation client with Supervisor access

**Phase 2: Technical Kickoff Meeting**

- **Supervisor Access** - Provide supervisor email for moderation access
- **Introduction Session** - Learn about features, adapter architecture, and integration process
- **Adapter Decision** - Confirm custom adapter is the right choice for your needs

**Phase 3: Post-Kickoff Setup**

- **Brand & Operator Setup** - Supervisor configures organizational structure (\~1 day) → [Managing Brands](https://apidocs.sportradar.com/resources/virtual-stadium/docs/moderationUi/uiOverview/dashboard/managingBrands.md)
- **Moderator Invitation** - Supervisor invites moderation team members → [Managing Users](https://apidocs.sportradar.com/resources/virtual-stadium/docs/moderationUi/uiOverview/management/managingSupervisorsAndModerators.md)
- **Channel Setup** - Create channels manually or via API → [Channel Creation](https://apidocs.sportradar.com/resources/virtual-stadium/docs/moderationUi/uiOverview/moderation/channels.md)
- **JWT Implementation** - Implement token generation on your backend → [JWT Authentication](https://apidocs.sportradar.com/resources/virtual-stadium/docs/gettingStarted/authentication.md)

[View Complete Integration Timeline →](https://apidocs.sportradar.com/resources/virtual-stadium/docs/overview.md#integration-timeline-overview)

## Implementation Timeline

The custom adapter implementation follows a structured timeline where you handle all development and hosting:

### Sportradar's Responsibility

- **Technical Guidance**

### Your Responsibility

**Module Implementation:**

- **Bet Share** - Share bet selections between users
- **Flash Bet** - Live event triggered betting
- **Bet Insights** - AI-powered betting suggestions

***

## Phase 1: Adapter Registration

Register your adapter before adding Virtual Stadium widgets to the page. The adapter function handles all communication between widgets and your backend.

[Base Adapter Registration Example →](https://apidocs.sportradar.com/resources/widgets/docs/adapter/SelfServiceImplementation.md#base-implementation-example)

***

## Phase 2: Module Development

Each module requires specific API endpoints and data formats. See individual module documentation for detailed requirements.

**Bet Share** - Social betting features

[Bet Share Implementation](https://apidocs.sportradar.com/resources/virtual-stadium/docs/web/adapter/custom/features/betShare.md)

**Flash Bet** - Live betting features

[Flash Bet Implementation](https://apidocs.sportradar.com/resources/virtual-stadium/docs/web/adapter/custom/features/flashBet.md)

**Bet Insights** - AI-powered suggestions

[Bet Insights Implementation](https://apidocs.sportradar.com/resources/virtual-stadium/docs/web/adapter/custom/features/betInsights.md)

***

## Integration Example with Mock Data:

***Expand Example Code***

Before using the example code, create a test channel. [See the channel creation documentation](https://apidocs.sportradar.com/resources/virtual-stadium/docs/moderationUi/uiOverview/moderation/channels.md#adding-a-new-channel). To test Flash Bet, activate the Testing Channel and [follow the testing channel instructions](https://apidocs.sportradar.com/resources/virtual-stadium/docs/moderationUi/uiOverview/moderation/channels.md#testing-channels).

In the code below, replace the following placeholders:

- `<your-channel-id>` with your channel ID
- `<CLIENT_ID>` with your client ID
- `<your-jwt-token>` with your newly generated JWT token

```html
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Self-Service Adapter Demo</title>
    <style>
      body {
        display: flex;
        justify-content: center;
      }
      .widgets {
        max-width: 620px;
        width: 100%;
      }
      #sr-widget {
        border: rgba(0, 0, 0, 0.12) solid 1px;
      }
    </style>
    <!-- Load the SIR widget loader script -->
    <script type="text/javascript">
      /**
       * TODO(developer): Replace <CLIENT_ID> with your actual client ID
       * provided by Sportradar before running this code.
       */
      (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/<CLIENT_ID>/widgetloader","SIR", {
          language: 'en' // SIR global options
      });

      // Mock ticket data for demonstration
      const singleBet = {
          id: 'sr:bet-share-event:single',
          betSlipId: 'sr:bet-share-event:single',
          betType: 'single',
          currency: 'USD',
          combinedOdds: {
              decimalValue: 2.55,
              displayValue: '2.55'
          },
          stake: {
              value: '50.00'
          },
          payout: {
              value: '127.50'
          },
          cashOut: {
              value: '21.83'
          },
          bets: [
              {
                  id: 'sr:bet:single',
                  betType: '',
                  markets: [
                      {
                          id: 'sr:single-market:1234',
                          name: 'Moneyline | 1st Quarter',
                          outcomes: [
                              {
                                  id: 'sr:single-outcome:56789',
                                  name: 'Monaco',
                                  odds: 2.55
                              }
                          ]
                      }
                  ],
                  odds: {
                      decimalValue: 2.55,
                      displayValue: ''
                  },
                  event: {
                      id: 'sr:single-event:56789',
                      name: 'Metz vs Monaco',
                      teams: [
                          { id: 'sr:competitor:102', name: 'Metz' },
                          { id: 'sr:competitor:201', name: 'Monaco' }
                      ]
                  }
              }
          ]
      };
      // Mock ticket data for demonstration
      const multiBet1 = {
          id: 'sr:bet-share-event:multibet1',
          betSlipId: 'sr:bet-share-event:multibet1',
          betType: 'multibet',
          currency: 'USD',
          combinedOdds: {
              decimalValue: 31.63,
              displayValue: '31.63'
          },
          stake: {
              value: '20.50'
          },
          payout: {
              value: '648.41'
          },
          cashOut: {
              value: '221.83'
          },
          bets: [
              {
                  id: 'sr:bet1:multibet1',
                  betType: '',
                  markets: [
                      {
                          id: 'sr:multibet1-bet1-market:2134',
                          name: 'Moneyline | Full Time',
                          outcomes: [
                              {
                                  id: 'sr:multibet1-bet1-outcome:645389',
                                  name: 'Metz',
                                  odds: '1.45'
                              }
                          ]
                      }
                  ],
                  event: {
                      id: 'sr:multibet1-bet1-event:98765',
                      teams: [
                          { id: 'sr:competitor:102', name: 'Metz' },
                          { id: 'sr:competitor:201', name: 'Monaco' }
                      ],
                      sport: { id: '1' }
                  }
              },
              {
                  id: 'sr:bet2:multibet1',
                  betType: '',
                  markets: [
                      {
                          id: 'sr:multibet1-bet2-market:12435',
                          name: 'Total - Full Time',
                          outcomes: [
                              {
                                  id: 'sr:multibet1-bet2-outcome:77965',
                                  name: 'Over 225.5',
                                  oddsDecimal: 2.05
                              }
                          ]
                      }
                  ],
                  event: {
                      id: 'sr:multibet1-bet2-event:98765945',
                      teams: [
                          { id: 'sr:competitor:102', name: 'Portland' },
                          { id: 'sr:competitor:201', name: 'Memphis' }
                      ],
                      sport: { id: '2' }
                  }
              },
              {
                  id: 'sr:bet3:multibet1',
                  betType: '',
                  markets: [
                      {
                          id: 'sr:multibet1-bet3-market:4432',
                          name: 'Both teams to score',
                          outcomes: [
                              {
                                  id: 'sr:multibet1-bet3-outcome:98765',
                                  name: 'Yes',
                                  oddsDecimal: 3
                              }
                          ]
                      }
                  ],
                  event: {
                      id: 'sr:multibet1-bet3-event:98765945',
                      teams: [
                          { id: 'sr:competitor:002', name: 'Nantes' },
                          { id: 'sr:competitor:001', name: 'Nice' }
                      ],
                      sport: { id: '1' }
                  }
              },
              {
                  id: 'sr:bet4:multibet1',
                  betType: '',
                  markets: [
                      {
                          id: 'sr:multibet1-bet4-market:4321',
                          name: 'Total | Full Time',
                          outcomes: [
                              {
                                  id: 'sr:multibet1-bet4-outcome:67898',
                                  name: 'Over 2.5',
                                  oddsDecimal: 1.55
                              }
                          ],
                          odds: {
                              // it is used as bet odds - see "use market odds as bet odds" comment in convertAdapterToBetSharePayload.ts
                              decimalValue: 3.55,
                              displayValue: ''
                          }
                      }
                  ],
                  event: {
                      id: 'sr:multibet1-bet4-event:98765945',
                      teams: [
                          { id: 'sr:competitor:002', name: 'Lazio' },
                          { id: 'sr:competitor:001', name: 'Juventus' }
                      ],
                      sport: { id: '1' }
                  }
              }
          ]
      };

      
      // Mock data for demonstration - replace with real API calls
      const adapter = {
        endpoints: {
          event: (args, callback) => {
            console.log(args);
            callback(undefined, {
              event: {
                id: args.selection.event,
                isLive: false,
                date: {
                  displayValue: "2026-02-02 18:00",
                  startTime: "2026-02-02T18:00:00Z",
                },
                sport: { id: "sr:sport:1", name: "Soccer" },
                category: { id: "sr:category:1", name: "France" },
                tournament: { id: "sr:tournament:17", name: "League 1" },
                teams: [
                  { id: "sr:competitor:1", name: "Monaco" },
                  { id: "sr:competitor:2", name: "Metz" },
                ],
              },
            });
            return () => {};
          },
          availableMarketsForEvent: (args, callback) => {
            console.log('availableMarketsForEvent: ',args);
            callback(undefined, {
              event: {
                id: args.selection.event,
                type: 'uf'
              },
              markets: [
                { id: "1", name: "1x2" },
                { id: "18", name: "Total Goals", specifiers: "total=0.5" },
                { id: "18", name: "Total Goals", specifiers: "total=1.5" },
              ],
            });
            return () => {};
          },          
          eventMarkets: (args, callback) => {
            console.log('eventMarkets:', args);
            callback(undefined, {              
              event: {
                id: args.selection.event,
                type: 'uf'
              },
              markets: [
                {
                  id: "1",
                  name: "1x2",
                  status: "active",
                  outcomes: [
                    {
                      id: "1",
                      name: "Home",
                      status: "active",
                      odds: { type: 'decimal', value: "1.85" },
                    },
                    {
                      id: "X",
                      name: "Draw",
                      status: "active",
                      odds: { type: 'decimal', value: "3.40" },
                    },
                    {
                      id: "2",
                      name: "Away",
                      status: "active",
                      odds: { type: 'decimal', value: "4.20" },
                    },
                  ],
                },
                {
                  id: "18",
                  name: "Total Goals",
                  specifiers: "total=1.5",
                  status: "active",
                  outcomes: [
                    {
                      id: "12",
                      name: "Over 2.5",
                      status: "active",
                      odds: { type: 'decimal', value: "1.95" },
                    },
                    {
                      id: "13",
                      name: "Under 2.5",
                      status: "active",
                      odds: { type: 'decimal', value: "1.85" },
                    },
                  ],
                },
              ],
            });
            return () => {};
          },
          matchEventSuggestedSelection: (args, callback) => {
            console.log('matchEventSuggestedSelection: ',args);
            callback(undefined, {
              selections: [
                {
                  type: "uf",
                  event: args.event.event,
                  market: "1",
                },
              ],
            });
            return () => {};
        },
          tickets: (args, callback) => {
            console.log(args);
            callback(undefined, {
              bets: [singleBet, multiBet1]
            });
            return () => {};
          },
        },
      };
    
      // Register adapter before adding widgets
      SIR("registerAdapter", adapter);

      // Add Virtual Stadium widget
      SIR('addWidget', '#sr-widget', 'virtualstadium', {
          jwt: '<your-jwt-token>', // TODO: Replace <your-jwt-token> with real JWT token
          channelId: '<your-channel-id>', // TODO: Replace <your-channel-id> with real channel ID
          enableFlashBet: true,
          enableBetInsights: true,
          testMarkets: '1|18;total=0.5,total=1.5' // ONLY FOR TESTING PURPOSES
      });
    </script>
  </head>
  <body>    
    <div class="widgets">
      <!-- Widget container -->
      <div id="sr-widget">Widget will load here...</div>
    </div>
  </body>
</html>
```

***

## Next Steps

- **Not yet integrated the widget?** → Start with [Virtual Stadium Quick Start](https://apidocs.sportradar.com/resources/virtual-stadium/docs/web/widgets/virtualStadium/quickStart.md) for complete widget setup
