Game Servers
HTTP API reference for creating matches, getting server status, and canceling matches
Game Servers API
Create ephemeral matches, get live server data, and cancel running matches. All endpoints are organization-scoped (determined by your API key).
SweatHost uses an ephemeral server model — each match gets a fresh server. A single API call creates the server, allocates a pod, and starts the game. The server is automatically destroyed when the match ends.
Create match
POST /api/v1/matchesScope: servers:write | Role: admin, owner
Start an ephemeral match. This single call creates a server, allocates a pod from the fleet, and applies game configuration via RCON. The server auto-destroys when the match completes.
Competitive match
curl -X POST "https://api.sweathost.com/api/v1/matches" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"region": "us-west-1",
"gameType": "cs2",
"gameSettings": {
"matchType": "competitive",
"map": "de_dust2",
"maps": ["de_dust2", "de_mirage", "de_inferno"],
"seriesFormat": "bo3"
}
}'Competitive with teams, callback, and auto-shutdown
curl -X POST "https://api.sweathost.com/api/v1/matches" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"region": "us-west-1",
"gameType": "cs2",
"callbackUrl": "https://example.com/webhooks/match",
"callbackAuthToken": "Bearer my-secret-token",
"externalMatchId": "my-tournament-match-42",
"gameSettings": {
"matchType": "competitive",
"map": "de_dust2",
"seriesFormat": "bo1",
"teams": {
"team1": { "name": "Alpha", "players": ["76561198000000001", "76561198000000002"] },
"team2": { "name": "Beta", "players": ["76561198000000003", "76561198000000004"] }
}
}
}'Deathmatch with player list
curl -X POST "https://api.sweathost.com/api/v1/matches" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"region": "us-west-1",
"gameType": "cs2",
"callbackUrl": "https://example.com/webhooks/match",
"callbackAuthToken": "Bearer my-secret-token",
"externalMatchId": "dm-lobby-99",
"gameSettings": {
"matchType": "deathmatch",
"map": "de_dust2",
"dmSubMode": "ffa",
"dmRoundMinutes": 10,
"players": ["76561198000000001", "76561198000000002", "76561198000000003"]
}
}'Basic deathmatch (no player list)
curl -X POST "https://api.sweathost.com/api/v1/matches" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"region": "us-west-1",
"gameType": "cs2",
"gameSettings": {
"matchType": "deathmatch",
"map": "de_dust2",
"dmSubMode": "ffa",
"dmRoundMinutes": 15
}
}'Wingman
curl -X POST "https://api.sweathost.com/api/v1/matches" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"region": "us-west-1",
"gameType": "cs2",
"gameSettings": {
"matchType": "wingman",
"map": "de_inferno",
"seriesFormat": "bo1",
"teams": {
"team1": { "name": "Duo A", "players": ["76561198000000001", "76561198000000002"] },
"team2": { "name": "Duo B", "players": ["76561198000000003", "76561198000000004"] }
}
}
}'Request body
| Field | Type | Required | Description |
|---|---|---|---|
region | string | Yes | Region code (e.g. us-west-1) |
gameType | string | Yes | Game type (e.g. cs2) |
callbackUrl | string | No | URL to POST match lifecycle events to |
callbackAuthToken | string | No | Token sent as Authorization header in webhook callbacks |
externalMatchId | string | No | Your correlation ID, returned in all callbacks |
gameSettings | object | Yes | Game-specific settings (see below) |
Game settings (gameSettings)
| Field | Type | Required | Description |
|---|---|---|---|
matchType | string | Yes | competitive, casual, deathmatch, wingman, retakes, custom |
map | string | Yes | Starting map (e.g. de_dust2) |
maps | string[] | No | Additional map pool |
seriesFormat | string | No | bo1, bo3, bo5 — competitive/wingman only |
teams | object | No | Team config — competitive/wingman only |
players | string[] | No | Steam ID 64s — deathmatch only |
serverPassword | string | No | Server password for players to connect |
dmSubMode | string | No | ffa or team — deathmatch only |
dmRoundMinutes | number | No | Round duration in minutes (min: 1) — deathmatch only |
freezeTime | number | No | Freeze time in seconds (min: 0) |
maxRounds | number | No | Max rounds (min: 1) |
customCvars | string | No | Raw CVar lines |
Validation
players and teams cannot be set at the same time. players is only valid for deathmatch. teams is only valid for competitive or wingman.
Teams object
{
"team1": { "name": "Alpha", "players": ["76561198000000001", "76561198000000002"] },
"team2": { "name": "Beta", "players": ["76561198000000003", "76561198000000004"] }
}Players are Steam ID 64 strings.
Response
{
"serverId": "uuid",
"gameId": "uuid",
"region": "us-west-1",
"status": "allocating",
"ip": null,
"port": null,
"callbackUrl": "https://example.com/webhooks/match",
"externalMatchId": "my-tournament-match-42",
"gameType": "cs2",
"createdAt": "2026-03-12T10:00:00.000Z",
"rconPassword": "auto-generated-password"
}| Field | Type | Description |
|---|---|---|
serverId | string | Unique server ID — use for get/cancel |
gameId | string | Session UUID |
region | string | Region code |
status | string | allocating → running |
ip | string | null | Public IP (available once running) |
port | number | null | Game port (available once running) |
callbackUrl | string | null | Your callback URL |
externalMatchId | string | null | Your correlation ID |
gameType | string | e.g. cs2 |
createdAt | string | ISO 8601 |
rconPassword | string | null | RCON password |
Server lifecycle
POST /api/v1/matches → status: "allocating"
↓ pod allocated from fleet
status: "running" (ip + port available)
↓ match plays out
match ends → server auto-destroyedMatch features
When players or teams is provided, the following features apply automatically:
| Feature | Behavior |
|---|---|
| Whitelist | Only listed players can connect. Unauthorized players are kicked immediately. |
| Auto-start | The game stays in warmup until every listed player has joined. Once the last player connects, the match starts right away — no manual ready-up needed. Competitive/wingman: MatchZy force-readies all players. Deathmatch: warmup ends, round restarts, stats recording begins. |
| Auto-shutdown | Server is destroyed immediately after the match ends. |
Get server
GET /api/v1/servers/:idScope: servers:read
Get a server with live match data (game-tailored response).
curl -X GET "https://api.sweathost.com/api/v1/servers/SERVER_ID" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"serverId": "uuid",
"gameId": "uuid",
"region": "us-west-1",
"status": "running",
"ip": "44.230.1.100",
"port": 27015,
"callbackUrl": "https://example.com/webhooks/match",
"externalMatchId": "my-tournament-match-42",
"gameType": "cs2",
"createdAt": "2026-03-12T10:00:00.000Z",
"rconPassword": "auto-generated-password",
"match": {
"matchType": "competitive",
"map": "de_dust2",
"score": { "team1": 8, "team2": 5 },
"players": [
{ "steamId": "76561198000000001", "name": "Player1", "kills": 15, "deaths": 10, "assists": 3 }
]
}
}| Field | Type | Description |
|---|---|---|
serverId | string | Unique server ID |
gameId | string | Session UUID |
region | string | Region code |
status | string | allocating, running, etc. |
ip | string | null | Public IP (available once running) |
port | number | null | Game port (available once running) |
callbackUrl | string | null | Your callback URL |
externalMatchId | string | null | Your correlation ID |
gameType | string | e.g. cs2 |
createdAt | string | ISO 8601 |
rconPassword | string | null | RCON password |
match | object | null | Live match data (present when a CS2 session is active) |
Match object
Present when the server has an active CS2 session.
| Field | Type | Description |
|---|---|---|
matchType | string | competitive, wingman, or deathmatch |
map | string | null | Current map name |
score | object | { team1, team2 } round scores — competitive/wingman only |
players | array | Player stats (see below) |
Player object
| Field | Type | Description |
|---|---|---|
steamId | string | Steam ID 64 |
name | string | Player name (if available) |
kills | number | Kill count |
deaths | number | Death count |
assists | number | Assist count |
Cancel match
Cancel a running match and destroy the server.
POST /api/v1/servers/:id/cancelScope: servers:write | Role: admin, owner
curl -X POST "https://api.sweathost.com/api/v1/servers/SERVER_ID/cancel" \
-H "Authorization: Bearer YOUR_API_KEY"Response: { "ok": true }
Fires a match_canceled callback if callbackUrl was set.
Get match by external ID
GET /api/v1/matches/:externalMatchIdScope: servers:read
Fetch the match result using the externalMatchId you provided when creating the match. Returns the same enriched result structure as the match_over webhook callback.
curl -X GET "https://api.sweathost.com/api/v1/matches/my-tournament-match-42" \
-H "Authorization: Bearer YOUR_API_KEY"Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
externalMatchId | string | Yes | Your correlation ID from match creation |
Response
| Field | Type | Description |
|---|---|---|
externalMatchId | string | Your correlation ID |
serverId | string | Server ID |
status | string | Server status (allocating, pending, running, failed, stopped) |
region | string | Region code |
createdAt | string | ISO 8601 |
result | object | null | Match result — null while in progress, populated once finished |
The result object is identical to the match_over webhook result field (see Callback events below for full schema).
Response example (competitive)
{
"externalMatchId": "my-tournament-match-42",
"serverId": "uuid",
"status": "stopped",
"region": "us-west-1",
"createdAt": "2026-03-12T10:00:00.000Z",
"result": {
"gameMode": "competitive",
"format": "bo3",
"winner": "team1",
"team1": { "name": "Alpha", "seriesScore": 2 },
"team2": { "name": "Beta", "seriesScore": 1 },
"maps": [
{
"mapName": "de_dust2",
"mapNumber": 1,
"team1Score": 13,
"team2Score": 7,
"winner": "team1",
"duration": 1800,
"players": [
{
"steamId": "76561198000000001",
"name": "Player1",
"team": "team1",
"kills": 25,
"deaths": 15,
"assists": 5,
"headshots": 12,
"hsPercent": 48.0,
"kdRatio": 1.67,
"adr": 85.3,
"damageDealt": 1706,
"utilityDamage": 120,
"flashAssists": 3,
"firstKills": 4,
"firstDeaths": 2,
"entryKills": 3,
"tradeKills": 2,
"bombPlants": 1,
"bombDefuses": 0,
"clutchWins": 1,
"roundsPlayed": 20,
"mvps": 5,
"kastPercent": 72.0,
"impactRating": 1.45
}
]
}
]
}
}Response example (deathmatch)
{
"externalMatchId": "dm-lobby-99",
"serverId": "uuid",
"status": "stopped",
"region": "us-west-1",
"createdAt": "2026-03-12T10:00:00.000Z",
"result": {
"gameMode": "deathmatch",
"map": "de_dust2",
"duration": 600,
"winnerSteamId": "76561198000000001",
"players": [
{
"steamId": "76561198000000001",
"name": "Player1",
"kills": 45,
"deaths": 20,
"assists": 3,
"headshots": 22,
"hsPercent": 48.9,
"kdRatio": 2.25,
"damageDealt": 5400,
"kpm": 4.5
}
]
}
}Response example (match in progress)
{
"externalMatchId": "my-tournament-match-42",
"serverId": "uuid",
"status": "running",
"region": "us-west-1",
"createdAt": "2026-03-12T10:00:00.000Z",
"result": null
}Callback events
When callbackUrl is set on match creation, the API sends a single POST to your URL when the match ends or is canceled. If callbackAuthToken was provided, it is sent as the Authorization header so your endpoint can verify authenticity.
Only one match_over callback is sent per match, regardless of which internal path finishes the game first.
match_over (competitive/wingman)
Includes per-map player stats. For Bo3/Bo5 series, the maps array contains one entry per map played.
{
"eventType": "match_over",
"gameId": "cs2",
"externalMatchId": "my-tournament-match-42",
"serverId": "uuid",
"sessionId": "uuid",
"timestamp": "2026-03-12T11:00:00.000Z",
"result": {
"gameMode": "competitive",
"format": "bo3",
"winner": "team1",
"team1": { "name": "Alpha", "seriesScore": 2 },
"team2": { "name": "Beta", "seriesScore": 1 },
"maps": [
{
"mapName": "de_dust2",
"mapNumber": 1,
"team1Score": 13,
"team2Score": 7,
"winner": "team1",
"duration": 1800,
"players": [
{
"steamId": "76561198000000001",
"name": "Player1",
"team": "team1",
"kills": 25,
"deaths": 15,
"assists": 5,
"headshots": 12,
"hsPercent": 48.0,
"kdRatio": 1.67,
"adr": 85.3,
"damageDealt": 1706,
"utilityDamage": 120,
"flashAssists": 3,
"firstKills": 4,
"firstDeaths": 2,
"entryKills": 3,
"tradeKills": 2,
"bombPlants": 1,
"bombDefuses": 0,
"clutchWins": 1,
"roundsPlayed": 20,
"mvps": 5,
"kastPercent": 72.0,
"impactRating": 1.45
}
]
}
]
}
}Competitive result fields
| Field | Type | Description |
|---|---|---|
result.gameMode | string | competitive or wingman |
result.format | string | bo1, bo3, or bo5 |
result.winner | string | null | team1, team2, tie, or null |
result.team1 | object | { name, seriesScore } — maps won in series |
result.team2 | object | { name, seriesScore } |
result.maps[] | array | One entry per map played |
Map fields
| Field | Type | Description |
|---|---|---|
mapName | string | Map name (e.g. de_dust2) |
mapNumber | number | Map number in series (1, 2, 3...) |
team1Score | number | Rounds won by team1 |
team2Score | number | Rounds won by team2 |
winner | string | null | team1, team2, tie, or null |
duration | number | null | Duration in seconds |
players[] | array | Per-player stats for this map |
Player stats (competitive)
| Field | Type | Description |
|---|---|---|
steamId | string | Steam ID 64 |
name | string | Player name |
team | string | team1 or team2 |
kills | number | Kill count |
deaths | number | Death count |
assists | number | Assist count |
headshots | number | Headshot kills |
hsPercent | number | Headshot percentage |
kdRatio | number | Kill/death ratio |
adr | number | Average damage per round |
damageDealt | number | Total damage dealt |
utilityDamage | number | Grenade damage |
flashAssists | number | Flash assists |
firstKills | number | Opening kills |
firstDeaths | number | Opening deaths |
entryKills | number | Entry frags |
tradeKills | number | Trade kills |
bombPlants | number | Bomb plants |
bombDefuses | number | Bomb defuses |
clutchWins | number | Clutch rounds won |
roundsPlayed | number | Rounds played |
mvps | number | MVP stars |
kastPercent | number | KAST% (Kill/Assist/Survived/Traded) |
impactRating | number | Impact rating |
match_over (deathmatch)
Flat player stats for a single map.
{
"eventType": "match_over",
"gameId": "cs2",
"externalMatchId": "dm-lobby-99",
"serverId": "uuid",
"sessionId": "uuid",
"timestamp": "2026-03-12T11:00:00.000Z",
"result": {
"gameMode": "deathmatch",
"map": "de_dust2",
"duration": 600,
"winnerSteamId": "76561198000000001",
"players": [
{
"steamId": "76561198000000001",
"name": "Player1",
"kills": 45,
"deaths": 20,
"assists": 3,
"headshots": 22,
"hsPercent": 48.9,
"kdRatio": 2.25,
"damageDealt": 5400,
"kpm": 4.5
}
]
}
}Player stats (deathmatch)
| Field | Type | Description |
|---|---|---|
steamId | string | Steam ID 64 |
name | string | Player name |
kills | number | Kill count |
deaths | number | Death count |
assists | number | Assist count |
headshots | number | Headshot kills |
hsPercent | number | Headshot percentage |
kdRatio | number | Kill/death ratio |
damageDealt | number | Total damage dealt |
kpm | number | Kills per minute |
match_canceled
{
"eventType": "match_canceled",
"gameId": "cs2",
"externalMatchId": "my-tournament-match-42",
"serverId": "uuid",
"sessionId": "uuid",
"timestamp": "2026-03-12T11:05:00.000Z",
"reason": "canceled_by_user"
}Common callback fields
| Field | Type | Description |
|---|---|---|
eventType | string | match_over or match_canceled |
gameId | string | Game identifier (currently always cs2) |
externalMatchId | string | Your correlation ID (from externalMatchId on creation) |
serverId | string | Server ID |
sessionId | string | Session ID |
timestamp | string | ISO 8601 timestamp |
result | object | undefined | Match result (only on match_over) |
reason | string | undefined | Cancellation reason (only on match_canceled) |
Callback delivery
Callbacks are fire-and-forget with 1 retry on failure (10s timeout per attempt). Your endpoint should return 2xx to acknowledge receipt. You can also fetch the same result via GET /api/v1/matches/:externalMatchId at any time.
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_PARAMS | Invalid request body or query |
| 401 | UNAUTHORIZED | Invalid or missing API key |
| 403 | FORBIDDEN | No access or wrong organization |
| 404 | NOT_FOUND | Server not found |