WardenCoredocs
APIEndpoints

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.

FieldTypeMeaning
verbstringThe command name, one token (e.g. Announce).
argsstring[]Positional arguments, in the order the command declares them. Optional.
subjectobjectWho or where the command applies to. Optional.

subject is either a player or a point, never both:

  • { "player": "76561198000000000" } — a Steam64 or a discord:123456789012345678 ref.
  • { "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: 0 regular, 1 admin, 2 super admin, 3 elevated, 4 developer.

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.

QueryMeaning
commandMatch commands containing this text.
start_date / end_dateBound the window, inclusive. See the format note below.
limit, cursorPage 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

StatusCodeWhen
400validation_errorA multi-token verb, control characters in an argument, or a subject carrying both a player and coordinates.
403insufficient_scopeThe key lacks commands:execute or commands:read.
404not_foundA discord: subject.player ref with no linked player. A Steam64 ref never 404s here.
503server_unavailableThe game server is not connected, or its command catalog is not built yet.

On this page