MatchExec API Reference

REST API for the MatchExec match and tournament management system. Last Updated for 0.7.0 Release

Base URL: All endpoints are relative to the application root (e.g., http://localhost:3000).

Content-Type: application/json for all request/response bodies unless noted otherwise.


Conventions

Request Format

  • Request bodies use application/json unless the endpoint accepts file uploads (multipart/form-data).
  • Path parameters are shown as {paramName}.
  • Fields marked (required) must be present; all others are optional.

Response Format

  • Successful responses return the resource or { "success": true }.
  • Error responses include an "error" string describing the problem.
  • Timestamps are ISO 8601 strings (e.g., "2024-12-31T23:59:59.999Z").
  • Boolean fields from SQLite are converted to native JSON booleans.

Health & Status

GET /api/health

Check whether the application and database are operational.

Response

{
  "status": "healthy",
  "timestamp": "2024-12-31T23:59:59.999Z",
  "services": {
    "database": "up",
    "web": "up"
  }
}
Status CodeMeaning
200Service is healthy
503Service unavailable — database connection failed

GET /api/stats

Get system-wide statistics.

Response

{
  "totalMatches": 42,
  "totalTournaments": 5,
  "totalSignups": 180
}
Status CodeMeaning
200Success

GET /api/version

Get application version information.

Response

{
  "version": "1.2.3",
  "branch": "main",
  "commitHash": "abc1234",
  "isDev": false
}
Status CodeMeaning
200Success

GET /api/db-status

Get database migration and seeding status. Used by the loading screen UI to track startup progress.

Response

{
  "ready": true,
  "progress": "Seeding complete",
  "timestamp": 1735689599000
}
Status CodeMeaning
200Success

Games

GET /api/games

List all supported games with mode and map counts.

Response

[
  {
    "id": "overwatch-2",
    "name": "Overwatch 2",
    "genre": "Hero Shooter",
    "developer": "Blizzard Entertainment",
    "description": "...",
    "minPlayers": 2,
    "maxPlayers": 12,
    "supportsAllModes": false,
    "iconUrl": "/games/overwatch-2/icon.png",
    "coverUrl": "/games/overwatch-2/cover.jpg",
    "color": "#F99E1A",
    "mapCount": 24,
    "modeCount": 4
  }
]
Status CodeMeaning
200Success

GET /api/games/{gameId}

Get details for a specific game.

URL Parameters

ParameterTypeDescription
gameIdstringGame identifier

Response

Same shape as a single item from GET /api/games.

Status CodeMeaning
200Success
404Game not found

GET /api/games/{gameId}/modes

List all game modes for a game.

URL Parameters

ParameterTypeDescription
gameIdstringGame identifier

Response

[
  {
    "id": "competitive",
    "name": "Competitive",
    "description": "Ranked play",
    "team_size": 5,
    "max_players": 10
  }
]
Status CodeMeaning
200Success
404Game not found

GET /api/games/{gameId}/modes/{modeId}

Get a specific game mode.

URL Parameters

ParameterTypeDescription
gameIdstringGame identifier
modeIdstringMode identifier

Response

Single mode object with all fields from the game’s modes.json.

Status CodeMeaning
200Success
404Mode not found in game

GET /api/games/{gameId}/modes/{modeId}/maps

List all maps available for a specific mode.

URL Parameters

ParameterTypeDescription
gameIdstringGame identifier
modeIdstringMode identifier

Response

[
  {
    "id": "king-s-row",
    "name": "King's Row",
    "modeId": "escort",
    "imageUrl": "/games/overwatch-2/maps/kings-row.jpg"
  }
]
Status CodeMeaning
200Success
404Game or mode not found

GET /api/games/{gameId}/maps

List all maps for a game across all modes. CS2 and games with supportsAllModes = true receive deduplicated results with additional fields.

URL Parameters

ParameterTypeDescription
gameIdstringGame identifier

Response

[
  {
    "id": "dust2",
    "name": "Dust II",
    "modeId": "competitive",
    "imageUrl": "/games/cs2/maps/dust2.jpg",
    "location": "Middle East",
    "modeName": "Competitive",
    "modeDescription": "...",
    "supportedModes": "competitive,deathmatch"
  }
]

Notes

  • The supportedModes field is only present for CS2 games.
  • Maps are deduplicated when supportsAllModes = true.
Status CodeMeaning
200Success
404Game not found

Discord Channels

GET /api/channels

List all Discord channels registered in the system.

Response

[
  {
    "id": "1",
    "discord_channel_id": "123456789012345678",
    "channel_name": "#announcements",
    "channel_type": "text",
    "send_announcements": true,
    "send_reminders": false,
    "send_match_start": true,
    "send_signup_updates": false,
    "send_health_alerts": false,
    "last_name_refresh": "2024-12-31T23:59:59.999Z",
    "created_at": "2024-01-01T00:00:00.000Z",
    "updated_at": "2024-12-31T23:59:59.999Z"
  }
]
Status CodeMeaning
200Success

POST /api/channels

Register a new Discord channel.

Request Body

{
  "discord_channel_id": "123456789012345678",
  "channel_type": "text",
  "send_announcements": false,
  "send_reminders": false,
  "send_match_start": false,
  "send_signup_updates": false,
  "send_health_alerts": false
}
FieldTypeRequiredDescription
discord_channel_idstringYesDiscord channel snowflake ID
channel_type"text" | "voice"YesChannel type
send_announcementsbooleanNoReceive match/tournament announcements
send_remindersbooleanNoReceive player reminders
send_match_startbooleanNoReceive match start notifications
send_signup_updatesbooleanNoReceive signup update notifications
send_health_alertsbooleanNoReceive health alert notifications

Response

{
  "success": true,
  "id": "2",
  "message": "Channel created successfully"
}
Status CodeMeaning
200Channel created
400Validation error
409Channel already registered

PUT /api/channels/{channelId}

Update notification settings for a registered channel.

URL Parameters

ParameterTypeDescription
channelIdstringInternal channel ID

Request Body

{
  "send_announcements": true,
  "send_reminders": true,
  "send_match_start": false,
  "send_signup_updates": false,
  "send_health_alerts": false
}

All fields are optional; omitted fields remain unchanged.

Response

{
  "success": true,
  "message": "Channel notification settings updated successfully"
}
Status CodeMeaning
200Updated
400Voice channels cannot have notification settings modified manually
404Channel not found

DELETE /api/channels/{channelId}

Remove a channel registration.

URL Parameters

ParameterTypeDescription
channelIdstringInternal channel ID

Response

{ "success": true, "message": "Channel deleted successfully" }
Status CodeMeaning
200Deleted
404Channel not found

POST /api/channels/refresh-names

Refresh all channel names by querying the Discord API. Channels that no longer exist on Discord are automatically removed from the database.

Response

{
  "success": true,
  "updated_count": 3,
  "removed_count": 1,
  "total_channels": 4,
  "errors": ["Failed to refresh channel 987654321098765432"]
}

Notes

  • errors is only present when some channels failed to refresh.
  • Channels deleted from Discord are removed from the local database.
Status CodeMeaning
200Refresh complete
500Internal error

Settings

GET /api/settings

Get all application settings in a single response.

Response

{
  "discord": {
    "application_id": "123456789012345678",
    "bot_token": "••••••••",
    "guild_id": "123456789012345678",
    "announcement_role_id": "123456789012345678",
    "mention_everyone": false,
    "event_duration_minutes": 60,
    "match_reminder_minutes": 15,
    "player_reminder_minutes": 5,
    "announcer_voice": "default",
    "voice_announcements_enabled": true
  },
  "announcer": {
    "announcer_voice": "default",
    "voice_announcements_enabled": true
  },
  "ui": {
    "auto_refresh_interval_seconds": 30
  },
  "scheduler": {
    "match_check_cron": "0 * * * * *",
    "cleanup_check_cron": "0 0 * * * *",
    "channel_refresh_cron": "0 0 * * * *"
  },
  "voices": [
    {
      "id": "default",
      "name": "Default",
      "path": "/voices/default.mp3"
    }
  ]
}

Notes

  • bot_token is always masked in responses for security.
Status CodeMeaning
200Success

GET /api/settings/discord

Get Discord bot settings only.

Response

Same as the "discord" object from GET /api/settings.

Status CodeMeaning
200Success

PUT /api/settings/discord

Update Discord bot settings. Automatically restarts the Discord bot process after saving.

Request Body

{
  "application_id": "123456789012345678",
  "bot_token": "MTIzNDU2Nzg5...",
  "guild_id": "123456789012345678",
  "announcement_role_id": "123456789012345678",
  "mention_everyone": false,
  "event_duration_minutes": 60,
  "match_reminder_minutes": 15,
  "player_reminder_minutes": 5,
  "announcer_voice": "default",
  "voice_announcements_enabled": true,
  "voice_channel_category_id": "123456789012345678",
  "voice_channel_cleanup_delay_minutes": 5
}

All fields are optional; omitted fields remain unchanged.

Notes

  • Send the full unmasked token when updating bot_token. Do not send the masked "••••••••" value.
  • After saving, the Discord bot process is restarted (via PM2 in dev, pkill in production Docker).

Response

{ "success": true }
Status CodeMeaning
200Updated and bot restarted
500Save failed

GET /api/settings/announcer

Get voice announcer settings.

Response

{
  "announcer_voice": "default",
  "voice_announcements_enabled": true,
  "match_start_delay_seconds": 5
}
Status CodeMeaning
200Success

PUT /api/settings/announcer

Update voice announcer settings.

Request Body

{
  "announcer_voice": "default",
  "voice_announcements_enabled": true,
  "match_start_delay_seconds": 5
}
FieldTypeValidation
announcer_voicestringValid voice ID
voice_announcements_enabledboolean
match_start_delay_secondsnumber0–300 inclusive

Response

{ "success": true }
Status CodeMeaning
200Updated
400match_start_delay_seconds out of range

GET /api/settings/ui

Get UI display settings.

Response

{
  "auto_refresh_interval_seconds": 30
}
Status CodeMeaning
200Success

PUT /api/settings/ui

Update UI display settings.

Request Body

{
  "auto_refresh_interval_seconds": 30
}
FieldTypeRequiredValidation
auto_refresh_interval_secondsnumberYes5–300 inclusive

Response

{ "message": "UI settings updated successfully" }
Status CodeMeaning
200Updated
400Value out of range

GET /api/settings/scheduler

Get cron job schedule settings.

Response

{
  "match_check_cron": "0 * * * * *",
  "cleanup_check_cron": "0 0 * * * *",
  "channel_refresh_cron": "0 0 * * * *"
}
Status CodeMeaning
200Success

PUT /api/settings/scheduler

Update cron job schedules. Cron expressions must use the 6-part format (second minute hour day month weekday).

Request Body

{
  "match_check_cron": "0 * * * * *",
  "cleanup_check_cron": "0 0 * * * *",
  "channel_refresh_cron": "0 0 * * * *"
}

All fields are optional. Any provided expression must have exactly 6 space-separated parts.

Response

Returns the full updated scheduler settings object (same as GET response).

Status CodeMeaning
200Updated
400Invalid cron expression (must have 6 parts)

GET /api/settings/log-level

Get the current application log level.

Response

{
  "log_level": "warning"
}

Valid values: "debug", "info", "warning", "error", "critical".

Status CodeMeaning
200Success

PUT /api/settings/log-level

Update the application log level. Takes effect within 5 seconds.

Request Body

{
  "log_level": "info"
}
FieldTypeRequiredValid Values
log_levelstringYes"debug", "info", "warning", "error", "critical"

Response

{
  "success": true,
  "log_level": "info"
}
Status CodeMeaning
200Updated
400Invalid log level value

Welcome Flow

GET /api/welcome-flow

Check whether the initial setup wizard has been completed.

Response

{
  "isFirstRun": false,
  "completed": true,
  "dbReady": true,
  "metadata": {
    "screens_completed": [1, 2, 3],
    "completion_date": "2024-01-01T00:00:00.000Z",
    "setup_type": "standard"
  }
}
Status CodeMeaning
200Success

PUT /api/welcome-flow

Mark the welcome wizard as completed.

Request Body

{
  "setupType": "standard"
}
FieldTypeRequiredValid Values
setupTypestringYes"pro_mode", "standard"

Response

{ "success": true }

Notes

  • Sets a welcome_flow_completed cookie valid for 30 days.
Status CodeMeaning
200Completed
400Missing or invalid setupType

PUT /api/welcome-flow/screen

Record which screen the user has reached in the welcome wizard.

Request Body

{
  "screen": 2
}
FieldTypeRequiredDescription
screennumberYesCurrent screen number

Response

{ "success": true }
Status CodeMeaning
200Saved

Upload

POST /api/upload/event-image

Upload an image for a match or tournament event. Accepts multipart/form-data.

Request

Form field image containing the image file.

ConstraintValue
Allowed typesJPEG, PNG, WebP, GIF
Max file size5 MB

File signatures are verified server-side (not just MIME type).

Response

{
  "success": true,
  "imageUrl": "/uploads/events/abc123.jpg",
  "filename": "abc123.jpg"
}
Status CodeMeaning
200Uploaded
400Missing file, invalid type, or file too large

DELETE /api/upload/event-image

Delete a previously uploaded event image.

Query Parameters

ParameterTypeRequiredDescription
imageUrlstringYesFull relative URL (e.g., /uploads/events/abc123.jpg)

Notes

  • The imageUrl must start with /uploads/events/. Requests targeting other paths are rejected.

Response

{ "success": true }
Status CodeMeaning
200Deleted
400Missing or invalid imageUrl

Matches

Match status follows this progression:

created → gather → assign → battle → complete
                                   ↘ cancelled

GET /api/matches

List matches. Returns active matches by default.

Query Parameters

ParameterValueDescription
status"complete"Return completed matches instead of active ones

Response

[
  {
    "id": "1",
    "name": "Grand Finals",
    "description": "Best of 5",
    "status": "battle",
    "game_id": "overwatch-2",
    "game_name": "Overwatch 2",
    "game_icon": "/games/overwatch-2/icon.png",
    "game_color": "#F99E1A",
    "map_codes_supported": true,
    "maps": ["king-s-row", "hanamura"],
    "map_codes": { "king-s-row": "ABCD1234" },
    "map_id": "competitive",
    "map_name": "Competitive",
    "start_time": "2024-12-31T20:00:00.000Z",
    "start_date": "2024-12-31T20:00:00.000Z",
    "rules": "competitive",
    "rounds": 5,
    "livestream_link": "https://twitch.tv/example",
    "blue_team_voice_channel": "123456789012345678",
    "red_team_voice_channel": "123456789012345679",
    "tournament_name": "Winter Cup",
    "tournament_round": 3,
    "tournament_bracket_type": "winners",
    "participant_count": 10,
    "created_at": "2024-12-01T00:00:00.000Z",
    "updated_at": "2024-12-31T20:00:00.000Z"
  }
]
Status CodeMeaning
200Success

POST /api/matches

Create a new match.

Request Body

{
  "name": "Grand Finals",
  "description": "Best of 5",
  "gameId": "overwatch-2",
  "gameModeId": "competitive",
  "mapId": "king-s-row",
  "maps": ["king-s-row", "hanamura", "blizzard-world"],
  "rules": "competitive",
  "rounds": 5,
  "startDate": "2024-12-31T20:00:00.000Z",
  "livestreamLink": "https://twitch.tv/example",
  "announcements": [
    { "minutesBefore": 60, "message": "Match starts in 1 hour!" },
    { "minutesBefore": 5,  "message": "Match starting soon!" }
  ],
  "playerNotifications": true,
  "eventImageUrl": "/uploads/events/abc123.jpg"
}
FieldTypeRequiredDescription
namestringYesMatch display name
gameIdstringYesGame identifier
gameModeIdstringYesGame mode identifier
descriptionstringNoMatch description
mapIdstringNoSingle map identifier
mapsstring[]NoOrdered list of map IDs for multi-map matches
rules"casual" | "competitive"NoRuleset
roundsnumberNoNumber of rounds/maps
startDateISO8601NoScheduled start time
livestreamLinkstringNoLivestream URL
announcementsobject[]NoScheduled announcement messages
playerNotificationsbooleanNoWhether to send reminders to participants
eventImageUrlstringNoURL from upload endpoint

Response

Created match object (201 status, same shape as list item).

Status CodeMeaning
201Created
400Validation error

GET /api/matches/{matchId}

Get a single match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Response

Single match object. Includes tournament_allow_match_editing boolean when the match is part of a tournament.

Status CodeMeaning
200Success
404Match not found

PUT /api/matches/{matchId}

Update a match. Only allowed before the match reaches "battle" status. Tournament matches may additionally be restricted by allow_match_editing.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Request Body

{
  "name": "Updated Name",
  "description": "Updated description",
  "startDate": "2025-01-01T20:00:00.000Z",
  "rules": "casual",
  "rounds": 3,
  "livestreamLink": "https://twitch.tv/updated",
  "maps": ["hanamura", "ilios"]
}

All fields are optional.

Response

Updated match object.

Status CodeMeaning
200Updated
403Match is in battle/complete/cancelled status, or tournament disallows editing
404Match not found

DELETE /api/matches/{matchId}

Delete a match and clean up associated resources.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Notes

  • Deletes the event image if one was uploaded.
  • Queues Discord message deletions for any announcements that were sent.

Response

{ "success": true }
Status CodeMeaning
200Deleted
404Match not found

GET /api/matches/{matchId}/participants

List all participants signed up for a match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Notes

  • Participants are added and removed via Discord bot commands, not via REST API.

Response

{
  "participants": [
    {
      "id": "1",
      "user_id": "user-abc",
      "discord_user_id": "123456789012345678",
      "username": "Player1",
      "avatar_url": "https://cdn.discordapp.com/avatars/...",
      "joined_at": "2024-12-31T18:00:00.000Z",
      "signup_data": {},
      "team_assignment": "blue",
      "receives_map_codes": true
    }
  ],
  "signupConfig": {}
}
Status CodeMeaning
200Success
404Match not found

GET /api/matches/{matchId}/games

Get the individual games (rounds) within a match. Auto-initializes game entries if none exist.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Response

{
  "success": true,
  "games": [
    {
      "id": "1",
      "match_id": "1",
      "round": 1,
      "participant1_id": "1",
      "participant2_id": "2",
      "map_id": "king-s-row",
      "winner_id": null,
      "status": "pending",
      "created_at": "2024-12-31T20:00:00.000Z"
    }
  ]
}
Status CodeMeaning
200Success
404Match not found

GET /api/matches/{matchId}/games-with-results

Get match games along with full result details (scores, winner, etc.).

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Response

Array of game objects with result data merged in.

Status CodeMeaning
200Success
404Match not found

POST /api/matches/{matchId}/assign-teams

Assign participants to Blue, Red, or Reserve teams.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Request Body

{
  "teamAssignments": [
    {
      "participantId": "1",
      "team": "blue",
      "receives_map_codes": true
    },
    {
      "participantId": "2",
      "team": "red",
      "receives_map_codes": false
    }
  ],
  "blueTeamVoiceChannel": "123456789012345678",
  "redTeamVoiceChannel": "123456789012345679"
}
FieldTypeRequiredDescription
teamAssignmentsobject[]YesList of participant → team assignments
teamAssignments[].participantIdstringYesParticipant ID
teamAssignments[].team"blue" | "red" | "reserve"YesTeam assignment
teamAssignments[].receives_map_codesbooleanNoWhether this player receives map codes
blueTeamVoiceChannelstringNoDiscord voice channel ID for Blue team
redTeamVoiceChannelstringNoDiscord voice channel ID for Red team

Response

{ "success": true }
Status CodeMeaning
200Assigned
404Match not found

GET /api/matches/{matchId}/overall-score

Get the aggregate score for a match (Blue vs Red across all games).

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Response

Scoring object with aggregate results for both teams.

Status CodeMeaning
200Success
404Match not found

POST /api/matches/{matchId}/transition

Transition a match to a new status.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Request Body

{
  "newStatus": "gather"
}
FieldTypeRequiredValid Values
newStatusstringYes"created", "gather", "assign", "battle", "complete", "cancelled"

Response

Updated match object.

Status CodeMeaning
200Transitioned
400Invalid transition or status
404Match not found

GET /api/matches/{matchId}/reminders

List all scheduled announcements and reminders for a match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Response

{
  "match": {
    "id": "1",
    "name": "Grand Finals",
    "start_date": "2024-12-31T20:00:00.000Z",
    "player_notifications": true,
    "status": "gather"
  },
  "reminders": [
    {
      "reminder_time": "2024-12-31T19:00:00.000Z",
      "message": "Match starts in 1 hour!",
      "status": "pending"
    }
  ],
  "reminderCount": 1
}
Status CodeMeaning
200Success
404Match not found

POST /api/matches/{matchId}/map-codes

Save map codes for the match’s maps. Codes are shared with players who have receives_map_codes = true.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Request Body

{
  "mapCodes": {
    "king-s-row": "ABCD1234",
    "hanamura": "EFGH5678"
  }
}

Map code values are capped at 24 characters each.

Response

{
  "success": true,
  "mapCodes": {
    "king-s-row": "ABCD1234",
    "hanamura": "EFGH5678"
  }
}
Status CodeMeaning
200Saved
400Code exceeds 24 characters
404Match not found

GET /api/matches/{matchId}/map-codes

Retrieve saved map codes for a match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Response

{
  "mapCodes": {
    "king-s-row": "ABCD1234"
  }
}
Status CodeMeaning
200Success
404Match not found

POST /api/matches/{matchId}/map-notes

Save a note for a specific map within the match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Request Body

{
  "mapId": "king-s-row",
  "note": "Blue team focuses on point A"
}
FieldTypeRequired
mapIdstringYes
notestringNo

Response

{
  "success": true,
  "note": "Blue team focuses on point A"
}
Status CodeMeaning
200Saved
404Match not found

GET /api/matches/{matchId}/map-notes

Retrieve all map notes for a match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID

Response

{
  "notes": {
    "king-s-row": "Blue team focuses on point A"
  }
}
Status CodeMeaning
200Success
404Match not found

GET /api/matches/{matchId}/games/{gameId}/result

Get the recorded result of a single game within a match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID
gameIdstringGame ID

Response

Result object with scores and winner.

Status CodeMeaning
200Success
404Match or game not found, or no result recorded yet

POST /api/matches/{matchId}/games/{gameId}/result

Record the result (winner) for a single game within a match.

URL Parameters

ParameterTypeDescription
matchIdstringMatch ID
gameIdstringGame ID

Request Body

{
  "gameId": "1",
  "matchId": "1",
  "winner": "team1"
}
FieldTypeRequiredDescription
gameIdstringYesMust match URL parameter
matchIdstringYesMust match URL parameter
winner"team1" | "team2"YesWinning team

Response

{
  "success": true,
  "message": "Blue Team wins!"
}
Status CodeMeaning
200Result recorded
400Validation error or mismatched IDs
404Match or game not found

Tournaments

Tournament status follows the same progression as matches:

created → gather → assign → battle → complete
                                   ↘ cancelled

GET /api/tournaments

List tournaments. Returns active tournaments by default.

Query Parameters

ParameterValueDescription
status"complete"Return completed tournaments instead of active ones

Response

[
  {
    "id": "1",
    "name": "Winter Cup",
    "description": "Annual championship",
    "status": "battle",
    "game_id": "overwatch-2",
    "game_name": "Overwatch 2",
    "game_icon": "/games/overwatch-2/icon.png",
    "game_color": "#F99E1A",
    "game_mode_id": "competitive",
    "format": "single-elimination",
    "rounds_per_match": 3,
    "ruleset": "competitive",
    "max_participants": 64,
    "allow_player_team_selection": false,
    "allow_match_editing": true,
    "event_image_url": "/uploads/events/abc123.jpg",
    "start_date": "2025-01-01T00:00:00.000Z",
    "start_time": "2025-01-01T18:00:00.000Z",
    "participant_count": 48,
    "has_bracket": true,
    "created_at": "2024-12-01T00:00:00.000Z",
    "updated_at": "2024-12-31T00:00:00.000Z"
  }
]
Status CodeMeaning
200Success

POST /api/tournaments

Create a new tournament.

Request Body

{
  "name": "Winter Cup",
  "description": "Annual championship",
  "gameId": "overwatch-2",
  "gameModeId": "competitive",
  "format": "single-elimination",
  "startDate": "2025-01-01T00:00:00.000Z",
  "startTime": "2025-01-01T18:00:00.000Z",
  "roundsPerMatch": 3,
  "ruleset": "competitive",
  "maxParticipants": 64,
  "eventImageUrl": "/uploads/events/abc123.jpg",
  "allowPlayerTeamSelection": false,
  "allowMatchEditing": true
}
FieldTypeRequiredDescription
namestringYesTournament display name
gameIdstringYesGame identifier
gameModeIdstringYesGame mode identifier
format"single-elimination" | "double-elimination"YesBracket format
roundsPerMatchnumberYesGames per bracket match
descriptionstringNoTournament description
startDateISO8601NoStart date
startTimeISO8601NoStart time
rulesetstringNoRule set label
maxParticipantsnumberNoParticipant cap
eventImageUrlstringNoURL from upload endpoint
allowPlayerTeamSelectionbooleanNoAllow players to choose their team
allowMatchEditingbooleanNoAllow editing matches after creation

Response

Created tournament object (201 status, same shape as list item).

Status CodeMeaning
201Created
400Validation error

GET /api/tournaments/{tournamentId}

Get full tournament details including teams and participants.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Response

Tournament object with an additional teams array:

{
  "teams": [
    {
      "id": "1",
      "tournament_id": "1",
      "team_name": "Team Alpha",
      "created_at": "2024-12-01T00:00:00.000Z",
      "members": [
        {
          "id": "1",
          "team_id": "1",
          "user_id": "user-abc",
          "discord_user_id": "123456789012345678",
          "username": "Player1",
          "avatar_url": "https://cdn.discordapp.com/avatars/...",
          "joined_at": "2024-12-15T00:00:00.000Z"
        }
      ]
    }
  ]
}
Status CodeMeaning
200Success
404Tournament not found

DELETE /api/tournaments/{tournamentId}

Delete a tournament and all associated data.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Notes

  • Queues Discord message deletions for tournament and all associated matches.
  • Deletes bracket matches, teams, and participants.

Response

{ "success": true }
Status CodeMeaning
200Deleted
404Tournament not found

GET /api/tournaments/{tournamentId}/participants

Get all participants signed up for a tournament.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Response

{
  "participants": [
    {
      "id": "1",
      "user_id": "user-abc",
      "username": "Player1",
      "joined_at": "2024-12-15T00:00:00.000Z",
      "team_assignment": "Team Alpha",
      "signup_data": {}
    }
  ],
  "signupConfig": {}
}
Status CodeMeaning
200Success
404Tournament not found

GET /api/tournaments/{tournamentId}/teams

Get all teams in a tournament with their members.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Response

Array of team objects (same shape as the teams array in the tournament detail response).

Status CodeMeaning
200Success
404Tournament not found

POST /api/tournaments/{tournamentId}/teams

Create a new team in the tournament.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Request Body

{
  "teamName": "Team Alpha"
}
FieldTypeRequired
teamNamestringYes

Response

Created team object with an empty members array (201 status).

Status CodeMeaning
201Created
409Team name already exists in this tournament

PUT /api/tournaments/{tournamentId}/teams

Assign participants to teams. Clears all previous team assignments before applying the new ones.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Request Body

{
  "teams": [
    {
      "teamId": "1",
      "members": [
        { "userId": "user-abc", "username": "Player1" },
        { "userId": "user-def", "username": "Player2" }
      ]
    }
  ]
}

Response

{ "success": true }
Status CodeMeaning
200Assigned
404Tournament or team not found

DELETE /api/tournaments/{tournamentId}/teams

Delete a specific team from the tournament.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Query Parameters

ParameterTypeRequiredDescription
teamIdstringYesTeam ID to delete

Response

{ "success": true }
Status CodeMeaning
200Deleted
400Missing teamId
404Team not found

GET /api/tournaments/{tournamentId}/matches

Get all bracket matches for a tournament.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Response

{
  "matches": [
    {
      "id": "1",
      "round": 1,
      "bracket_type": "winners",
      "team1": { "id": "1", "name": "Team Alpha" },
      "team2": { "id": "2", "name": "Team Beta" },
      "winner": null,
      "status": "ongoing",
      "rawStatus": "battle",
      "match_order": 1
    }
  ],
  "count": 4
}
Status CodeMeaning
200Success
404Tournament not found

GET /api/tournaments/{tournamentId}/standings

Get win/loss standings for all teams in the tournament.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Response

{
  "standings": [
    {
      "team_id": "1",
      "team_name": "Team Alpha",
      "matches_played": 3,
      "wins": 3,
      "losses": 0
    }
  ]
}
Status CodeMeaning
200Success
404Tournament not found

POST /api/tournaments/{tournamentId}/transition

Transition a tournament to a new status.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Request Body

{
  "newStatus": "battle"
}
FieldTypeRequiredValid Values
newStatusstringYes"created", "gather", "assign", "battle", "complete", "cancelled"

Notes

  • Transitioning to "battle" requires at least 2 teams with members assigned.

Response

Updated tournament object.

Status CodeMeaning
200Transitioned
400Invalid transition, or requirements not met (e.g., insufficient teams)
404Tournament not found

POST /api/tournaments/{tournamentId}/bracket-assignments

Save the seeding positions for each team before generating bracket matches.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Request Body

{
  "assignments": [
    { "position": 1, "teamId": "1" },
    { "position": 2, "teamId": "2" }
  ]
}

Notes

  • Tournament must be in "assign" status.

Response

{
  "message": "Bracket assignments saved successfully",
  "assignments": [...]
}
Status CodeMeaning
200Saved
400Tournament not in assign status
404Tournament not found

POST /api/tournaments/{tournamentId}/generate-matches

Generate bracket matches for the tournament. If bracket assignments are provided, they are applied first; otherwise teams are seeded automatically.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Request Body (optional)

{
  "assignments": [
    { "position": 1, "teamId": "1" },
    { "position": 2, "teamId": "2" }
  ]
}

Notes

  • Tournament must be in "assign" status.
  • Returns an error if bracket matches have already been generated.

Response

{
  "message": "Bracket generated successfully",
  "matchCount": 8,
  "format": "single-elimination",
  "tournamentId": "1"
}
Status CodeMeaning
200Bracket generated
400Not in assign status or matches already generated
404Tournament not found

POST /api/tournaments/{tournamentId}/progress

Advance the tournament bracket to the next round, or finalize the tournament if all rounds are complete.

URL Parameters

ParameterTypeDescription
tournamentIdstringTournament ID

Notes

  • Tournament must be in "battle" status.
  • All matches in the current round must be completed before progressing.
  • Handles both single-elimination and double-elimination bracket logic.

Response (new round generated)

{
  "message": "Next round generated",
  "matchCount": 4,
  "nextRound": 2,
  "tournamentId": "1"
}

Response (tournament complete)

{
  "message": "Tournament complete",
  "tournamentId": "1",
  "winner": "Team Alpha"
}
Status CodeMeaning
200Progressed or tournament finalized
400Not in battle status or current round not fully complete
404Tournament not found