Skip to main content

Matches

warning

Keep in mind that for legacy reasons Match entity has both _id and id property . While reading rest of this page, take notice of the different spelling, if request param will be described as /api/match/:id, you must use id property, if /api/match/:_id, then _id.

Listing matches

Matches can be listed on /api/match endpoint:

GET /api/match
[
{
"_id": "O3twsvpFriCqHYbn",
"id": "ec1362cf-cde5-4667-b46c-1b532fd3e4f0",
"current": false,
"left": {
"id": "UyfhxQ3AtoCoGIEe",
"wins": 0
},
"right": {
"id": "8nz9GXabr3tUUf0W",
"wins": 0
},
"matchType": "bo3", // Possible values: bo1, bo2, bo3, bo5, bo7, bo9
"vetos": [
{
"teamId": "UyfhxQ3AtoCoGIEe",
"mapName": "de_mirage",
"side": "NO",
"score": { // Optional. This contains information about given map score
"UyfhxQ3AtoCoGIEe": 13,
"8nz9GXabr3tUUf0W": 16
},
"type": "pick", // Possible values: pick, ban, decider
"mapEnd": true
},
{
"teamId": "",
"mapName": "",
"side": "NO",
"type": "pick",
"mapEnd": false
},
...
],
"startTime": 0,
"game": "cs2"
}
]

Additionally, each veto entry can have rounds property. It gets automatically filled by LHM after every round, and possess information about per-round statistics for every player. It looks like this:

Veto entry with player stats
...
"vetos": [
{
"teamId": "UyfhxQ3AtoCoGIEe",
"mapName": "de_mirage",
"side": "NO",
"score": {
"UyfhxQ3AtoCoGIEe": 0,
"8nz9GXabr3tUUf0W": 1
},
"type": "pick",
"mapEnd": false,
"rounds": [
{
"round": 1,
"win_type": "elimination", // Possible values: elimination, defuse, time, bomb,
"winner": "CT",
"players": { // Record with SteamID64 as keys, and per-round stats object for given player as value
"768XXXX": {
"kills": 0,
"killshs": 0,
"damage": 0,
"assists": 0,
"deaths": 0,
}
}

}
]
}
],
...

Last payload

In Counter-Strike, right when the final round finishes and LHM marks map with "mapEnd":true property, last GSI payload is also attached to the veto with game property. This is not raw GSI that comes from the Counter-Strike data provider, but a result of parsing with csgogsi package. To actually read this property while listing all matches, you must add ?full query to the request path. This field is automatically added when fetching current Match, or by its id.

Fetching current match

Each match has current bool property, which tells HUDs which meta-data (team names / logos) to pull. Only one match can be active at the same time, and can be easily fetched with:

GET /api/match/current
{
"_id": "O3twsvpFriCqHYbn",
"id": "ec1362cf-cde5-4667-b46c-1b532fd3e4f0",
"current": false,
"left": {
"id": "UyfhxQ3AtoCoGIEe",
"wins": 0
},
"right": {
"id": "8nz9GXabr3tUUf0W",
"wins": 0
},
"matchType": "bo3", // Possible values: bo1, bo2, bo3, bo5, bo7, bo9
"vetos": [
{
"teamId": "UyfhxQ3AtoCoGIEe",
"mapName": "de_mirage",
"side": "NO",
"score": { // Optional. This contains information about series score
"UyfhxQ3AtoCoGIEe": 0,
"8nz9GXabr3tUUf0W": 1
},
"type": "pick", // Possible values: pick, ban, decider
"mapEnd": false
},
{
"teamId": "",
"mapName": "",
"side": "NO",
"type": "pick",
"mapEnd": false
},
...
],
"startTime": 0,
"game": "cs2"
}

If no match is set as active, this endpoint returns 404.

Fetching single match

It is possible to fetch single match by doing:

GET /api/match/:id

Toggling active match

Match can have its current state toggled by doing:

POST /api/match/current/:id

Match Creation

To create Match Entity, you must send a POST to /api/match with proper payload. This payload differs based on a game. For all games beside Apex Legends, it must look like this:

Basic Match Creation Payload
{
"id": "", // Must be empty
"current": false,
"left": { "id": null, "wins": 0 },
"right": { "id": null, "wins": 0 },
"matchType": "bo1", // or any other BO. Supported BOs are: bo1, bo2, bo3, bo5, bo7, bo9
"vetos": [Fully Filled Veto, must have 9 elements],
"startTime": 0
}

If the match is for Apex Legends, use the schema below:

Apex Legends Match Creation Payload
{
"id": "",
"name": "",
"current": false,
"teams": [Fully Filled Veto, must have 9 elements],
"matchType": "multiteam",
"vetos": [],
"startTime": 0,
"game": "apexlegends"
}

Each Match must have fully filled Veto - an array of 9 Veto objects (even if BO is not specified BO1!), which structure depends on the game.

For Counter-Strike, Veto's structure looks like this:

Counter-Strike Veto Example
{
"teamId": "",
"mapName": "",
"side": "NO",
"type": "pick",
"mapEnd": false,
"reverseSide": false /
}
FieldDescription
teamIdRequired, can empty - ID of a team
mapNameRequired - Name of the map (ex. "de_mirage")
sideRequired - "NO", "CT" or "T" - whether an opponent of team with teamId has picked a starting side
typeRequired - "pick", "ban" or "decider"
mapEndRequired - Indicate whether map has finished. Setting this to true will prevent LHM from trying to update statistics.
reverseSideWhether game assigned Teams to have different orientation than left / right setup in the base Match fields. Optional field, can be undefined

While for the rest of games:

Other Veto Example
{
"mapEnd": false,
"reverseSide": false
}
FieldDescription
mapEndRequired - Indicate whether map has finished. Setting this to true will prevent LHM from trying to update statistics.
reverseSideWhether game assigned Teams to have different orientation than left / right setup in the base Match fields. Optional field, can be undefined

Updating a Match

Updating a Match works in form of full payload replacement, so any fields missing from the request body will be missing, and Match will be corrupted as a result. To make sure you do not miss any fields, it's recommended to firstly fetch a Match, to get full payload, with the game property, then transforming the response, and send it back with a request:

PATCH /api/match/:id

Switching sides

You can switch sides of the teams in the currently active Match programatically by doing:

POST /api/match/reverse

Keep in mind that there are limitations and behaviours that might apply when triggering the switch.