SweatHost
HTTP API Reference

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/matches

Scope: 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

FieldTypeRequiredDescription
regionstringYesRegion code (e.g. us-west-1)
gameTypestringYesGame type (e.g. cs2)
callbackUrlstringNoURL to POST match lifecycle events to
callbackAuthTokenstringNoToken sent as Authorization header in webhook callbacks
externalMatchIdstringNoYour correlation ID, returned in all callbacks
gameSettingsobjectYesGame-specific settings (see below)

Game settings (gameSettings)

FieldTypeRequiredDescription
matchTypestringYescompetitive, casual, deathmatch, wingman, retakes, custom
mapstringYesStarting map (e.g. de_dust2)
mapsstring[]NoAdditional map pool
seriesFormatstringNobo1, bo3, bo5 — competitive/wingman only
teamsobjectNoTeam config — competitive/wingman only
playersstring[]NoSteam ID 64s — deathmatch only
serverPasswordstringNoServer password for players to connect
dmSubModestringNoffa or team — deathmatch only
dmRoundMinutesnumberNoRound duration in minutes (min: 1) — deathmatch only
freezeTimenumberNoFreeze time in seconds (min: 0)
maxRoundsnumberNoMax rounds (min: 1)
customCvarsstringNoRaw 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"
}
FieldTypeDescription
serverIdstringUnique server ID — use for get/cancel
gameIdstringSession UUID
regionstringRegion code
statusstringallocatingrunning
ipstring | nullPublic IP (available once running)
portnumber | nullGame port (available once running)
callbackUrlstring | nullYour callback URL
externalMatchIdstring | nullYour correlation ID
gameTypestringe.g. cs2
createdAtstringISO 8601
rconPasswordstring | nullRCON 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-destroyed

Match features

When players or teams is provided, the following features apply automatically:

FeatureBehavior
WhitelistOnly listed players can connect. Unauthorized players are kicked immediately.
Auto-startThe 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-shutdownServer is destroyed immediately after the match ends.

Get server

GET /api/v1/servers/:id

Scope: 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 }
    ]
  }
}
FieldTypeDescription
serverIdstringUnique server ID
gameIdstringSession UUID
regionstringRegion code
statusstringallocating, running, etc.
ipstring | nullPublic IP (available once running)
portnumber | nullGame port (available once running)
callbackUrlstring | nullYour callback URL
externalMatchIdstring | nullYour correlation ID
gameTypestringe.g. cs2
createdAtstringISO 8601
rconPasswordstring | nullRCON password
matchobject | nullLive match data (present when a CS2 session is active)

Match object

Present when the server has an active CS2 session.

FieldTypeDescription
matchTypestringcompetitive, wingman, or deathmatch
mapstring | nullCurrent map name
scoreobject{ team1, team2 } round scores — competitive/wingman only
playersarrayPlayer stats (see below)

Player object

FieldTypeDescription
steamIdstringSteam ID 64
namestringPlayer name (if available)
killsnumberKill count
deathsnumberDeath count
assistsnumberAssist count

Cancel match

Cancel a running match and destroy the server.

POST /api/v1/servers/:id/cancel

Scope: 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/:externalMatchId

Scope: 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

ParameterTypeRequiredDescription
externalMatchIdstringYesYour correlation ID from match creation

Response

FieldTypeDescription
externalMatchIdstringYour correlation ID
serverIdstringServer ID
statusstringServer status (allocating, pending, running, failed, stopped)
regionstringRegion code
createdAtstringISO 8601
resultobject | nullMatch 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

FieldTypeDescription
result.gameModestringcompetitive or wingman
result.formatstringbo1, bo3, or bo5
result.winnerstring | nullteam1, team2, tie, or null
result.team1object{ name, seriesScore } — maps won in series
result.team2object{ name, seriesScore }
result.maps[]arrayOne entry per map played

Map fields

FieldTypeDescription
mapNamestringMap name (e.g. de_dust2)
mapNumbernumberMap number in series (1, 2, 3...)
team1ScorenumberRounds won by team1
team2ScorenumberRounds won by team2
winnerstring | nullteam1, team2, tie, or null
durationnumber | nullDuration in seconds
players[]arrayPer-player stats for this map

Player stats (competitive)

FieldTypeDescription
steamIdstringSteam ID 64
namestringPlayer name
teamstringteam1 or team2
killsnumberKill count
deathsnumberDeath count
assistsnumberAssist count
headshotsnumberHeadshot kills
hsPercentnumberHeadshot percentage
kdRationumberKill/death ratio
adrnumberAverage damage per round
damageDealtnumberTotal damage dealt
utilityDamagenumberGrenade damage
flashAssistsnumberFlash assists
firstKillsnumberOpening kills
firstDeathsnumberOpening deaths
entryKillsnumberEntry frags
tradeKillsnumberTrade kills
bombPlantsnumberBomb plants
bombDefusesnumberBomb defuses
clutchWinsnumberClutch rounds won
roundsPlayednumberRounds played
mvpsnumberMVP stars
kastPercentnumberKAST% (Kill/Assist/Survived/Traded)
impactRatingnumberImpact 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)

FieldTypeDescription
steamIdstringSteam ID 64
namestringPlayer name
killsnumberKill count
deathsnumberDeath count
assistsnumberAssist count
headshotsnumberHeadshot kills
hsPercentnumberHeadshot percentage
kdRationumberKill/death ratio
damageDealtnumberTotal damage dealt
kpmnumberKills 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

FieldTypeDescription
eventTypestringmatch_over or match_canceled
gameIdstringGame identifier (currently always cs2)
externalMatchIdstringYour correlation ID (from externalMatchId on creation)
serverIdstringServer ID
sessionIdstringSession ID
timestampstringISO 8601 timestamp
resultobject | undefinedMatch result (only on match_over)
reasonstring | undefinedCancellation 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

HTTPCodeDescription
400INVALID_PARAMSInvalid request body or query
401UNAUTHORIZEDInvalid or missing API key
403FORBIDDENNo access or wrong organization
404NOT_FOUNDServer not found

On this page