The server answers plain HTTP requests under /api/, on the same port as
the consoles. It is how the editors read content and save some of it, and it is how a tool
of your own — a score board, a stream overlay, a test script — can look at a running game
without opening a console.
This page covers the basics: where the API is, how requests and answers look, and how
writing is authorized. The endpoints themselves are on three pages:
- Web API Live State: the running session — status, mission, vessels, contacts,
player ships, server load.
- Web API Content: authored content — missions, vessel classes, components, factions,
models, maps, roles, tasks, modules and the rest.
- Web API Writes: creating, replacing and deleting content, and the rules that apply.
For the browser-side wrappers a widget or editor uses (CallAPI,
WriteAPI and the rest) see Server Commands. For what runs inside the
server, see Mission Scripting.
Where it is
http://<server>:1864/api/<name>
Port 1864 is the server's web port, the one players type into a browser. It is set by
<webserver port="1864"> in the server's config.xml. When the
server also runs its TLS port (1866 by default) the API is there too. There is nothing
separate to open or forward: a machine that can load a console can reach the API.
Everything is JSON. Answers come back as application/json, and a write
sends its record as a JSON body.
Paths
| Path | Means |
/api/{name} | The whole collection, such as every faction. |
/api/{name}/{id} | One record by its key, such as one faction. |
/api/{name}/{module}/{id} | One record in a named module. Used to read
encyclopedia entries and by the deletes that have to say which module's copy goes. |
Endpoint names are not case sensitive. Ids are usually matched exactly, case included; a
few kinds, such as roles, task definitions and bots, ignore case. Encode anything that is
not a letter or a digit: the role Drone: Cargo is
/api/archetypes/Drone%3A%20Cargo.
Reads and writes
Reads are open. A GET needs no token. It returns the data
itself, with no envelope around it. A record that does not exist comes back as
200 with an empty body (a few endpoints answer {} instead), so
test for an empty answer rather than for a status code. A name the server
does not know is a 404 with an empty body.
Writes need the token. POST, PUT and
DELETE must carry it, and they always answer with an envelope:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": "module-sealed" }
The error is a short code rather than a sentence, so a tool can branch on it.
Web API Writes lists the codes and the status that goes with each.
The token
Every server has one API token. It lives in config.xml:
<server>
...
<api-token>3f9c0d...</api-token>
</server>
If there is none when the server starts, it makes one, writes it to
config.xml and notes it in debug.log. Send it as a bearer
token:
Authorization: Bearer 3f9c0d...
The token is not scoped. Any request that carries it can make any write the API
offers, so treat it like a password to your content.
On the server machine, a program can ask for it:
GET /api/auth/token -> { "token": "3f9c0d..." }
The server answers that only to a caller on the same machine — 127.0.0.1,
::1 or localhost. Anyone else gets 403. This is how
Hydra Studio gets the token when you open it at http://localhost:1864/; the
editor and admin pages also receive it from the server as they are served. See
Modding With Hydra Studio.
From another machine, copy the token from config.xml into
the tool that needs it.
To change it, stop the server and edit the value, or clear it to have a
new one made at the next start. Anything that kept the old one, such as the editor's cached
copy in the browser, has to be given the new one.
The endpoints behind Hydra Studio's assistant (POST /api/model/...) follow
a slightly different rule: they accept a caller on the server machine without the token,
and anyone else only with it.
Calling it from another site
The API sends no cross-origin headers. A web page served from somewhere other than the
game server cannot read its answers from a browser. Pages the game server serves itself
(consoles, widgets, the editors) can, and so can scripts and command-line tools, which are
not bound by the browser's rules.
The catalog
The server describes its own read endpoints:
GET /api/endpoints
{
"endpoints": [
{ "name": "status", "summary": "Current server state: mode, running mission, player.",
"takes_id": false, "save_command": null },
{ "name": "components", "summary": "Component definitions: power, weapons, sensors, shields.",
"takes_id": true, "save_command": "COMPONENT-SAVE" },
...
],
"usage": "GET /api/{name} lists; GET /api/{name}/{id} returns one, where takes_id is true."
}
The catalog is built from the same table the server answers reads from, so it cannot fall
out of date. It lists reads only: /api/auth/token, the writes and the
assistant endpoints are not in it. save_command names the command the editors
save that kind of record with (see Server Commands), and is null for kinds
that have none.
A first look with curl
curl ships with Windows 10 and later; in PowerShell type curl.exe, because
plain curl there means something else.
curl http://localhost:1864/api/status
{"State":"Playing","Mode":"Mission","Mission":"Fighter Escort","Players":"Horizons"}
curl http://localhost:1864/api/factions/Daichi
curl http://localhost:1864/api/archetypes/Pirate
curl http://localhost:1864/api/validate/Horizons
Watching a game from a script
The most common outside use is a program that polls. This one prints the player ships
once a second while a mission is being played:
import json, time, urllib.request
BASE = "http://localhost:1864/api/"
def get(path):
with urllib.request.urlopen(BASE + path) as reply:
body = reply.read().decode("utf-8")
return json.loads(body) if body.strip() else None
while True:
status = get("status")
if status and status["State"] == "Playing":
for v in get("vessels") or []:
if v.get("IsPlayer"):
print(v["Name"], v["Integrity"], "/", v["MaxIntegrity"], v["Map"], v["Planet"])
time.sleep(1)
Keep the rate sensible. Each call to /api/vessels builds the whole list,
and once a second is plenty for a score board. Web API Live State describes the
fields.
The API or packets?
The API answers when it is asked. Packets arrive the moment something changes. A display
that has to keep pace with the game — a gauge, a radar, anything that moves — belongs in a
widget that accepts packets; see Packets And Topics and Writing A Widget. The API
is for reading content, for checks now and then, and for tools that live outside the
game.