REST API Reference

Complete reference for all HTTP endpoints.

12 min read

All endpoints require JWT authentication unless noted otherwise. Send the token in an Authorization: Bearer <token> header. Obtain a token via POST /api/auth/login or the registration flow.

Health & Configuration

Diagnostic endpoints for monitoring server state. Health endpoints are safe to poll frequently and do not mutate data.

GET /api/health No auth required
Basic health check returning server uptime and database connection status. Safe to use as a liveness probe.
Returns: { status, uptime_s, db }
GET /api/health/detailed Optional API key
Full system telemetry including CPU usage, RAM, disk, container status, and connected client count. Supply the optional API key via X-Health-Key header to bypass authentication entirely.
Returns: { status, uptime_s, db, cpu, memory, disk, containers, connected_clients, version }
GET /api/config/features
Returns the current feature toggle status for all feature flags. Clients use this response to conditionally render UI panels.
Returns: { chat, markers, files, overlays, voice, federation } — each boolean

Authentication

Endpoints for retrieving auth configuration and obtaining JWT tokens. When AUTH_REQUIRED=false is set on the server, tokens are still issued but route guards are relaxed.

GET /api/auth/config No auth required
Returns the server's authentication mode so the client can display or skip the login form appropriately.
Returns: { auth_required: boolean }
POST /api/auth/login No auth required
Authenticate with callsign and password. Returns a signed JWT and the user record. The endpoint is rate-limited to prevent brute-force attacks.
Body: { callsign, password }
Returns: { token, user: { id, callsign, role } }

Users

User registration and roster management. The first user to register is automatically promoted to the admin role.

POST /api/users/register No auth required
Register a new user account. The password field is optional when the server is in open-access mode.
Body: { callsign, password? }
Returns: { token, user: { id, callsign, role } }
GET /api/users/roster
Returns all active users along with their most recent position fix, if available. Used to populate the user roster panel.
Returns: array of { id, callsign, role, last_position, last_seen_at }
PUT /api/users/:id/role Admin only
Change the role of an existing user. Valid roles are observer, operator, and admin. Connected sessions reflect the new role immediately via Socket.IO.
Body: { role }
DELETE /api/users/:id Admin only
Deactivate a user account. The record is soft-deleted; historical positions and messages are preserved. The deactivated user's active Socket.IO connection is terminated.

Positions

Position history is stored in the positions table and pruned by the scheduled retention job. Live position updates flow over WebSocket; this REST endpoint is for historical track retrieval.

GET /api/positions/:userId/track
Returns a user's position history as a GeoJSON LineString feature, ordered chronologically. Useful for rendering track trails on the map.
Query: hours — lookback window in hours (default: 1, max: 72)
Returns: GeoJSON Feature<LineString> with recorded_at timestamps in properties

Chat

Channel discovery, history retrieval, and direct message setup. Real-time message delivery uses the chat:message Socket.IO event rather than REST.

GET /api/channels
List all channels the authenticated user is a member of, including the global General channel and any direct message threads.
Returns: array of { id, name, type, unread_count, last_message }
POST /api/channels Operator+
Create a named group channel and add the specified members. The creator is added automatically as a member.
Body: { name, member_ids: string[] }
GET /api/channels/:id/messages
Paginated message history for a channel. Uses cursor-based pagination — pass the id of the oldest message received as the before cursor to load the next page.
Query: limit (default: 50), before (message ID cursor)
Returns: { messages: [...], has_more: boolean }
POST /api/channels/dm
Find or create a direct message channel between the authenticated user and the specified user. Idempotent — returns the existing channel if one already exists.
Body: { user_id }

Markers

Markers are map features that unify points, lines, and polygons into a single system. Each marker has a marker_type (point, line, or polygon), optional name and category, and GeoJSON geometry stored in PostGIS. All mutation endpoints broadcast real-time updates via Socket.IO.

POST /api/markers Operator+
Create a new marker. The geometry field must be a valid GeoJSON geometry object (Point, LineString, or Polygon). Point markers support name, category, description, and photo attachments.
Body: { marker_type, geometry: GeoJSON.Geometry, name?, category?, description?, properties? }
GET /api/markers
List all markers. Supports bounding box, category, type, and radius filtering. Radius search requires lat, lng, and radius (metres).
Query: marker_type, category, bbox=west,south,east,north, lat, lng, radius
PUT /api/markers/:id
Update a marker's type, geometry, name, category, description, or properties. Only the original creator or an admin may update a marker.
Body: { marker_type?, geometry?, name?, category?, description?, properties? }
DELETE /api/markers/:id
Delete a marker. Only the original creator or an admin may delete a marker. Broadcasts marker:deleted to all connected clients.

Files

File upload, download, rename, and deletion with metadata stored in PostgreSQL and binaries on a persistent Docker volume. The MIME allowlist permits GeoJSON, GPX, KML, images, audio, video, and common document types.

POST /api/files Operator+
Upload a file using multipart/form-data. The server validates the MIME type against the allowlist and stores the file on the persistent volume. Broadcasts file:created to all clients.
Body: multipart/form-data with file field
GET /api/files
List all uploaded files with metadata and aggregate storage statistics. Includes file name, size, MIME type, uploader, and upload timestamp.
Returns: { files: [...], total_bytes, file_count }
GET /api/files/:id/download
Download the raw file binary. The response sets Content-Disposition: attachment to trigger a browser download. Authentication is required.
GET /api/files/:id/content
Parse a spatial file (GeoJSON or GPX) and return its contents as a normalized GeoJSON FeatureCollection along with a computed bounding box. Used by the overlay system to preview spatial files before enabling them as map layers.
Returns: { geojson: FeatureCollection, bbox: [west, south, east, north] }
PATCH /api/files/:id Uploader or Admin
Rename a file. The filename is sanitized before storage. Only the original uploader or an admin may rename a file. Broadcasts file:updated to all clients.
Body: { "filename": "new-name.ext" }
DELETE /api/files/:id
Delete a file and its associated metadata. Only the original uploader or an admin may delete a file. Also removes any active overlays referencing this file.

Overlays

Overlays render uploaded spatial files as interactive map layers. Both vector overlays (GeoJSON, GPX) and georeferenced raster image overlays are supported.

POST /api/overlays Operator+
Enable an uploaded file as a map overlay layer. The server parses the file, computes the bounding box, and persists the overlay configuration. Broadcasts overlay:created to all clients.
Body: { file_id, name?, style? }
GET /api/overlays
List all active overlay configurations including style settings, bounding boxes, and overlay type. Used by clients on initial load to reconstruct the map layer stack.
PATCH /api/overlays/:id
Update overlay style properties. For vector overlays: color, opacity, line_width. For image overlays: opacity and corners (four-corner georeferencing coordinates).
Body: { color?, opacity?, line_width?, corners? }
DELETE /api/overlays/:id
Disable and remove an overlay. The underlying file is preserved. Broadcasts overlay:removed to all connected clients.

Admin

All admin endpoints require the admin role. They power the web-based admin dashboard and are not intended for general client use.

GET /api/admin/status Admin only
Full system status telemetry: uptime, CPU, RAM, disk usage, Docker container states, connected client count, and server version string.
GET /api/admin/users Admin only
List all users (active and deactivated) along with their current Socket.IO connection status, connect time, and user-agent string where available.
GET /api/admin/logs Admin only
Retrieve recent log entries from the in-process Pino ring buffer (up to 1000 entries). Supports level and text filtering.
Query: level (e.g. warn, error), search (substring match)
GET /api/admin/export/archive Admin only
Generate and download a ZIP archive of selected data types. The archive includes timestamped JSON and GeoJSON files for each requested category.
Query: include=positions,chat,markers — comma-separated list

Setup

Setup endpoints are used exclusively by the first-run wizard. Once setup is complete, the PUT /api/setup/complete endpoint locks these routes.

GET /api/setup/status No auth required
Returns the current setup state and basic server information. The client reads this on startup to decide whether to show the first-run wizard or the main application.
Returns: { setup_complete: boolean, server_name, version, auth_required }
PUT /api/setup/complete Admin only
Mark first-run setup as complete. Persists the flag to the database and unlocks normal application access. This action cannot be undone via the API.
GET /api/ca-cert No auth required
Download the server's self-signed CA certificate as a PEM file. Clients on the local network install this certificate to trust the server's HTTPS connection without browser warnings.