Skip to content

API Endpoints

All API endpoints are prefixed with /api/.

Authentication

Authentication is handled via Discord OAuth through django-social-auth.

Test Endpoints

For E2E testing, the following endpoints are available:

Endpoint Method Description
/api/tests/login-user/ POST Login as regular user
/api/tests/login-staff/ POST Login as staff user
/api/tests/login-admin/ POST Login as admin user
/api/tests/login-as/ POST Login as any user by PK
/api/tests/tournament-by-key/{key}/ GET Get tournament by test config key

Test Only

These endpoints are only available in test/development environments.

Login As User

POST /api/tests/login-as/
Content-Type: application/json

{"user_pk": 123}

Returns user details and sets session cookies.

Users

Method Endpoint Description
GET /api/users/ List users
GET /api/users/{id}/ Get user details
PUT /api/users/{id}/ Update user

Tournaments

Method Endpoint Description
GET /api/tournaments/ List tournaments
POST /api/tournaments/ Create tournament
GET /api/tournaments/{id}/ Get tournament
PUT /api/tournaments/{id}/ Update tournament
DELETE /api/tournaments/{id}/ Delete tournament

Teams

Method Endpoint Description
GET /api/teams/ List teams
POST /api/teams/ Create team
GET /api/teams/{id}/ Get team

Games

Method Endpoint Description
GET /api/games/ List games
POST /api/games/ Create game
GET /api/games/{id}/ Get game

Steam / League Stats

Endpoints for Steam integration and league statistics.

Leaderboard

Method Endpoint Description
GET /api/steam/leaderboard/ Paginated league leaderboard

Query Parameters:

Parameter Type Default Description
page int 1 Page number
page_size int 20 Results per page
sort_by string league_mmr Sort field: league_mmr, games_played, win_rate, avg_kda
order string desc Sort order: asc, desc

Response:

{
  "count": 50,
  "next": "/api/steam/leaderboard/?page=2",
  "previous": null,
  "results": [
    {
      "user_id": 1,
      "username": "player1",
      "avatar": "https://...",
      "league_mmr": 3250,
      "mmr_adjustment": 150,
      "games_played": 25,
      "wins": 15,
      "losses": 10,
      "win_rate": 0.6,
      "avg_kda": 3.5,
      "avg_gpm": 450
    }
  ]
}

League Stats

Method Endpoint Description Auth
GET /api/steam/league-stats/{user_id}/ Get user's league stats No
GET /api/steam/league-stats/me/ Get current user's stats Yes

Response:

{
  "user_id": 1,
  "username": "player1",
  "league_id": 1,
  "games_played": 25,
  "wins": 15,
  "losses": 10,
  "win_rate": 0.6,
  "avg_kills": 8.5,
  "avg_deaths": 4.2,
  "avg_assists": 12.3,
  "avg_kda": 3.5,
  "avg_gpm": 450,
  "avg_xpm": 520,
  "league_mmr": 3250
}

Organizations

Method Endpoint Description Auth
GET /api/organizations/ List organizations No
POST /api/organizations/ Create organization Admin
GET /api/organizations/{id}/ Get organization details No
PUT /api/organizations/{id}/ Update organization Org Admin
DELETE /api/organizations/{id}/ Delete organization Admin

Query Parameters (list):

Parameter Type Description
user int Filter by user membership (admin or staff)

Response (detail):

{
  "id": 1,
  "name": "DTX Gaming",
  "description": "Dota 2 gaming organization",
  "logo": "https://...",
  "admins": [{ "id": 1, "username": "admin" }],
  "staff": [{ "id": 2, "username": "staff" }],
  "leagues": [{ "id": 1, "name": "Spring League" }],
  "league_count": 2,
  "tournament_count": 5
}

Leagues

Method Endpoint Description Auth
GET /api/leagues/ List leagues No
POST /api/leagues/ Create league Yes
GET /api/leagues/{id}/ Get league details No
PUT /api/leagues/{id}/ Update league League Admin
DELETE /api/leagues/{id}/ Delete league League Admin

Query Parameters (list):

Parameter Type Description
organization int Filter by organization ID

Response (detail):

{
  "id": 1,
  "name": "Spring League 2024",
  "organization": { "id": 1, "name": "DTX Gaming" },
  "admins": [{ "id": 1, "username": "admin" }],
  "staff": [{ "id": 2, "username": "staff" }],
  "tournaments": [{ "id": 1, "name": "Week 1" }],
  "seasons": [{ "id": 1, "name": "Season 1", "status": "active" }],
  "tournament_count": 4,
  "season_count": 1
}

Seasons

Seasons organize tournaments within a league. A season contains a player pool (via signups), teams, and links to tournaments.

Planned Feature

Seasons are a planned feature. See Team Management & Seasons for the full design.

Season CRUD

Method Endpoint Description Auth
GET /api/leagues/{id}/seasons/ List seasons for a league No
POST /api/leagues/{id}/seasons/ Create a season League Admin
GET /api/leagues/{id}/seasons/{season_id}/ Get season details No
PUT /api/leagues/{id}/seasons/{season_id}/ Update season League Admin
PATCH /api/leagues/{id}/seasons/{season_id}/ Partial update season League Admin
DELETE /api/leagues/{id}/seasons/{season_id}/ Delete season League Admin

Request Body (create/update):

{
  "name": "Season 3",
  "number": 3,
  "status": "upcoming",
  "start_date": "2026-03-01T00:00:00Z",
  "end_date": "2026-06-01T00:00:00Z",
  "signup_deadline": "2026-02-28T23:59:59Z",
  "timezone": "America/New_York"
}

Response (detail):

{
  "id": 1,
  "league": 1,
  "name": "Season 3",
  "number": 3,
  "status": "active",
  "start_date": "2026-03-01T00:00:00Z",
  "end_date": "2026-06-01T00:00:00Z",
  "signup_deadline": "2026-02-28T23:59:59Z",
  "timezone": "America/New_York",
  "member_count": 24,
  "team_count": 4,
  "tournament_count": 3,
  "created_at": "2026-02-01T00:00:00Z",
  "updated_at": "2026-02-15T00:00:00Z"
}

Season Signups

Method Endpoint Description Auth
GET /api/seasons/{id}/signups/ List all signups League Staff
GET /api/seasons/{id}/signups/me/ Get current user's signup LeagueUser
POST /api/seasons/{id}/signups/ Sign up for season LeagueUser
DELETE /api/seasons/{id}/signups/{signup_id}/ Withdraw signup Owner
POST /api/seasons/{id}/signups/{signup_id}/accept/ Accept signup League Admin
POST /api/seasons/{id}/signups/{signup_id}/reject/ Reject signup League Admin
POST /api/seasons/{id}/signups/bulk-accept/ Accept multiple signups League Admin

Request Body (sign up):

{
  "note": "Prefer to play support"
}

Request Body (bulk accept):

{
  "signup_ids": [1, 2, 3, 5]
}

Response (signup detail):

{
  "id": 1,
  "season": 1,
  "league_user": {
    "id": 5,
    "user": { "id": 10, "username": "player1", "avatar": "https://..." },
    "mmr": 4500
  },
  "status": "pending",
  "note": "Prefer to play support",
  "signed_up_at": "2026-02-10T14:30:00Z",
  "reviewed_by": null,
  "reviewed_at": null
}

Season Members

Members are derived from accepted signups. These endpoints provide convenience access.

Method Endpoint Description Auth
GET /api/seasons/{id}/members/ List accepted members No
POST /api/seasons/{id}/members/ Add member directly (auto-accepts signup) League Admin
DELETE /api/seasons/{id}/members/{league_user_id}/ Remove member League Admin

Request Body (direct add):

{
  "league_user_id": 5
}

Response (member list item):

{
  "id": 5,
  "user": { "id": 10, "username": "player1", "avatar": "https://..." },
  "mmr": 4500,
  "signed_up_at": "2026-02-10T14:30:00Z"
}

Season Teams

Method Endpoint Description Auth
GET /api/seasons/{id}/teams/ List season teams No
POST /api/seasons/{id}/teams/ Create team League Admin
GET /api/seasons/{id}/teams/{team_id}/ Get team details No
PUT /api/seasons/{id}/teams/{team_id}/ Update team League Admin
PATCH /api/seasons/{id}/teams/{team_id}/ Partial update League Admin
DELETE /api/seasons/{id}/teams/{team_id}/ Delete team League Admin
POST /api/seasons/{id}/teams/{team_id}/logo/ Upload team logo League Admin

Request Body (create/update):

{
  "name": "Team Phoenix",
  "captain_id": 5,
  "deputy_captain_id": 8,
  "member_ids": [5, 8, 12, 15, 20]
}

Request Body (logo upload):

Multipart form data with logo file field.

Response (team detail):

{
  "id": 1,
  "season": 1,
  "name": "Team Phoenix",
  "captain": {
    "id": 5,
    "user": { "id": 10, "username": "captain1", "avatar": "https://..." },
    "mmr": 5000
  },
  "deputy_captain": {
    "id": 8,
    "user": { "id": 14, "username": "deputy1", "avatar": "https://..." },
    "mmr": 4200
  },
  "members": [
    { "id": 5, "user": { "id": 10, "username": "captain1" }, "mmr": 5000 },
    { "id": 8, "user": { "id": 14, "username": "deputy1" }, "mmr": 4200 },
    { "id": 12, "user": { "id": 18, "username": "player2" }, "mmr": 3800 },
    { "id": 15, "user": { "id": 21, "username": "player3" }, "mmr": 3500 },
    { "id": 20, "user": { "id": 26, "username": "player4" }, "mmr": 3200 }
  ],
  "logo": "/media/season_teams/team-phoenix.png",
  "created_at": "2026-02-15T00:00:00Z",
  "updated_at": "2026-02-15T00:00:00Z"
}

Tournament Team Import

Import SeasonTeams into a tournament as Tournament Teams.

Method Endpoint Description Auth
POST /api/tournaments/{id}/import-season-teams/ Import teams from season Tournament Staff

Request Body:

{
  "season_team_ids": [1, 2, 3, 4]
}

Omit season_team_ids to import all teams from the tournament's linked season.

Response:

{
  "imported": 4,
  "teams": [
    { "id": 50, "name": "Team Phoenix", "season_team_source": 1 },
    { "id": 51, "name": "Team Dragon", "season_team_source": 2 },
    { "id": 52, "name": "Team Hydra", "season_team_source": 3 },
    { "id": 53, "name": "Team Kraken", "season_team_source": 4 }
  ]
}

Import Behavior

  • Tournament must have a linked season (tournament.season is set)
  • Import creates independent copies — changes to SeasonTeams after import do not propagate
  • Re-importing when teams already exist will error unless existing teams are removed first
  • LeagueUser references are resolved to CustomUser for Tournament Team compatibility

CSV Import

Bulk-import users into organizations or tournaments via pre-parsed CSV data.

Feature Documentation

See CSV Import for the full design.

Organization CSV Import

Method Endpoint Description Auth
POST /api/organizations/{id}/import-csv/ Bulk-import users to org Org Staff

Request Body:

{
  "rows": [
    {
      "steam_friend_id": "76561198012345678",
      "discord_id": "123456789012345678",
      "base_mmr": 5000
    }
  ]
}

Response:

{
  "summary": {
    "added": 2,
    "skipped": 1,
    "created": 1,
    "errors": 0
  },
  "results": [
    {
      "row": 1,
      "status": "added",
      "user": { "pk": 10, "username": "player1", "avatar": "..." },
      "created": false
    },
    {
      "row": 2,
      "status": "added",
      "user": { "pk": 50, "username": "steam_76561198099999999" },
      "created": true,
      "warning": "Steam ID already linked to different Discord ID — provided Discord ID was ignored"
    },
    {
      "row": 3,
      "status": "skipped",
      "reason": "Already a member",
      "user": { "pk": 5, "username": "existing_member" }
    }
  ]
}
Summary Field Description
added Users successfully added to the organization
skipped Users already in the organization
created New stub users created (subset of added)
errors Rows that failed to process

Tournament CSV Import

Method Endpoint Description Auth
POST /api/tournaments/{id}/import-csv/ Bulk-import users to tournament Org Staff

Same request/response format as organization import. Additional per-row fields:

Field Type Description
team_name string Optional team assignment (request)
team string Team name assigned (response)

Tournament Import Behavior

  • Creates OrgUser in parent organization if user is not already a member
  • Adds user to tournament.users M2M
  • Creates Team by name if team_name provided and team doesn't exist
  • Groups rows with same team_name into the same team
  • Maximum 500 rows per import

CSV Import Error Responses

Status Condition
400 rows is not a list, or exceeds 500 row limit
403 User lacks org staff access
404 Organization or tournament not found

Auction House

Real-time auction-based team formation with salary cap budgets and nomination rotation.

Planned Feature

Auction House is a planned feature. See Auction House for the full design.

Auction CRUD

Method Endpoint Description Auth
POST /api/tournaments/{id}/create-auction/ Create auction for tournament Org Staff
POST /api/seasons/{id}/create-auction/ Create auction for season League Admin
GET /api/auctions/{id}/ Get auction state (teams, budgets, lots, current phase) No
POST /api/auctions/{id}/pause/ Admin pause auction Org Staff
POST /api/auctions/{id}/resume/ Admin resume auction Org Staff
POST /api/auctions/{id}/abort/ Abort auction Org Staff
GET /api/auctions/{id}/results/ Final results and spending breakdown No

Response (auction state):

{
  "id": 1,
  "status": "bidding",
  "tournament": 5,
  "season": null,
  "config": { "budget": 2000, "min_bid": 5, "bid_timer": 20, "..." : "..." },
  "teams": [
    { "team_id": 1, "name": "Team Phoenix", "budget_remaining": 1200, "players_won": 2, "pause_count": 0 }
  ],
  "current_lot": {
    "lot_number": 5,
    "player": { "pk": 10, "username": "mid_player" },
    "nominator": "Team Dragon",
    "highest_bid": 350,
    "highest_bidder": "Team Phoenix",
    "time_remaining": 8
  },
  "remaining_pool_count": 12
}

Response (results):

{
  "auction_id": 1,
  "status": "completed",
  "teams": [
    {
      "team_id": 1,
      "team_name": "Team Phoenix",
      "budget_start": 2000,
      "budget_spent": 1847,
      "players": [
        { "user": { "pk": 10, "username": "star_player" }, "cost": 800, "lot_number": 3 },
        { "user": { "pk": 30, "username": "pos4" }, "cost": 0, "lot_number": null, "fallback": true }
      ]
    }
  ],
  "lots": [
    { "lot_number": 1, "player": { "pk": 5, "username": "mid_player" }, "winning_team": "Team Dragon", "winning_bid": 650, "bid_count": 8 }
  ]
}

AuctionConfig (Cascading)

Configuration inherits down: Organization → League → Season → Tournament. Null fields inherit from parent.

Method Endpoint Description Auth
GET /api/organizations/{id}/auction-config/ Get org-level config No
PUT /api/organizations/{id}/auction-config/ Set org-level config Org Admin
GET /api/leagues/{id}/auction-config/ Get league-level config No
PUT /api/leagues/{id}/auction-config/ Set league-level config League Admin
GET /api/seasons/{id}/auction-config/ Get season-level config No
PUT /api/seasons/{id}/auction-config/ Set season-level config League Admin
GET /api/tournaments/{id}/auction-config/ Get tournament-level config No
PUT /api/tournaments/{id}/auction-config/ Set tournament-level config Org Staff
GET /api/tournaments/{id}/auction-config/resolved/ Get fully merged config No

Request/Response:

{
  "budget": 2000,
  "min_bid": 5,
  "bid_timer": null,
  "bid_extension_timer": null,
  "nomination_timer": null,
  "unsold_behavior": null,
  "max_roster_size": null,
  "fallback_mode": null,
  "max_pauses_per_captain": null,
  "reconnect_timeout": null
}

Auction WebSocket

Endpoint: /api/auction/{auction_id}/

Real-time bidding via Daphne WebSocket (same pattern as HeroDraft).

Client Messages:

Message Payload Description
start Admin triggers auction start
nominate { player_id: int } Captain nominates a player
bid { amount: int } Place bid on current lot

Server Events:

Event Description
auction_started Auction state, budgets, pool
nomination_turn Captain ID, timer
nomination Lot details, player, opening bid
bid_placed Team, amount, time remaining
lot_sold Winner, price, updated budgets
lot_unsold Auto-assigned to nominator at min_bid
fallback_pick Team, player, team MMR
captain_disconnected / captain_reconnected Team ID, pause count
auction_paused / auction_resumed Timestamp, reason
auction_completed Final results

Drafts (Player Draft)

Method Endpoint Description
GET /api/drafts/ List drafts
GET /api/drafts/{id}/ Get draft details
PUT /api/drafts/{id}/ Update draft (e.g., change style)
POST /api/tournaments/init-draft Initialize draft for tournament
POST /api/tournaments/pick_player Pick player for draft round

Draft Pick (Shuffle Draft)

Method Endpoint Description
POST /api/tournaments/{id}/draft/pick/ Make a draft pick

Request Body:

{
  "player_id": 123
}

Response (Shuffle Draft with tie):

{
  "success": true,
  "tournament": { ... },
  "next_pick": {
    "captain_id": 5,
    "team_id": 2,
    "team_name": "Team Beta",
    "team_mmr": 15400
  },
  "tie_resolution": {
    "tied_teams": [
      {"id": 1, "name": "Team Alpha", "mmr": 15400},
      {"id": 2, "name": "Team Beta", "mmr": 15400}
    ],
    "roll_rounds": [
      [{"team_id": 1, "roll": 4}, {"team_id": 2, "roll": 6}]
    ],
    "winner_id": 2
  }
}

Shuffle Draft

The next_pick and tie_resolution fields only appear for shuffle draft style. For snake/normal drafts, pick order is predetermined.

Pick Player (Captain Draft)

Allows staff or the current round's captain to pick a player:

POST /api/tournaments/pick_player
Content-Type: application/json

{
  "draft_round_pk": 123,
  "user_pk": 456
}

Captain Permissions

Captains can pick players during their turn. Staff can pick for any captain.

HeroDraft (Captain's Mode)

Hero draft endpoints for Dota 2 Captain's Mode pick/ban phase. All endpoints require authentication.

WebSocket Support

HeroDraft also supports real-time updates via WebSocket at /api/herodraft/{draft_pk}/. The WebSocket broadcasts events like draft_created, captain_ready, roll_result, choice_made, hero_selected, and draft_abandoned.

Create HeroDraft

Method Endpoint Description
POST /api/games/{game_pk}/create-herodraft/ Create a HeroDraft for a game

Creates a new hero draft for a game. Returns existing draft if one already exists.

Requirements:

  • Game must have both radiant_team and dire_team assigned
  • Both teams must have captains assigned

Response (201 Created / 200 OK if exists):

{
  "id": 1,
  "game": 5,
  "state": "waiting_for_captains",
  "draft_teams": [
    {
      "id": 1,
      "tournament_team": { ... },
      "is_ready": false,
      "is_connected": false,
      "is_first_pick": null,
      "is_radiant": null
    },
    { ... }
  ],
  "rounds": [],
  "roll_winner": null
}

Get HeroDraft

Method Endpoint Description
GET /api/herodraft/{draft_pk}/ Get HeroDraft details

Returns the current state of a hero draft.

Set Captain Ready

Method Endpoint Description
POST /api/herodraft/{draft_pk}/set-ready/ Mark captain as ready

Marks the authenticated user's team as ready. When both captains are ready, the draft transitions to "rolling" state.

Requirements:

  • Draft must be in waiting_for_captains state
  • User must be a captain in this draft

Errors:

  • 403: User is not a captain in this draft
  • 400: Invalid state for this operation

Trigger Roll

Method Endpoint Description
POST /api/herodraft/{draft_pk}/trigger-roll/ Trigger dice roll

Triggers the coin flip to determine which team chooses first (pick order or side).

Requirements:

  • Draft must be in rolling state
  • User must be a captain in this draft

Response: Returns updated draft data with roll_winner set.

Submit Choice

Method Endpoint Description
POST /api/herodraft/{draft_pk}/submit-choice/ Submit first pick choice

Submit a choice for pick order or side. The roll winner chooses first, then the other team gets the remaining choice.

Request Body:

{
  "choice_type": "pick_order",
  "value": "first"
}

Field Type Values
choice_type string "pick_order" or "side"
value string For pick_order: "first" or "second". For side: "radiant" or "dire"

Requirements:

  • Draft must be in choosing state
  • User must be a captain in this draft
  • Roll winner must choose first
  • Choice must not already be made

Submit Pick

Method Endpoint Description
POST /api/herodraft/{draft_pk}/submit-pick/ Submit hero pick/ban

Submit a hero pick or ban for the current round.

Request Body:

{
  "hero_id": 1
}

Requirements:

  • Draft must be in drafting state
  • User must be a captain in this draft
  • Must be the user's turn to pick/ban
  • Hero must be available (not already picked or banned)

Errors:

  • 403: User is not a captain or not their turn
  • 400: Invalid state, hero already picked, or invalid hero

List Events

Method Endpoint Description
GET /api/herodraft/{draft_pk}/list-events/ List draft events

Returns all events for a hero draft (for audit trail and replay).

Response:

[
  {
    "id": 1,
    "event_type": "captain_connected",
    "draft_team": null,
    "metadata": { "created_by": 1 },
    "created_at": "2024-01-01T00:00:00Z"
  },
  {
    "id": 2,
    "event_type": "captain_ready",
    "draft_team": 1,
    "metadata": { "captain_id": 1 },
    "created_at": "2024-01-01T00:00:05Z"
  }
]

List Available Heroes

Method Endpoint Description
GET /api/herodraft/{draft_pk}/list-available-heroes/ List available heroes

Returns all hero IDs that are still available (not picked or banned).

Response:

{
  "available_heroes": [1, 2, 3, 5, 7, 8, ...]
}

Abandon Draft

Method Endpoint Description
POST /api/herodraft/{draft_pk}/abandon/ Abandon draft

Abandon a hero draft. Can be called by a captain in the draft or an admin.

Requirements:

  • Draft must not be in completed or abandoned state
  • User must be a captain in this draft OR an admin

Errors:

  • 403: User not authorized to abandon this draft
  • 400: Draft already completed or abandoned

Response Format

All responses follow this format:

{
  "id": 1,
  "field": "value",
  "created_at": "2024-01-01T00:00:00Z",
  "updated_at": "2024-01-01T00:00:00Z"
}

Error Responses

{
  "detail": "Error message"
}
Status Description
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
500 Server Error