Commands
Run admin console commands on your game server, discover every available command, and read what has been executed.
Run the same admin commands the in-panel console runs, from your own scripts and bots.
`commands:execute` is the widest scope in this API
A key holding it can run any admin command your server allows: kick and ban players, spawn items, teleport, change the weather, shut the server down. Only your server's own command blocklist narrows that; this API does not.
Issue it sparingly, keep it out of read-only keys, and pair it with a per-key IP allowlist.
POST /v1/commands
Commands are sent structured: the verb and its arguments stay separate fields instead of one typed-out line. You never have to guess quoting rules, and an argument containing spaces just works.
Scope: commands:execute.
| Field | Type | Meaning |
|---|---|---|
verb | string | The command name, one token (e.g. Announce). |
args | string[] | Positional arguments, in the order the command declares them. Optional. |
subject | object | Who or where the command applies to. Optional. |
subject is either a player or a point, never both:
{ "player": "76561198000000000" }— a Steam64 or adiscord:123456789012345678ref.{ "x": 1000, "y": 2000, "z": 300 }— a world position.
Omit it for a server-wide command.
The two ref forms behave differently. A discord: ref has to resolve: an unlinked id returns
404 and nothing is dispatched. A well-formed Steam64 is taken at face value, since a command
may target someone with no roster entry yet — it dispatches either way, and subject.discord_id
comes back null when nothing was found.
POST /v1/commands
Content-Type: application/json
{ "verb": "Announce", "args": ["Server restarting in 10 minutes"] }{ "verb": "Announce", "success": true, "result": "OK|{}", "subject": null }`success: false` is still a 200
A 200 means the command reached the game server. Whether the game accepted it is in success,
with the server's own explanation in result — for example
"No online player to route the command through".
Check success, not just the status code. HTTP errors here mean transport, authorization or
validation problems, never a command the game declined.
GET /v1/commands/catalog
Every command the server exposes, with its typed arguments. Use it to discover what you can run and to check arguments before sending them.
Scope: commands:read.
{
"version": 1,
"data": [
{
"verb": "ChangeCurrencyBalance",
"level": 1,
"target_mode": "arg_targeted",
"output_mode": "silent",
"requires_online": true,
"direct_spawn_capable": false,
"args": [
{
"name": "Currency Type",
"type_name": "Currency Type Name",
"description": "",
"data_type": "String",
"completion": "CurrencyType",
"required": true,
"repeating": false,
"values": ["Normal", "Gold"]
}
]
}
]
}requires_online— the command needs at least one connected player to carry it.values— the allowed options for a fixed-choice argument. Empty when the values are dynamic (an item name, a player, a vehicle); resolve those yourself.level— the executor rank the game requires:0regular,1admin,2super admin,3elevated,4developer.
Commands come back sorted by verb, so identical responses are byte-identical. The endpoint
supports conditional requests: send the ETag back as If-None-Match and
you get a 304 while the catalog is unchanged.
503 right after a server restart
The catalog is assembled once the game server finishes loading. Until then this endpoint returns
503 server_unavailable with a retry hint. Wait a few seconds and call again.
GET /v1/commands/history
Commands that have been executed, newest first, whatever ran them: this API, the panel console, a Discord command or a scheduled task.
Scope: commands:read.
| Query | Meaning |
|---|---|
command | Match commands containing this text. |
start_date / end_date | Bound the window, inclusive. See the format note below. |
limit, cursor | Page size (1–100, default 50) and the opaque cursor from next_cursor. |
Date bounds take either YYYY-MM-DD or YYYY-MM-DD HH:MM:SS[.mmm], on the game server's local
clock — the same one the at field reports. A bare start_date expands to 00:00:00.000 on that
day and a bare end_date to 23:59:59.999, so a single day is start_date=end_date. Anything
else is rejected with 400.
{
"data": [
{
"at": "2026-08-04 10:00:00",
"command": "Announce Server restarting in 10 minutes",
"result": "OK",
"actor": { "steam_id": "76561198000000009", "discord_id": "123456789012345678" }
}
],
"has_more": true,
"next_cursor": "eyJvZmZzZXQiOjUwfQ"
}actor is the operator who ran the command: a steam_id, with discord_id beside it when that
operator is linked. Both are null when the entry could not be attributed to a person, and
actor itself is null on entries written by a game server version older than this field.
Caller IP addresses are not returned and cannot be filtered
The game server records a network address for unattributed entries, and it can belong to an
administrator's browser. commands:read keys never see it, and there is no ip filter either:
the game server matches that field as a substring, so filtering would let a key recover those
addresses one octet at a time. Server owners still see them in the panel console.
The history is never cached — it records who did what on your server.
Errors
| Status | Code | When |
|---|---|---|
400 | validation_error | A multi-token verb, control characters in an argument, or a subject carrying both a player and coordinates. |
403 | insufficient_scope | The key lacks commands:execute or commands:read. |
404 | not_found | A discord: subject.player ref with no linked player. A Steam64 ref never 404s here. |
503 | server_unavailable | The game server is not connected, or its command catalog is not built yet. |