Alderon Server REST API
HTTP/JSON administration API for a Path of Titans dedicated server — a resource-oriented alternative to Source RCON. Enable it with -EnableServerApi and set [AlderonServerAPI] Port + ApiKey in Game.ini.
Base URL: http://<host>:<port>/v1/api — 77 endpoints, OpenAPI 3.2.0 served at GET /openapi.json.
Conventions
Auth
Every request needs Authorization: Bearer <ApiKey>. The API is fail-closed: a missing/wrong key returns 401. Each endpoint requires one permission (listed per-endpoint below, via GET /permissions, and as x-permission in OpenAPI). The default ApiKey has full access; ReadOnlyApiKeys hold only read. Define finer keys in [AlderonServerAPI] by assigning a role or an explicit permission list:
+ApiRole=(Name="Moderator", Permissions="read,players,bans,whitelist,chat,reports") +ApiKeyDef=(Key="d41d8cd98f00b204e9800998ecf8427ee2c1f0a9b7d3c6584f21ba90de7c3315", Role="Moderator") +ApiKeyDef=(Key="9b1c7e42a5f80d36c4e19af27b0d58e63a7c21fd48b6e095c3d7a1f2b8604e75", Permissions="read,rcon")Keys must be at least
MinApiKeyLength characters (default 64) or the server refuses them at startup and logs which were too short — the key is the only thing protecting full server administration and it is reachable over the network. Generate them: openssl rand -hex 32. Permission tokens: read, players, bans, whitelist, admins, chat, world, settings, config, maintenance, reports, rcon, database (* = full). A key missing an endpoint's permission gets 403. Note reads are not all read: /config, /webhooks, /database and /backups need their own token even for a GET.Transport
Plain HTTP. Intended for localhost / a private network, or behind a TLS-terminating reverse proxy. Do not expose it directly to the internet.
Response envelope
Responses are JSON objects with a success boolean. Reads put their payload under a named key (e.g. players); actions return a human string under response. Errors use the matching HTTP status and a body of {success:false, error, code}, where code is a stable machine-readable slug (bad_request, unauthorized, forbidden, not_found, rate_limited, server_error).
Rate limits & safety
Requests are rate-limited (a 429 includes a Retry-After header), the client IP can be allowlisted, and admin actions require an admin-scoped key.
Content-Length
Requests with a body must send Content-Type: application/json and an explicit Content-Length — the server rejects chunked transfer encoding (411). Bodyless POST/PUT/DELETE should send Content-Length: 0.
Encoding
Query values are x-www-form-urlencoded — both %20 and + decode to a space, so reason=a+b and reason=a%20b are equivalent.
Player IDs (agid)
Path params named {agid} accept either the dashed Alderon display id (525-053-709) or the raw 9-digit id (525053709) — targeted actions and GET /players/{agid} both normalize the input.
Discovery
GET /
readonly — Endpoint index
Response — endpoints[] (method, path, scope, summary) + openapi link.
GET /openapi.json
readonly — OpenAPI 3.2 description of this API
Response — A tool-consumable OpenAPI 3.2.0 document — path params, request bodies, typed error responses (shared Error schema), tags, operationIds, and bearer security. Point Swagger UI / Postman / a code generator at it.
3.2 specifically, not 3.0/3.1: it is the first version with query as a first-class operation key, so it is the only one that can describe QUERY /events at all. Earlier versions have a closed set of operation keys and would silently omit that endpoint. Tooling that only understands 3.0/3.1 may reject the document or skip the query operation.
GET /healthz
readonly — Lightweight liveness probe (for monitoring)
Response — status: "ok" + uptimeSeconds. Cheap — no game-thread work.
GET /readyz
readonly — Readiness probe: is the server joinable yet?
Response — ready: true with 200 once the world is loaded, has begun play, is not tearing down, and the game mode + session are up. Otherwise 503 with reasons[] naming what is missing. Distinct from /healthz, which only says the process is alive: use this one for orchestration and load-balancer membership, since it answers with the status code rather than in the body.
GET /permissions
readonly — List the API permission catalog
Response — permissions[] — the tokens you can assign to keys/roles: read, players, bans, whitelist, admins, chat, world, settings, config, maintenance, reports, rcon.
GET /capabilities
readonly — Feature tokens this server supports
Response — apiVersion + features[] — named tokens for optional behaviour, so a client can tell what this server supports instead of guessing from a version number. Purely additive: tokens are never renamed or removed, and an absent token means not supported. A server too old to have this endpoint returns 404, which clients should read as “baseline server”, not as an error.
Example response
{
"success": true,
"apiVersion": 1,
"count": 15,
"features": [
"waters",
"players.groupId",
"backups.list",
"list.pagination"
]
}GET /commands
readonly — List of RCON/console commands (DumpCommands)
Response — commands[] — the console commands available via POST /rcon.
Server
GET /info
readonly — Server identity + population summary
Response — serverName, motd, discord, playersOnline, maxPlayers, mapName, gameVersion (e.g. 1337h1), buildRevision, engineVersion, uptimeSeconds.
Example response
{
"success": true,
"serverName": "My Server",
"playersOnline": 12,
"maxPlayers": 100,
"mapName": "Panjura",
"gameVersion": "1337h1",
"uptimeSeconds": 84213
}GET /metrics
readonly — Performance metrics
Response — fps, frameTimeMs, playersOnline, uptimeSeconds.
GET /log
readonly — Recent server log lines (tail)
Query parameters
| Name | Description |
|---|---|
count | int, optional (default 200) — number of trailing lines |
Response — lines[] (strings, oldest first).
POST /save
admin — Force a world/database save
Response — response string.
POST /restart
admin — Schedule a restart
Request body
| Name | Description |
|---|---|
seconds | int, optional — delay before restart |
message | string, optional — broadcast shown to players |
Example request body
{ "seconds": 300, "message": "Restarting for an update in 5 min" }POST /cancelrestart
admin — Cancel a scheduled restart
GET /restart/schedule
readonly — Recurring auto-restart schedule
Response — bServerAutoRestart, restartLengthInSeconds, times[] etc.
PUT /restart/schedule
admin — Update the recurring auto-restart schedule
Request body
| Name | Description |
|---|---|
(schedule object) | same shape returned by GET /restart/schedule |
POST /rcon
admin — Run any RCON/console command
Note: Requires bAllowGenericCommand=true on the server.
Request body
| Name | Description |
|---|---|
command | string, required — the console command line |
Example request body
{ "command": "Announce Server maintenance in 10 minutes" }Players
GET /players
readonly — Connected players
Response — players[] — each: name, alderonId, ping, role, roleHierarchy, platform, species, gender, growth, growthStage, marks, location{x,y,z}, spectator, characterSelect, inHomeCave, groupId, groupLeader, discordId. groupId/groupLeader appear only when the player is in a party, and discordId only when they have a Discord account linked.
Example response
{
"success": true,
"players": [
{
"name": "RexHunter",
"alderonId": "525053709",
"ping": 42,
"species": "Tyrannosaurus",
"growth": 1.0,
"spectator": false,
"characterSelect": false
}
]
}GET /players/
readonly — A single connected player, or 404
Path parameters
| Name | Description |
|---|---|
agid | dashed (525-053-709) or raw (525053709) id |
Response — A single player object (as in /players).
DELETE /players/
admin — Kick a player
Path parameters
| Name | Description |
|---|---|
agid | dashed display id, e.g. 525-053-709 |
Query parameters
| Name | Description |
|---|---|
reason | string, optional — shown to the kicked player |
POST /players/{agid}/heal
admin — Heal a player
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
POST /players/{agid}/cleareffects
admin — Clear a player's buffs/debuffs (leaves state effects alone)
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
POST /players/{agid}/damage
admin — Deal damage to a player. No instigator is recorded, so a lethal hit is not credited to anyone as a kill
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
Request body
| Name | Description |
|---|---|
amount | number — damage to apply, must be > 0 |
POST /players/{agid}/growth
admin — Reward growth to a player
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
Request body
| Name | Description |
|---|---|
amount | number — growth to add (0..1 scale) |
POST /players/{agid}/marks
admin — Add/remove a player's marks
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
Request body
| Name | Description |
|---|---|
delta | int — signed change to the player's marks |
POST /players/{agid}/mute
admin — Server-mute/unmute a player
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
Request body
| Name | Description |
|---|---|
muted | bool — true to mute, false to unmute |
POST /whisper
admin — Direct-message one player
Request body
| Name | Description |
|---|---|
alderonId | dashed display id (alias accepted: agid) |
message | string |
Moderation
GET /mutes
readonly — Persisted server mutes
Query parameters
| Name | Description |
|---|---|
offset | int, optional |
limit | int, optional |
Response — mutes[] — each: alderonId, expiration (0 = permanent, else a unix timestamp), adminReason, userReason, expired. This is the PERSISTED store, not the connected players: a mute outlives the session that applied it, so offline players appear here too. expired is reported rather than left to the client, because an expired entry survives in the file until the next load prunes it.
GET /bans
readonly — List active bans
Query parameters
| Name | Description |
|---|---|
offset | int, optional — skip this many (default 0) |
limit | int, optional — max to return (default: all) |
Response — total, count + bans[] — each: alderonId, ipAddress, expiration (0 = permanent), adminReason, userReason.
PUT /bans/
admin — Ban a player
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
Request body
| Name | Description |
|---|---|
reason | string, optional |
DELETE /bans/
admin — Lift a ban
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
GET /whitelist
readonly — List whitelisted players
Query parameters
| Name | Description |
|---|---|
offset | int, optional — skip this many (default 0) |
limit | int, optional — max to return (default: all) |
Response — enforced (bool), total, count + whitelist[] — each: alderonId.
PUT /whitelist/
admin — Whitelist a player
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
DELETE /whitelist/
admin — Remove from whitelist
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
GET /admins
readonly — Players with an assigned role, including offline ones
Response — count + admins[] — each: alderonId, role, online plus name when the player is currently connected. Read from the persisted role assignments, so OFFLINE admins are included — unlike /players, which only shows who is on right now. A player promoted by name while offline is stored under that name, and is reported verbatim in alderonId.
PUT /admins/
admin — Promote (assign an admin role)
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
Request body
| Name | Description |
|---|---|
role | string — role name to assign |
DELETE /admins/
admin — Demote (remove admin roles)
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
GET /reports
readonly — In-game player reports queue
Query parameters
| Name | Description |
|---|---|
offset | int, optional — skip this many (default 0) |
limit | int, optional — max to return (default: all) |
Response — total, count + reports[] — each: id, time, reporterName, reporterAlderonId, reportedName, reportedAlderonId, reason, category, resolved.
PATCH /reports/
admin — Update a report. Every field is optional and only applied when present, so you can assign a ticket without clearing its reply. Sending a reply shows it to the reporter in game immediately if they are online, and on their next join if not
Path parameters
| Name | Description |
|---|---|
id | report id |
Request body
| Name | Description |
|---|---|
status | string, optional — open | investigating | resolved | rejected |
assignedTo | string, optional — moderator handling it |
action | string, optional — action taken |
response | string, optional — reply shown to the reporter in game |
resolved | bool, optional — kept in step with status |
Database editor
GET /database/characters/
admin — Every stored character record for a player, including deleted ones (so a character removed by mistake can be recovered). Needs the 'database' permission, which is deliberately NOT part of an ordinary admin key — these are the raw save records
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
PUT /database/characters/{agid}/
admin — Overwrite one character record. Takes a backup first. Rejected without confirm:true, because a malformed record costs the player their character and the only undo is that backup. The player must reconnect for the change to load
Path parameters
| Name | Description |
|---|---|
agid | dashed display id |
cid | character id |
Request body
| Name | Description |
|---|---|
data | object — the complete character record |
confirm | bool — must be true |
Chat & Events
GET /chat
readonly — Recent player chat messages
Response — messages[].
POST /chat
admin — Post a message to global chat as the server
Request body
| Name | Description |
|---|---|
message | string |
POST /announce
admin — Broadcast a message (HUD announcement)
Request body
| Name | Description |
|---|---|
message | string |
QUERY /events
readonly — Filtered event search (structured body, GET semantics)
Request body
| Name | Description |
|---|---|
types | array of strings, optional — only these event types |
contains | string, optional — substring match on the event text |
count | int, optional — how many to return |
Response — The same events[] shape as GET /events, filtered. Every field is optional, and an empty body is equivalent to the plain newest-N tail.
HTTP QUERY is a safe, idempotent read that carries a body (draft-ietf-httpbis-safe-method-w-body): GET semantics with POST transport, for filters too structured for a query string. It needs an engine that recognises the verb — older builds reject it before routing, so check the query.events capability first.
Example request body
{
"types": ["PlayerKilled"],
"contains": "Rex",
"count": 50
}GET /events
readonly — Recent server events (webhook feed)
Query parameters
| Name | Description |
|---|---|
count | int, optional (default 200) |
Response — events[] — the same events webhooks emit (joins, kills, admin actions...).
World
GET /world
readonly — Current weather + time of day
Response — weather, timeOfDay (+ related fields).
POST /world/weather
admin — Set the weather
Request body
| Name | Description |
|---|---|
type | string — weather type name |
POST /world/time
admin — Set the time of day
Request body
| Name | Description |
|---|---|
timeOfDay | string/number — e.g. "day", "night", or 0..2400 |
GET /waters
readonly — Water bodies + current quality
Response — count + waters[] — each: identifier, quality (0..1).
POST /waters/refresh
admin — Refresh water quality to full
Query parameters
| Name | Description |
|---|---|
identifier | string, optional — one water body; omit for all |
Response — refreshed — number of water bodies set back to full.
Settings & Config
GET /settings
readonly — Current mutable server settings
Response — Flat object of setting key -> current value.
GET /settings/all
readonly — All mutable settings with metadata + values
Response — settings[] — each: key, value, type, default, needsRestart, description.
PATCH /settings
admin — Update server settings
Request body
| Name | Description |
|---|---|
(key: value) | object of setting keys to new values |
Response — results — per-key outcome.
Example request body
{ "MaxPlayers": 100, "bServerPassword": false }GET /config/
admin — Read a raw config ini
Path parameters
| Name | Description |
|---|---|
which | game | engine | curveoverrides | mods | webhooks |
Response — content — the raw ini text — plus exists. For mods and webhooks, an absent dedicated file falls back to the legacy location and sets usingFallback (see the config.legacyFallback capability). A file that is simply not there (Mods.ini and Webhooks.ini are optional) answers 200 with exists: false and empty content, NOT a 500. Keep the two apart: for Mods.ini, absent means "fall back to GameUserSettings" while present-but-empty means "no mods", so collapsing them would turn a save into a silent mod wipe.
PUT /config/
admin — Overwrite a raw config ini (needs restart)
Path parameters
| Name | Description |
|---|---|
which | game | engine | curveoverrides | mods | webhooks |
Request body
| Name | Description |
|---|---|
content | string — full ini file text |
Response — needsRestart + bytes. The previous contents are copied to <name>.ini.bak first, and the write is staged to a temp file and atomically renamed, so a failed write cannot truncate the live config. Creates the file if it did not exist.
POST /config/{which}/reload
admin — Re-read a config ini that was edited outside the API
Path parameters
| Name | Description |
|---|---|
which | game | engine | curveoverrides | mods | webhooks |
Response — Picks up a hand edit or a hosting-panel change without a restart. Branch-shape aware: game/engine rebuild the whole combined hierarchy (Default*.ini layers plus the generated file), standalone files re-read in place. Audited into the events feed like a write.
Player state
POST /players/{agid}/teleport
admin — Teleport a player to coordinates, a POI, or another player
Path parameters
| Name | Description |
|---|---|
agid | player id, dashed or raw |
Request body
| Name | Description |
|---|---|
destination | string — a player name, a POI name, or Unreal coordinates (X=..,Y=..,Z=..) |
x / y / z | numbers — an alternative to destination for coordinates, which is what a JSON client naturally produces |
safe | bool, optional (default true) — a safe teleport finds ground and avoids dropping the player inside geometry. Send false to place them exactly where asked. |
Response — The command's response text. Forwards to the same Teleport RCON command an admin would type, so player resolution and permissions match chat.
Example request body
{
"destination": "(X=45000,Y=-60000,Z=1500)"
}GET /players/{agid}/attributes
readonly — Read a player's live gameplay attributes
Path parameters
| Name | Description |
|---|---|
agid | player id, dashed or raw |
Response — The GetAllAttr response text — health, stamina, growth and the rest of the attribute set. Returned as the command's own text rather than parsed: the set is large and game-specific, and parsing it in the plugin would put a second copy of that knowledge out of step with the game.
POST /players/{agid}/attributes
admin — Set one live gameplay attribute
Path parameters
| Name | Description |
|---|---|
agid | player id, dashed or raw |
Request body
| Name | Description |
|---|---|
name | string — the attribute, e.g. Health or Growth. Must be a plain identifier; the game resolves it and reports an unknown name as an error. |
value | number — the new value |
Response — The SetAttr response text. These exist so an integration does not need bAllowGenericCommand, a far broader grant than “may adjust a player’s health”.
Example request body
{
"name": "Health",
"value": 1450
}MOTD & rules
GET /motd
readonly — Server MOTD, with its character limit
Response — The text under both motd and content, plus length and maxLength (16384) so you can check before sending rather than after a 400.
PUT /motd
admin — Replace the server MOTD
Request body
| Name | Description |
|---|---|
motd | string — the new MOTD |
content | string — accepted as an alias for the above |
Response — Applies live; players see it immediately, so needsRestart is false. Over the limit is refused with a 400 naming the length and the limit — the setter would otherwise truncate silently. Newlines and the in-game rich-text tags both work. Needs the settings permission.
Example request body
{
"motd": "Welcome!\nRaiding is allowed."
}GET /rules
readonly — Server rules, with their character limit
Response — As GET /motd, under rules / content.
PUT /rules
admin — Replace the server rules
Request body
| Name | Description |
|---|---|
rules | string — the new rules |
content | string — accepted as an alias for the above |
Response — As PUT /motd. Both are also readable and writable through /settings; these exist because a single-value resource is the shape a bot actually wants. Stored as Saved/MOTD.txt and Rules.txt, not in an ini, which is why they do not appear in the config editor.
Webhooks
GET /webhooks
admin — Configured webhooks, section defaults, and every trigger this build can fire
Response — globals (the [ServerWebhooks] defaults actually in effect), webhooks[] (each: key, url, bAllowRetries, retryCount, timeoutSeconds, maxQueued, critical, perKeyOverrides) and availableKeys[] — every trigger name this build can fire, so a client can offer a picker instead of expecting the owner to know them. Per-key values are the EFFECTIVE ones: a key with no overrides reports the section defaults, and perKeyOverrides says which it is.
Needs the config permission, not read. A webhook URL is a bearer credential — whoever holds a Discord webhook URL can post to that channel as the server indefinitely — so listing them is exactly as privileged as reading Game.ini.
PUT /webhooks
admin — Replace the webhook set
Request body
| Name | Description |
|---|---|
webhooks | array — the FULL set to keep; an omitted entry is a deletion, and an entry with an empty url is removed rather than written blank |
globals | object, optional — the [ServerWebhooks] section defaults |
Response — count written. Applies immediately (needsRestart: false): webhook config is re-read on every trigger. The section is rewritten wholesale, and a struct line is only emitted for a key that genuinely overrides a default, so a simple config stays simple. Keys are validated as plain identifiers before becoming ini key names.
Curves
GET /curves
readonly — Active curve/stat overrides
Response — The overrides set in the server ini alongside what actually resolved and applied, so a value that was accepted but never matched a real curve is visible rather than silently ignored.
PUT /curves
admin — Replace the server's curve overrides
Response — Applies live and persists to CurveOverrides.ini — no restart. Replaces the whole set, so send the full list, not a delta.
Roles
GET /roles
readonly — Configured player roles + permissions
Response — roles[] — each role with its permissions/flags (incl. spectatorAccess).
PUT /roles/
admin — Create or edit a role definition (hot-reloaded)
Path parameters
| Name | Description |
|---|---|
name | role name |
Request body
| Name | Description |
|---|---|
(role definition) | permissions/flags object |
DELETE /roles/
admin — Delete a role definition
Path parameters
| Name | Description |
|---|---|
name | role name |
Mods
GET /mods
readonly — Enabled + installed mods on this server
Response — enabledMods[] + installed[] — the installed list is the Mods folder itself (see mods.disk), each entry with hasFiles and detailsKnown.
GET /mods/available
readonly — Browse the Alderon mod catalog
Response — available[].
PUT /mods/
admin — Enable a mod (needs restart)
Path parameters
| Name | Description |
|---|---|
sku | mod SKU, e.g. UGC_..._SK |
Request body
| Name | Description |
|---|---|
buildSku | string, default "release" |
DELETE /mods/
admin — Disable a mod (needs restart)
Path parameters
| Name | Description |
|---|---|
sku | mod SKU |
Backups
GET /backups
admin — List database backups
Response — count + backups[] — each: id, name (id is what /backups/{id}/restore takes).
POST /backups
admin — Create a database backup
POST /backups/{id}/restore
admin — Restore a database backup
Path parameters
| Name | Description |
|---|---|
id | backup id |