# Cue Sports API

Base URL:

```text
https://api.wsbkdata.pro
```

All `/api/cue-sports` endpoints require an API key.

```http
X-API-Key: your_api_key
```

## Endpoints

```http
GET /api/cue-sports/admin/counts
GET /api/cue-sports/assets/{asset_id}
GET /api/cue-sports/disciplines
GET /api/cue-sports/tournaments?discipline=heyball&season=2026
GET /api/cue-sports/tournaments/{tournament_id}
GET /api/cue-sports/tournaments/{tournament_id}/matches
GET /api/cue-sports/tournaments/{tournament_id}/entries
GET /api/cue-sports/tournaments/{tournament_id}/bracket
GET /api/cue-sports/matches?tournament_id=...
GET /api/cue-sports/matches/{match_id}/frames
GET /api/cue-sports/live-scores?tournament_id=...
GET /api/cue-sports/players?name=...
GET /api/cue-sports/players/{player_id}
GET /api/cue-sports/players/{player_id}/matches
GET /api/cue-sports/rankings?discipline=heyball&source=wpa
GET /api/cue-sports/rankings/{ranking_id}/rows
GET /api/cue-sports/news?source=joy
GET /api/community/chat/messages?room=general&limit=100
POST /api/community/chat/messages
```

## Tournament Shape

```json
{
  "id": "mongo_id",
  "source": "joy",
  "source_tournament_id": "stable_external_id",
  "source_url": "https://www.joybilliard.com/...",
  "name": "JOY Heyball Masters #1",
  "discipline": "heyball",
  "tour_id": "joy-heyball-masters",
  "organizer": "JOY Billiards",
  "season": 2026,
  "start_at": "2026-01-10T00:00:00+00:00",
  "end_at": "2026-01-20T00:00:00+00:00",
  "venue": "Qinhuangdao Olympic Sports Center Gymnasium",
  "city": "Qinhuangdao",
  "country": "CHN",
  "prize_money": 10800000,
  "prize_currency": "CNY",
  "participant_count": 64,
  "format": "double_elimination",
  "race_to": 7,
  "handicap_enabled": false,
  "status": "scheduled",
  "raw": {},
  "scraped_at": "2026-06-02T00:00:00+00:00"
}
```

## Match Shape

```json
{
  "id": "mongo_id",
  "source": "cuescore",
  "source_match_id": "external_match_id",
  "source_tournament_id": "external_tournament_id",
  "tournament_id": "mongo_tournament_id",
  "round_name": "Quarter Final",
  "match_no": "A001",
  "scheduled_at": "2026-06-02T12:00:00+00:00",
  "table_no": "T08",
  "player_a_id": "player_a",
  "player_b_id": "player_b",
  "player_a_name": "Player A",
  "player_b_name": "Player B",
  "score_a": 7,
  "score_b": 4,
  "race_to": 7,
  "winner_id": "player_a",
  "status": "finished",
  "live_url": "https://...",
  "vod_url": "https://..."
}
```

## Player Detail And History

```http
GET /api/cue-sports/players/{player_id}
GET /api/cue-sports/players/{player_id}/matches?limit=30
```

`player_id` accepts either the Mongo `id` or the source player id. The player response includes public profile fields such as `name`, `country`, `region`, `source_url`, `raw`, `avatar_url`, and `avatar_asset_id` when available. The history endpoint queries `cue_matches` by player ids and player names, so it can return historical CueScore matches even when only the source player id is present in the match row.

## Community Chat

```http
GET /api/community/chat/messages?room=general&limit=100
POST /api/community/chat/messages
```

Post body:

```json
{
  "room": "general",
  "author": "Heyball Fan",
  "content": "Match discussion"
}
```

Messages are stored in `community_chat_messages` and sorted by `created_at`.

## Crawler Commands

```bash
python -m crawler.cue_jobs seed-disciplines
python -m crawler.cue_jobs scrape-wpa
python -m crawler.cue_jobs scrape-joy
python -m crawler.cue_jobs scrape-epbf
python -m crawler.cue_jobs scrape-cbsa
python -m crawler.cue_jobs scrape-all
python -m crawler.cue_jobs import-cuescore-tournament --tournament-id 12345
python -m crawler.cue_jobs import-cuescore-batch --tournament-ids 78608725,78609847
```

Use `--dry-run --json` to inspect parsed data before writing to MongoDB.

CueScore imports now populate:

- `cue_players`
- `cue_entries`
- `cue_matches`
- `cue_brackets`
- `cue_live_scores`
- `cue_rankings`
- `cue_ranking_rows`
- `cue_match_frames`
- `cue_assets`

Player avatars are downloaded from public `cue_players.avatar_url` into `cue_assets`. After download, the player document is updated with `avatar_asset_id`, which can be fetched through `GET /api/cue-sports/assets/{asset_id}`.

When CueScore does not expose true frame-by-frame rows, `cue_match_frames` stores one synthetic aggregate row per match with `frame_number: 0`, `synthetic: true`, and `source_detail: "aggregate_only"`. This is not a fabricated frame sequence; it only preserves the match score as an aggregate frame summary.
