> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ticktock.bet/llms.txt
> Use this file to discover all available pages before exploring further.

# Scopes & Bundles

> How granular permissions decide which fields your key sees

## Why scopes

Same URL, different field set per caller. A bookmaker on `cs2.odds.basic` calling `GET /cs2/v1/matches/{id}` gets the markets and rounds. A stats consumer on `cs2.stats` calling the same URL gets the stats but no markets. An internal operator calling it gets everything plus operational fields. **One URL, one serializer, scope-filtered output.**

Use [`/v1/whoami`](/api-reference/meta/whoami) to introspect what your key currently grants.

## Bundles

Bundles are issuance shorthand — pick one (or several) at key creation, the system expands them to a flat scope list. There is no "bundle" entity in the database — just the resulting scope set on your key.

| Bundle                      | Use case                                       | Includes                                                                                                   |
| --------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `cs2.odds.basic`            | Bookmakers consuming odds + settlements        | matches list/read/detail, markets read, settlements, WS for matches & markets, tournaments, teams, players |
| `cs2.odds.scoreboard-addon` | Add-on to `odds.basic` for live scoreboard     | live HP/money/weapon scoreboard + WS scoreboard frames (front-running risk; priced separately)             |
| `cs2.stats`                 | Stats-only consumers                           | matches list/read/detail, tournaments, teams, players, maps. No odds, no live frames.                      |
| `cs2.scoretock`             | Public-portal partners (finished matches only) | matches list/read/detail, teams/players/tournaments read                                                   |
| `cs2.operator`              | Internal services and our own UIs              | every `cs2:*:*` scope. Issued only to internal systems.                                                    |

## Granular scopes

Format: `cs2:{resource}:{action}`. Keys can also be issued with raw scopes a la carte (e.g. add `cs2:matches:scoreboard` to a stats key on request).

`cs2` and `soccer` are the live public sport namespaces today. Treat
[`GET /v1/sports`](/api-reference/meta/list-sports) as the source of truth
before using any future sport namespace. Dota 2 scope strings exist only as
internal groundwork until the Dota router is deliberately mounted.

| Scope                                           | Grants                                                                                            |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `cs2:matches:list`                              | Match list — basic shape (id, teams, event, status, scheduled\_at, score)                         |
| `cs2:matches:read`                              | Single-match read — same shape as list, scoped to one match                                       |
| `cs2:matches:detail`                            | Adds rounds, events, timeline, demo URL, streams (with VOD anchors), per-player stats             |
| `cs2:matches:scoreboard`                        | Adds live scoreboard (HP/money/weapons). Front-running risk — gated explicitly                    |
| `cs2:matches:internals`                         | Operator-only: integration IDs, `data_delay_seconds`, `suspended`, `booking_status`, `source_url` |
| `cs2:markets:read`                              | Active market offers, odds, suspend status                                                        |
| `cs2:markets:settlements`                       | Settlement audit log                                                                              |
| `cs2:tournaments:list` / `cs2:tournaments:read` | Tournament directory and detail                                                                   |
| `cs2:teams:list` / `cs2:teams:read`             | Team directory, profile, form, h2h                                                                |
| `cs2:players:list` / `cs2:players:read`         | Player directory and profile                                                                      |
| `cs2:maps:read`                                 | Global map analytics + active pool                                                                |
| `cs2:stream:matches`                            | WS frames: `match.*`, `round.*`, `event.*`                                                        |
| `cs2:stream:markets`                            | WS frames: `market.*`, `bet_settlement`                                                           |
| `cs2:stream:scoreboard`                         | WS frames: `scoreboard.frame`                                                                     |

## Scope → fields

The same URL emits different fields depending on which scopes you have. Fields outside your scope set are simply absent from the response — not nulled, just not emitted.

### Example: `GET /cs2/v1/matches/{id}`

| Field group                                                                         | Required scope                           |
| ----------------------------------------------------------------------------------- | ---------------------------------------- |
| `id`, `team_a`, `team_b`, `event`, `bo_type`, `status`, `scheduled_at`, `score_a/b` | `cs2:matches:list` or `cs2:matches:read` |
| `started_at`, `finished_at`, `maps[]`                                               | `cs2:matches:read`                       |
| `rounds[]`, `events[]`, `streams[]`, `demo_url`, `player_stats[]`                   | `cs2:matches:detail`                     |
| `markets[]`                                                                         | `cs2:markets:read`                       |
| `scoreboard`                                                                        | `cs2:matches:scoreboard`                 |
| `uof_match_id`, `data_delay_seconds`, `suspended`, …                                | `cs2:matches:internals`                  |

The same logic applies to `Round`, `Stream`, `Market`, and other shapes. Detailed mappings live alongside the corresponding endpoint pages in the [API Reference](/api-reference/introduction).

## Content negotiation

Every REST endpoint serves both JSON (default) and XML. Pass `Accept: application/xml` and the response body comes back as XML on the same URL. The seven AMQP-feed endpoints additionally expose a `.xml` sibling URL for clients whose HTTP infrastructure prefers a distinct cache key per format.

### Two XML dialects

| Dialect                                                                      | Endpoints                                                                                                     | Schema                                                                                                                  |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Feed XML** (hand-tuned, byte-compatible with [AMQP feed](/odds-feed/amqp)) | The seven endpoints below                                                                                     | UOF-style envelopes. Stable wire format.                                                                                |
| **Stats XML** (mechanical JSON→XML)                                          | Everything else (teams, players, tournaments, maps, per-match analytics, settlements, search, sports catalog) | Predictable: dict → element with attribute children; list → wrapper with singularised item tags; null → element absent. |

### Feed XML endpoints

These use hand-tuned envelopes that match the AMQP feed byte-for-byte — useful for clients that already have an UOF-style XML pipeline.

| Endpoint                           | XML root element                                         |
| ---------------------------------- | -------------------------------------------------------- |
| `GET /v1/alive`                    | `<alive>`                                                |
| `GET /v1/whoami`                   | `<bookmaker_details>`                                    |
| `GET /cs2/v1/matches`              | `<schedule>`                                             |
| `GET /cs2/v1/matches/{id}`         | `<fixtures_fixture>` (compact, not the rich JSON detail) |
| `GET /cs2/v1/matches/{id}/summary` | `<match_summary>`                                        |
| `GET /cs2/v1/matches/{id}/markets` | `<odds_change>` over active B2B-feed offers              |
| `GET /cs2/v1/markets/descriptions` | `<market_descriptions>`                                  |

### Stats XML

Every other endpoint produces a mechanical XML rendering of the JSON shape. Example: `GET /cs2/v1/teams/11712` with `Accept: application/xml` returns:

```xml theme={null}
<team>
  <data hltv_team_id="11712" name="Sashi" world_ranking="42" country_code="DK" rating="1.04">
    <map_stats>
      <map_stat map_name="de_mirage" matches_played="18" win_pct="0.55"/>
      <map_stat map_name="de_inferno" matches_played="12" win_pct="0.50"/>
    </map_stats>
  </data>
</team>
```

Rules: scalars become attributes, nested dicts become child elements, lists become wrapper elements with singularised item tags, `null` is elided. There is no XSD for this dialect today — consumers parse it via XPath / element-tree.

<Tip>
  Pass the `Accept: application/xml` header on any GET, or use the `.xml` URL form on the seven feed endpoints. Errors (4xx/5xx) keep the JSON `{"detail": "..."}` shape regardless of `Accept` so error parsing stays uniform.
</Tip>

WebSocket frames are JSON only.
