Drone Battles API

Version 1.0

REST API for controlling drones and buildings in Drone Battles. IMPORTANT: All coordinates use the acting drone as the origin (0, 0). Both action requests (fire, identify, mark, scan, etc.) and response data (scan results, action completion messages) use relative coordinates.

Base URL: /api/v1

Authentication

Type: Bearer Token

All endpoints (except /auth/register and /auth/login) require authentication. Include the token in the Authorization header.

Authorization: Bearer <token>

Authentication

/auth

User registration, login, and session management

POST /auth/register Public

Register a new account (player or admin, depending on the code)

Request Body

username: string (required, min 3 chars)
password: string (required, min 6 chars)
name: string (required)
registration_code: string (required) - a team code registers a player on that team; the admin REGISTRATION code (separate from the admin API token) registers an administrator account. Team codes are single-use: one account per code (teammates share that account). Deleting the account frees its code; the admin registration code is not single-use.

Response

user_id: string
team: integer
message: string

Example

{
  "name": "Commander Alpha",
  "password": "secretpass123",
  "registration_code": "your-team-secret-code",
  "username": "player1"
}

Notes

Creates a new user with a command center, starter buildings, and 2 starter drones with mining equipment. Each registration_code is tied to a specific team.

POST /auth/login Public

Login and get access token

Request Body

username: string (required)
password: string (required)

Response

token: string
user_id: string
registration_codes: object (admin users only) - {admin_token, admin_registration_code, teams: {code: team_number}} so admins can hand out codes straight from login

Example

{
  "password": "secretpass123",
  "username": "player1"
}

Notes

If MFA is enabled the response is {mfa_required: true, mfa_token} instead; complete with POST /auth/login/mfa, whose response has the same shape as a normal login.

POST /auth/logout

Logout and invalidate token

Response

message: string
GET /auth/me

Get current user info

Response

user_id: string
username: string
name: string
team: integer
score: integer
drone_count: integer
building_count: integer
GET /auth/scoreboard Public

Get the current scoreboard (public, no auth required)

Response

scoreboard: [array of {name, user_id, team, score}]
team_scores: {team_number: total_score}

Notes

Returns all players sorted by score, plus aggregate team scores. Team 0 (admins) is excluded from team_scores.

Game

/game

Game-wide information (fog-of-war safe)

GET /game/status

Get the game clock: engine running state, current cycle, and cycle time

Response

running: boolean
paused: boolean
cycle_count: integer - current engine cycle
cycle_time: float - seconds per cycle
last_update: float - unix timestamp of the engine's last status write
match_state: string - active | frozen | ended; actions are rejected with HTTP 423 while frozen/ended

Notes

Use this to pace polling and time multi-cycle actions instead of reading the engine_status piggybacked on GET /drones.

Drones

/drones

Drone listing and information

GET /drones

List drone IDs owned by current user (POST /drones/<drone_id>/status to queue a status report)

Response

drone_ids: [array of drone ID strings]
POST /drones/<drone_id>/status

Queue a status action (1 battery, 1 cycle)

Response

action_id: string
message: status action queued successfully

Notes

Status information is returned via message queue when action completes. Costs 1 battery and takes 1 cycle.

Drone Actions

/drones/<drone_id>/<equipment>

Queue actions for drones. All actions return action_id on success.

Ammunition

1 endpoint
POST /drones/<drone_id>/ammo/transfer

Transfer ammunition to adjacent friendly drone

Request Body

target_q: int (relative)
target_r: int (relative)
equipment_id: string
level: int (1-3)

Example

{
  "equipment_id": "uuid",
  "level": 2,
  "target_q": 1,
  "target_r": 0
}

Notes

Transfers ammunition, howitzer shells, or howitzer charges to a friendly drone on an adjacent tile. The equipment moves with its current state (remaining rounds/charges). L1: 15 cycles, 1 batt/cycle. L2: 10 cycles, 2 batt/cycle. L3: 5 cycles, 6 batt/cycle. Target must be adjacent and on same team.

Auto-Cannon

3 endpoints
POST /drones/<drone_id>/auto_cannon/fire

Fire an auto-cannon round at target coordinates or in a direction

Request Body

target_q: integer - relative Q offset from drone (required if no direction)
target_r: integer - relative R offset from drone (required if no direction)
direction: integer (0-5) - fire in a hex direction instead of at coordinates (required if no target_q/target_r)
level: integer (1-3)

Example

{
  "level": 2,
  "target_q": 3,
  "target_r": 0
}

Requires

Auto-Cannon equipment + Ammunition (loaded via autocannon_load)

Notes

Accepts EITHER (target_q, target_r) as relative coordinates OR a direction (0-5), not both. Direction mode fires along the hex direction (0=NE, 1=SE, 2=S, 3=SW, 4=NW, 5=N) to max range. Fires a projectile round that travels tile-by-tile along a hex line path. Accuracy starts at 75% at close range and falls to 20% at max range (capped at 95% with bonuses). AP rounds (50 dmg) ignore armour and disappear at max range on a miss. HE rounds (30 dmg + 15 splash) always explode at max range even on a miss, dealing splash to adjacent tiles. Canister rounds fire an instant cone of flechettes (range 3, 5 dmg/tile). Requires line of sight (except canister, which handles its own blocking). Min range: 2 tiles. 10-cycle cooldown between shots. All weapons receive +30% accuracy bonus against LUCAS drones.

POST /drones/<drone_id>/auto_cannon/load

Load ammunition into auto-cannon

Request Body

ammo_type: string (armour_piercing, high_explosive, or canister_round)

Example

{
  "ammo_type": "armour_piercing"
}

Requires

Auto-Cannon equipment + Ammunition equipment

Notes

Takes 20 cycles to complete. The cannon must be loaded before it can fire. When a loaded ammo pack empties, another must be loaded. Valid ammo types: armour_piercing, high_explosive, canister_round. Canister rounds fire an instant cone of flechettes (range 3) along the line from drone to target.

POST /drones/<drone_id>/auto_cannon/unload

Unload ammunition from auto-cannon

Requires

Auto-Cannon equipment with loaded ammunition

Notes

Takes 10 cycles to complete. Unloads the currently loaded ammunition pack from the cannon. The ammunition must be unloaded before it can be transferred to another drone via ammo transfer.

Decoy Beacon

1 endpoint
POST /drones/<drone_id>/decoy_beacon/deploy

Deploy a decoy drone on an adjacent tile

Request Body

direction: integer (0-5, optional, default 0) - hex direction for target adjacent tile

Example

{
  "direction": 2
}

Requires

Decoy Beacon equipment (unlock_decoy_beacon research completed, decoys remaining)

Notes

Deploys a decoy drone on an adjacent tile. The decoy appears on scans and DF as a real drone but has no equipment and minimal HP. Consumable — equipment is removed when all decoys are deployed. Deploy cycles and battery cost vary by level.

DF Antenna

5 endpoints
POST /drones/<drone_id>/df_antenna/deploy

Deploy the DF antenna (prevents movement)

Requires

DF Antenna equipment

Notes

Takes 10 cycles. Drone cannot turn, drive, or hover while deployed. Must stow before moving again.

POST /drones/<drone_id>/df_antenna/stow

Stow the DF antenna (re-enables movement)

Requires

DF Antenna equipment (deployed, antenna at height 0)

Notes

Takes 10 cycles. Antenna must be lowered to height 0 before stowing.

POST /drones/<drone_id>/df_antenna/raise

Raise the DF antenna mast

Request Body

target_height: integer (optional) - target antenna height (default: current + 5, max 20)

Example

{
  "target_height": 10
}

Requires

DF Antenna equipment (deployed)

Notes

20 cycles per 5 units of height. Does NOT change drone elevation. Maximum antenna height is 20.

POST /drones/<drone_id>/df_antenna/lower

Lower the DF antenna mast

Request Body

target_height: integer (optional) - target antenna height (default: current - 5, min 0)

Example

{
  "target_height": 0
}

Requires

DF Antenna equipment (deployed, height > 0)

Notes

20 cycles per 5 units of height. Must lower to 0 before stowing.

POST /drones/<drone_id>/df_antenna/configure

Configure DF antenna cone direction and width for passive EM detection

Request Body

direction: integer (0-5, required) - hex direction for cone center
cone_width: integer (0-4, required) - 0=narrow line (±10°), 1=±30°, 2=±60°, 3=±120°, 4=360°
level: integer (1-3, optional) - detection sensitivity level

Example

{
  "cone_width": 2,
  "direction": 1,
  "level": 2
}

Requires

DF Antenna equipment (deployed)

Notes

Takes 1 cycle. Sets cone direction and width for passive EM monitoring. Once configured with antenna raised, the DF antenna passively detects electromagnetic signatures from drone actions within its cone each cycle. Detections are delivered as 'EM detection' messages with an 8-bin frequency spectrum and bearing direction. Levels: L1=30 range/threshold 3.0, L2=50 range/threshold 2.0, L3=75 range/threshold 1.0. LOS is checked at antenna height. Signature dampeners reduce detected signal strength. EM/DF antennas are LONG range (0-49 tiles at T0) — the primary strategic intelligence tool. Uses inverse square law decay: 1/(1 + d^2 * rate) with frequency-dependent attenuation (EM freq attenuation rate: 0.006). Higher frequencies fade faster at distance. Example T0 detection ranges: idle/deploy=0, driving=11, hovering=19, repair/charging=24-27, shield_gen=31, laser/mark=35, autocannon/missile=33-36, identify=38, scan/howitzer_fire=41, jammer=45, howitzer_aim/LUCAS_engine=49 tiles.

Drill

1 endpoint
POST /drones/<drone_id>/drill/mine

Mine resources from adjacent tile

Request Body

resource_q: integer (required)
resource_r: integer (required)
level: integer (1-3)

Example

{
  "level": 2,
  "resource_q": 15,
  "resource_r": 22
}

Requires

Drill and Hopper equipment

Notes

Takes 10 cycles. Mines ore from an adjacent resource node into the drone's hopper.

Hopper

1 endpoint
POST /drones/<drone_id>/hopper/dump

Discard resources from drone's hopper

Request Body

resource_type: string (optional) - specific resource to dump
amount: integer (optional) - amount to dump

Example

{
  "amount": 10,
  "resource_type": "titanium_ore"
}

Requires

Hopper equipment with cargo

Notes

Takes 1 cycle, costs 0 battery. If resource_type and amount are omitted, dumps all resources. If only resource_type is specified, dumps all of that type.

Hover Tech

2 endpoints
POST /drones/<drone_id>/hover_tech/ascend

Increase drone elevation

Request Body

target_elevation: integer (required)
level: integer (1-3)

Example

{
  "level": 2,
  "target_elevation": 50
}

Requires

Hover Tech equipment

Notes

Elevation change per action must be 1-5. If the drone's elevation changed between queue and execution (e.g. from a prior queued action), the action will fail with 'Invalid elevation change'. WARNING: Drones at elevation > 0 drain battery each cycle to maintain hover. Drain scales with elevation and drone weight. If battery reaches 0, drone crashes and takes fall damage.

POST /drones/<drone_id>/hover_tech/descend

Decrease drone elevation

Request Body

target_elevation: integer (required)
level: integer (1-3)

Example

{
  "level": 1,
  "target_elevation": 0
}

Requires

Hover Tech equipment

Notes

Elevation change per action must be 1-5. If the drone's elevation changed between queue and execution (e.g. from a prior queued action), the action will fail with 'Invalid elevation change'. Descending to elevation 0 stops hover battery drain. Recommended when low on battery to avoid crash damage.

Howitzer

6 endpoints
POST /drones/<drone_id>/howitzer/deploy

Deploy the howitzer for firing (prevents movement)

Requires

Howitzer equipment (unlock_howitzer research completed, drone at elevation 0, not hovering)

Notes

Level-dependent: L1=8 cycles/1 batt/cycle, L2=5 cycles/2 batt/cycle, L3=3 cycles/3 batt/cycle. Drone cannot move, turn, hover, or take movement actions while deployed. Must stow before moving again. Cannot deploy while hovering. Cannot deploy inside a base (ground tiles are too hard to anchor the howitzer).

POST /drones/<drone_id>/howitzer/stow

Stow the howitzer (re-enables movement)

Requires

Howitzer equipment (deployed)

Notes

Level-dependent: L1=8 cycles/1 batt/cycle, L2=5 cycles/2 batt/cycle, L3=3 cycles/3 batt/cycle. Re-enables movement after completion.

POST /drones/<drone_id>/howitzer/load_shell

Load a howitzer shell into the breech

Request Body

level: integer (1-3)

Example

{
  "level": 2
}

Requires

Howitzer equipment (deployed) + Howitzer Shells equipment

Notes

Consumes 1 shell from the Howitzer Shells equipment. Cycles: L1=8, L2=5, L3=3. Battery: L1=1/cycle, L2=2/cycle, L3=7/cycle.

POST /drones/<drone_id>/howitzer/load_charges

Load propellant charges to set range

Request Body

charges: integer (1-3, required) - number of charges to load
level: integer (1-3)

Example

{
  "charges": 2,
  "level": 2
}

Requires

Howitzer equipment (deployed, shell loaded) + Howitzer Charges equipment

Notes

Each charge adds +8 to min and max range. 1 charge: min 8/max 16. 2 charges: min 16/max 24. 3 charges: min 24/max 32. Consumes charges from Howitzer Charges equipment. Cycles: L1=8, L2=5, L3=3. Battery: L1=1/cycle, L2=2/cycle, L3=7/cycle.

POST /drones/<drone_id>/howitzer/aim

Aim the howitzer at target coordinates

Request Body

target_q: integer (required) - relative Q offset from drone
target_r: integer (required) - relative R offset from drone
level: integer (1-3)

Example

{
  "level": 2,
  "target_q": 15,
  "target_r": -10
}

Requires

Howitzer equipment (deployed, shell and charges loaded)

Notes

Aim speed: L1=2 cycles/tile, L2=1 cycle/tile, L3=1 cycle/tile. Battery: L1=1/cycle, L2=3/cycle, L3=8/cycle. Target must be within range determined by loaded charges. Coordinates are relative to drone position.

POST /drones/<drone_id>/howitzer/fire

Fire the howitzer at the aimed target

Request Body

level: integer (1-3)

Example

{
  "level": 2
}

Requires

Howitzer equipment (deployed, loaded, aimed)

Notes

Cycles: L1=8, L2=5, L3=3. Battery: L1=1/cycle, L2=2/cycle, L3=7/cycle. 10-cycle cooldown between shots. Accuracy: 30% direct hit, 30% scatter to adjacent tile, 30% scatter 2 tiles, 10% dud. Shell travels 2 tiles/cycle (8 tiles/second), ignores LOS (arcing). Damage: 100 direct, 50 splash ring 1, 25 splash ring 2. Buildings take 10% damage on direct hit only (no splash). Very loud seismic signature.

Jammer

6 endpoints
POST /drones/<drone_id>/jammer/deploy

Deploy the signal jammer (prevents movement)

Requires

Jammer equipment

Notes

Takes 10 cycles. Drone must be at elevation 0. Cannot turn, drive, or hover while deployed.

POST /drones/<drone_id>/jammer/stow

Stow the signal jammer (re-enables movement)

Requires

Jammer equipment (deployed, inactive, antenna at height 0)

Notes

Takes 10 cycles. Jammer must be deactivated and antenna lowered to 0 before stowing.

POST /drones/<drone_id>/jammer/raise

Raise the jammer antenna mast

Request Body

target_height: integer (optional) - target antenna height (default: current + 5, max 20)

Example

{
  "target_height": 10
}

Requires

Jammer equipment (deployed)

Notes

20 cycles per 5 units of height. Maximum antenna height is 20.

POST /drones/<drone_id>/jammer/lower

Lower the jammer antenna mast

Request Body

target_height: integer (optional) - target antenna height (default: current - 5, min 0)

Example

{
  "target_height": 0
}

Requires

Jammer equipment (deployed, inactive, height > 0)

Notes

20 cycles per 5 units of height. Jammer must be deactivated before lowering. Must lower to 0 before stowing.

POST /drones/<drone_id>/jammer/activate

Activate the signal jammer beam

Request Body

direction: integer (0-5, required) - hex direction for the jamming beam

Example

{
  "direction": 1
}

Requires

Jammer equipment (deployed, antenna raised, sufficient battery)

Notes

Takes 1 cycle. Projects a beam that jams ACTION_COMPLETED and WARNING messages for drones in its path. Drains battery each cycle while active. Levels: L1=15 range/3 battery, L2=25 range/8 battery, L3=40 range/15 battery. Affects both friendly and enemy drones (except the jammer drone itself).

POST /drones/<drone_id>/jammer/deactivate

Deactivate the signal jammer

Requires

Jammer equipment (active)

Notes

Takes 1 cycle. Stops jamming and battery drain.

Land Mine

1 endpoint
POST /drones/<drone_id>/land_mine/deploy

Deploy a land mine at the drone's current tile

Requires

Land Mine equipment (unlock_land_mine research completed, drone at elevation 0, mines remaining)

Notes

Deploys a mine on the drone's current tile. Mines are invisible to scan but visible to identify. Detonation triggers: ground-level drone movement (drive), hover descent to elevation 0, fall damage landing, weapon hits (laser, missile, auto-cannon). Mines deal full damage to the triggering drone and splash damage to adjacent tiles (if upgraded). Equipment is removed when all mines are depleted. Deploy cycles and battery cost vary by level (L1: 20 cycles/1 battery per cycle, L2: 15 cycles/3 battery, L3: 5 cycles/10 battery). Upgrades reduce deploy cycles and increase mine count.

Laser Cannon

1 endpoint
POST /drones/<drone_id>/laser_cannon/fire

Fire laser cannon at target tile

Request Body

target_q: integer (required)
target_r: integer (required)
level: integer (1-3)

Example

{
  "level": 3,
  "target_q": 1,
  "target_r": 0
}

Requires

Laser Cannon equipment

Notes

Targets relative coordinates (drone is at 0, 0). Engine auto-selects target: ground drones first, then elevated drones (by elevation), then buildings. Returns error if tile is empty. IMPORTANT: Lasers are top-mounted and cannot fire at targets below the attacker's height (terrain elevation + hover elevation).

Mobile Repair Kit

1 endpoint
POST /drones/<drone_id>/mobile_repair_kit/repair

Repair health or equipment at target coordinates

Request Body

target_q: integer (required)
target_r: integer (required)
level: integer (1-3)
repair_type: string ('health' or 'equipment', defaults to 'health')
equipment_id: string - full UUID (required if repair_type is 'equipment')

Example

{
  "level": 2,
  "target_q": 10,
  "target_r": 20
}

Requires

Mobile Repair Kit equipment

Notes

Auto-targets at coordinates. For health: priority is ground drones, elevated drones, then buildings. For equipment: specify the full equipment_id UUID from the drone's equipment list. Equipment repair durability: L1=25, L2=50, L3=75.

Point Defense

1 endpoint
POST /drones/<drone_id>/point_defense/activate

Activate point defense system for missile interception

Requires

Point Defense equipment (unlock_point_defense research completed)

Notes

Activates the PDS for the specified number of cycles (default 1), during which it intercepts incoming enemy missiles within range. Request body: {"cycles": int (optional, default 1)}. Interception chance: 30% per missile. Levels affect range and battery cost (L1: range 1/5 batt activate/30 per intercept, L2: range 2/10/25, L3: range 3/30/20).

Portable Charger

1 endpoint
POST /drones/<drone_id>/portable_charger/charge

Transfer battery to drone at target coordinates

Request Body

target_q: integer (required)
target_r: integer (required)
level: integer (1-3)

Example

{
  "level": 1,
  "target_q": 10,
  "target_r": 20
}

Requires

Portable Charger equipment

Notes

Auto-targets drone at coordinates. Ground-level drones prioritized over elevated.

Propulsion

2 endpoints
POST /drones/<drone_id>/propulsion/turn

Rotate drone left or right

Request Body

direction: integer (1=clockwise, -1=counter-clockwise)
level: integer (1-3)

Example

{
  "direction": 1,
  "level": 2
}

Requires

Propulsion equipment
POST /drones/<drone_id>/propulsion/drive

Move drone one tile forward or backward

Request Body

direction: integer (1=forward, -1=reverse)
level: integer (1-3)

Example

{
  "direction": 1,
  "level": 2
}

Requires

Propulsion equipment

Notes

Hovering drones can fly over buildings if high enough: above elevation 5 for 1x1 buildings, above 10 for 2x2, above 15 for 3x3 (including Command Centers). Difficult terrain (terrain_type 2) doubles the number of cycles required for ground movement.

Sensors

2 endpoints
POST /drones/<drone_id>/sensors/scan

Scan nearby tiles for terrain, resources, and units

Request Body

level: integer (1-3)

Example

{
  "level": 2
}

Requires

Sensors equipment

Notes

Returns tiles using relative coordinates where (0, 0) is the scanning drone's position. A tile at (1, -2) means 1 column east and 2 rows north of the drone.

POST /drones/<drone_id>/sensors/identify

Identify objects at target tile with detailed info

Request Body

target_q: integer (required)
target_r: integer (required)
level: integer (1-3)

Example

{
  "level": 1,
  "target_q": 2,
  "target_r": -1
}

Requires

Sensors equipment

Notes

Targets relative coordinates (drone is at 0, 0). Returns detailed info about drones, buildings, and resources at that location. Target must be within sensor range.

Shield Generator

2 endpoints
POST /drones/<drone_id>/shield_generator/charge

Charge drone's shields

Request Body

level: integer (1-3)

Example

{
  "level": 2
}

Requires

Shield Generator equipment
POST /drones/<drone_id>/shield_generator/transfer

Transfer shields to an adjacent drone

Request Body

target_q: integer (required)
target_r: integer (required)
level: integer (1-3, default 1)

Example

{
  "level": 1,
  "target_q": 10,
  "target_r": 20
}

Requires

None (all drones can transfer shields)

Notes

All drones have base max shields of 50. Target must be adjacent and at same elevation. L1: 3 cycles/25 shields/5 battery, L2: 2 cycles/20 shields/10 battery, L3: 1 cycle/15 shields/15 battery. Shield Generator research adds shield_rate_bonus to transfer amount.

Smoke Launcher

1 endpoint
POST /drones/<drone_id>/smoke_launcher/deploy

Deploy a smoke screen on the drone's current tile

Requires

Smoke Launcher equipment (unlock_smoke_launcher research completed, charges remaining)

Notes

Deploys a smoke screen on the drone's current tile and surrounding tiles (radius 1). Smoke blocks line-of-sight for scan and identify commands. Duration: 20 cycles. Consumable — equipment is removed when all charges are used. Deploy cycles and battery cost vary by level.

Targeting Computer

1 endpoint
POST /drones/<drone_id>/targeting_computer/mark

Mark enemy drone or building at coordinates for accuracy bonus

Request Body

target_q: integer (required) - Q coordinate of target tile
target_r: integer (required) - R coordinate of target tile
level: integer (1-3)

Example

{
  "level": 2,
  "target_q": -1,
  "target_r": 1
}

Requires

Targeting Computer equipment

Notes

Uses relative coordinates (drone is at 0, 0). Automatically detects target at coordinates. Prioritizes ground drones over hover drones. If no drone found, marks building if present.

Wire-Guided Missile

1 endpoint
POST /drones/<drone_id>/missile/fire

Launch wire-guided missile at target tile

Request Body

target_q: integer (required)
target_r: integer (required)
level: integer 1-3 (optional, default 1) - L1: range 5, battery 10; L2: range 10, battery 20; L3: range 15, battery 30

Example

{
  "level": 2,
  "target_q": 3,
  "target_r": -2
}

Requires

Wire-Guided Missile equipment

Notes

Missile travels tile-by-tile (default 2 cycles/tile). Level controls range and battery cost (L1: 5 range/10 battery, L2: 10/20, L3: 15/30). Drone cannot take other actions while guiding, EXCEPT mark which redirects the missile to a new target (requires targeting computer). If drone is destroyed, missile is lost. Targets at location receive warning.

Drone Warnings

/drones/<drone_id>/warnings

Configure automated warning thresholds for drone stats. When a stat drops below the configured percentage, a WARNING message is delivered via the message queue. Warnings fire once per threshold crossing and reset when the stat recovers above the threshold.

GET /drones/<drone_id>/warnings

Get warning thresholds for a drone

Response

drone_id: string
thresholds: {battery: int, health: int, shields: int, equipment: int} (only set keys present)
POST /drones/<drone_id>/warnings

Set warning thresholds for a drone

Request Body

battery: integer (0-100, optional) - battery percentage threshold
health: integer (0-100, optional) - health percentage threshold
shields: integer (0-100, optional) - shield percentage threshold
equipment: integer (0-100, optional) - equipment durability percentage threshold (triggers when ANY equipped item drops below)

Example

{
  "battery": 20,
  "equipment": 25,
  "health": 30,
  "shields": 50
}

Notes

Values are percentages (0-100). Null or missing keys disable that warning type. Equipment warnings trigger when any single equipped item's durability percentage drops below the threshold. The warning message identifies which specific items are below threshold.

DELETE /drones/<drone_id>/warnings

Remove all warning thresholds for a drone

Response

drone_id: string
message: Warning thresholds removed

Building Warnings

/buildings/<building_id>/warnings

Configure automated warning thresholds for building stats. Supports health and shields for all buildings, plus storage thresholds for Silos and Refiners. 'Below' warnings fire when a value drops below the threshold; 'above' warnings fire when a value rises above the threshold. Warnings fire once per crossing and reset when the value returns past the threshold.

GET /buildings/<building_id>/warnings

Get warning thresholds for a building

Response

building_id: string
thresholds: {health: int, shields: int, ...} (only set keys present)
POST /buildings/<building_id>/warnings

Set warning thresholds for a building

Request Body

health: integer (0-100, optional) - health percentage threshold (below)
shields: integer (0-100, optional) - shield percentage threshold (below)
storage_above: integer (0-100, optional, Silo only) - warn when storage goes above this %
storage_below: integer (0-100, optional, Silo only) - warn when storage drops below this %
ore_storage_above: integer (0-100, optional, Refiner only) - warn when ore storage goes above this %
ore_storage_below: integer (0-100, optional, Refiner only) - warn when ore storage drops below this %
resource_storage_above: integer (0-100, optional, Refiner only) - warn when resource storage goes above this %
resource_storage_below: integer (0-100, optional, Refiner only) - warn when resource storage drops below this %

Example

{
  "health": 30,
  "shields": 50,
  "storage_below": 10
}

Notes

Values are percentages (0-100). Valid keys depend on the building type: all buildings support health and shields; Silos also support storage_above and storage_below; Refiners also support ore_storage_above, ore_storage_below, resource_storage_above, and resource_storage_below. Unknown keys for the building type are rejected.

DELETE /buildings/<building_id>/warnings

Remove all warning thresholds for a building

Response

building_id: string
message: Warning thresholds removed

Buildings

/buildings

Building listing and information

GET /buildings

List building IDs owned by current user (POST /buildings/<building_id>/common/status to queue a status report)

Response

building_ids: [array of building ID strings]
POST /buildings/<building_id>/common/status

Queue a status action (1 power, 1 cycle)

Request Body

efficiency: float (0.01-1.0, optional)

Response

action_id: string
message: status action queued successfully

Notes

Status information is returned via message queue when action completes. Coordinates are relative to the building's origin (top-left tile), where (0, 0) is the origin. Costs 1 power and takes 1 cycle (adjusted by efficiency). Solar panels include: direction (0-359), tilt (0-90), base_power, buffer, max_buffer, current_power, solar_efficiency.

Common

1 endpoint
POST /buildings/<building_id>/common/repair

Self-repair building health

Request Body

efficiency: float (0.01-1.0, optional)

Example

{
  "efficiency": 1.0
}

Notes

All buildings can self-repair. Costs 3 titanium_parts + 1 battery_materials from command center (consumed immediately when queued). Takes 20 cycles, heals 50 HP (capped at max health). Power cost: 2.5/cycle (50 total at efficiency 1.0). Requires a functional command center with sufficient resources.

Building Actions

/buildings/<building_id>/<type>

Queue actions for buildings. All require efficiency (0.01-1.0) parameter.

Charging Station

1 endpoint
POST /buildings/<building_id>/charging_station/charge

Charge a drone at charging station

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "q": 50,
  "r": 35
}

Building Type

Drone Charging Station

Notes

Takes 10 cycles. Drone is identified by its position. Server resolves the drone_id from q/r coordinates.

Command Center

5 endpoints
POST /buildings/<building_id>/command_center/build

Build a new building

Request Body

building_type: string (required)
target_q: integer (required)
target_r: integer (required)
efficiency: float (0.01-1.0)

Example

{
  "building_type": "laser_turret",
  "efficiency": 1.0,
  "target_q": 25,
  "target_r": 30
}

Building Type

Command Center

Notes

Building types: solar_panel_array, refiner, drone_charging_station, drone_production_plant, drone_equipment_production_plant, drone_repair_station, laser_turret, missile_turret, auto_cannon_turret, silo, battery_array, shield_array, sensor_array, point_defense_system, lucas_launcher

POST /buildings/command_centre/status_report

Get comprehensive status report of all drones and buildings

Request Body

efficiency: float (0.01-1.0, optional)

Example

{
  "efficiency": 1.0
}

Building Type

Command Center

Notes

Automatically finds user's command center. Returns full report of all owned drones (including those outside base), all buildings, adjacent tile info, and summary stats. All coordinates are relative to the command center's origin (top-left tile), where (0, 0) is the origin. For a 3x3 command center, this is the top-left corner, not the center tile. Costs 5 power.

GET /buildings/<building_id>/upgrades

Get upgrade status and available upgrades for your team

Response

available_upgrades: [array of available upgrades]
current_upgrades: {equipment_type: level}
active_research: {current research info or null}

Building Type

Command Center

Notes

Shows all upgrades your team can research, current upgrade levels, and any research in progress

POST /buildings/<building_id>/command_center/research

Start researching an equipment upgrade

Request Body

equipment_type: string (required)
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "equipment_type": "drill"
}

Building Type

Command Center

Notes

Requires resources in command center. Research takes many cycles to complete. Only one research can be active at a time per team. Queue this action each cycle to progress research. Valid equipment_type values: drill, sensors, battery, portable_charger, reactive_armour, hopper, mobile_repair_kit, shield_generator, laser_cannon, propulsion, hover_tech, targeting_computer, wire_guided_missile, auto_cannon, df_antenna, jammer, land_mine, signature_dampener, decoy_beacon, smoke_launcher, point_defense, seismic_sensor, ammunition, howitzer, howitzer_shells, howitzer_charges, baseline_drone, unlock_reactive_armour, unlock_hover_tech, unlock_wire_guided_missile, unlock_auto_cannon, unlock_shield_generator, unlock_targeting_computer, unlock_df_antenna, unlock_jammer, unlock_seismic_sensor, unlock_land_mine, unlock_signature_dampener, unlock_decoy_beacon, unlock_smoke_launcher, unlock_point_defense, unlock_howitzer. The baseline_drone type upgrades the drone chassis itself (health, battery, equipment slots, shields) rather than a specific equipment piece. The unlock_* types are single-tier research that enables building advanced equipment at the Equipment Production Plant.

POST /buildings/<building_id>/command_center/cancel_research

Cancel active research (resources NOT refunded)

Request Body

efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0
}

Building Type

Command Center

Notes

Cancels research in progress. Resources spent to start research are lost.

Equipment Plant

2 endpoints
POST /buildings/<building_id>/equipment_plant/install

Install equipment on drone

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
equipment_type: string (required)
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "equipment_type": "laser_cannon",
  "q": 50,
  "r": 35
}

Building Type

Drone Equipment Production Plant

Notes

Takes 25 cycles. Drone is identified by its position. Server resolves the drone_id from q/r coordinates.

POST /buildings/<building_id>/equipment_plant/remove

Remove equipment from drone

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
equipment_index: integer (required)
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "equipment_index": 0,
  "q": 50,
  "r": 35
}

Building Type

Drone Equipment Production Plant

Notes

Takes 25 cycles. Drone is identified by its position. Server resolves the drone_id from q/r coordinates.

LUCAS Launcher

2 endpoints
POST /buildings/<building_id>/lucas/launch

Launch a LUCAS drone at target coordinates

Request Body

target_q: integer (required, relative to building)
target_r: integer (required, relative to building)
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "target_q": 30,
  "target_r": -15
}

Building Type

LUCAS Launcher

Notes

Launches a stored LUCAS drone at the target tile. The drone flies at elevation 30, speed 1 tile per 3 cycles. Takes 25 cycles to launch (500 power at max efficiency). Only 1 LUCAS drone may be in flight per launcher. Must have a stored LUCAS drone (built via manufacture endpoint). Target must be within 3 tiles of the nearest enemy base edge. LUCAS drones emit seismic signatures only on launch and impact (they are airborne during flight, so no ground vibration). LUCAS drones emit strong EM signatures continuously during flight (guidance/propulsion electronics). All weapons receive +30% accuracy bonus against LUCAS drones. Max flight time: 500 cycles. If the launcher is destroyed, the in-flight drone loses guidance and falls. Coordinates are relative to the building origin.

POST /buildings/<building_id>/lucas/redirect

Redirect an in-flight LUCAS drone to new target

Request Body

target_q: integer (required, relative to building)
target_r: integer (required, relative to building)
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "target_q": 40,
  "target_r": -20
}

Building Type

LUCAS Launcher

Notes

Redirects the currently in-flight LUCAS drone to new target coordinates. Cannot redirect a LUCAS drone that is already falling. New target must be within 3 tiles of the nearest enemy base edge. Coordinates are relative to the building origin.

Point Defense System

1 endpoint
POST /buildings/<building_id>/point_defense/activate

Activate building point defense system

Request Body

efficiency: float (0.01-1.0)
cycles: integer (optional, default 1)

Example

{
  "cycles": 10,
  "efficiency": 1.0
}

Building Type

Point Defense System

Notes

Activates the building PDS for the specified number of cycles (default 1) to intercept incoming enemy missiles within 8-tile interception range. Each missile has a 30% chance of being intercepted. The PDS re-activates automatically each cycle until the specified cycles are exhausted. If power is lost, the action pauses and resumes when power returns.

Production

1 endpoint
POST /buildings/<building_id>/production/manufacture

Build a drone or equipment (unified endpoint)

Request Body

efficiency: float (0.01-1.0)
equipment_type: string (required for equipment plant only)

Example

{
  "efficiency": 1.0,
  "equipment_type": "laser_cannon"
}

Building Type

Drone Production Plant, Drone Equipment Production Plant, LUCAS Launcher

Notes

For Drone Production Plant: builds a new drone. For Equipment Plant: requires equipment_type. For LUCAS Launcher: builds a LUCAS attack drone (stored internally, max 4). Equipment types: drill, sensors, battery, portable_charger, reactive_armour, hopper, mobile_repair_kit, shield_generator, laser_cannon, propulsion, hover_tech, targeting_computer, wire_guided_missile, auto_cannon, df_antenna, jammer, land_mine, signature_dampener, decoy_beacon, smoke_launcher, point_defense, seismic_sensor, armour_piercing_ammunition, high_explosive_ammunition, canister_round_ammunition, howitzer, howitzer_shells, howitzer_charges. Note: Advanced equipment (reactive_armour, hover_tech, wire_guided_missile, auto_cannon, all ammunition types, shield_generator, targeting_computer, df_antenna, jammer, seismic_sensor, land_mine, signature_dampener, decoy_beacon, smoke_launcher, point_defense, howitzer + howitzer_shells + howitzer_charges) requires unlock research at the Command Center before it can be built. See equipment_unlock_research in the upgrades section.

Refiner

1 endpoint
POST /buildings/<building_id>/refiner/process

Process ore into refined materials

Request Body

efficiency: float (0.01-1.0)
ore_type: string (optional)

Example

{
  "efficiency": 1.0,
  "ore_type": "titanium_ore"
}

Building Type

Refiner

Notes

Takes 1 cycle per ore unit (at efficiency 1.0). Processes ore from the refiner's input storage into refined materials.

Repair Station

2 endpoints
POST /buildings/<building_id>/repair_station/repair

Repair drone at repair station

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "q": 50,
  "r": 35
}

Building Type

Drone Repair Station

Notes

Drone is identified by its position. Server resolves the drone_id from q/r coordinates.

POST /buildings/<building_id>/repair_station/repair_equipment

Repair equipment durability on a drone

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
equipment_id: string - full UUID (required)
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "equipment_id": "4aac1f00-a1b2-4c3d-8e5f-1234567890ab",
  "q": 50,
  "r": 35
}

Building Type

Drone Repair Station

Notes

Drone is identified by its position. Restores up to 50 durability per repair action, capped at equipment's max durability. Cannot repair destroyed equipment (durability 0).

Resource Transfer

2 endpoints
POST /buildings/<building_id>/common/load

Transfer resources from building to drone (unified endpoint)

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
efficiency: float (0.01-1.0)
resource_type: string (optional, transfers all valid types if omitted)
amount: integer (optional, transfers max if omitted)

Example

{
  "amount": 20,
  "efficiency": 1.0,
  "q": 50,
  "r": 35,
  "resource_type": "titanium_parts"
}

Building Type

Refiner, Silo, DroneProductionPlant, DroneEquipmentProductionPlant, CommandCenter, LucasLauncher

Notes

Takes 5 cycles. Unified endpoint for all building-to-drone transfers. Drone is identified by position. Drone must have hopper equipment and be within range of the building.

POST /buildings/<building_id>/common/unload

Transfer resources from drone to building (unified endpoint)

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
efficiency: float (0.01-1.0)
resource_type: string (optional, transfers all valid types if omitted)
amount: integer (optional, some buildings support this)

Example

{
  "efficiency": 1.0,
  "q": 50,
  "r": 35,
  "resource_type": "titanium_ore"
}

Building Type

Refiner, Silo, DroneProductionPlant, DroneEquipmentProductionPlant, CommandCenter, LucasLauncher

Notes

Takes 5 cycles. Unified endpoint for all drone-to-building transfers. Drone is identified by position. Drone must have hopper equipment and be within range of the building.

Sensor Array

2 endpoints
POST /buildings/<building_id>/sensor_array/scan

Scan nearby tiles for terrain, resources, and units

Request Body

efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0
}

Building Type

Sensor Array

Notes

Scans a 20-tile radius around the sensor array, revealing terrain, resources, drones, and buildings. The sensor array is elevated high enough to bypass all line-of-sight checks. Returns results via message queue using coordinates relative to the building's position.

POST /buildings/<building_id>/sensor_array/identify

Identify objects at a specific tile with detailed info

Request Body

q: integer (required, relative to building)
r: integer (required, relative to building)
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "q": 5,
  "r": -3
}

Building Type

Sensor Array

Notes

Identifies any tile within 20-tile range, returning detailed information about terrain, resources, buildings, and drones at that location. The sensor array is elevated high enough to bypass all line-of-sight checks. Coordinates are relative to the sensor array's position.

Shield Array

3 endpoints
POST /buildings/<building_id>/shield_array/generate

Generate shields in shield array

Request Body

efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0
}

Building Type

Shield Array
POST /buildings/<building_id>/shield_array/transfer

Transfer shields to another building

Request Body

target_building_id: string (required)
efficiency: float (0.01-1.0)
amount: integer (optional)

Example

{
  "amount": 50,
  "efficiency": 1.0,
  "target_building_id": "command-center-1"
}

Building Type

Shield Array
POST /buildings/<building_id>/shield_array/transfer_to_drone

Transfer shields from shield array to a drone

Request Body

q: integer (required) - drone hex Q coordinate
r: integer (required) - drone hex R coordinate
efficiency: float (0.01-1.0)
amount: integer (optional)

Example

{
  "amount": 25,
  "efficiency": 1.0,
  "q": 50,
  "r": 35
}

Building Type

Shield Array

Notes

Transfers shield power from buffer to target drone. Drone is identified by position.

Solar Panel

1 endpoint
POST /buildings/<building_id>/solar_panel/reposition

Reposition solar panel direction and tilt

Request Body

direction: float (required, 0-359 degrees) - azimuth direction
tilt: float (optional, 0-90 degrees) - elevation angle (0=horizon, 90=straight up). Defaults to current tilt if omitted.
efficiency: float (0.01-1.0)

Example

{
  "direction": 180.0,
  "efficiency": 0.5,
  "tilt": 45.0
}

Building Type

Solar Panel Array

Notes

Panel efficiency uses 3D dot-product: panel and sun orientations are converted to unit vectors and efficiency = max(0, dot(panel, sun)) * brightness. A sun below the horizon (elevation <= 0) contributes 0 power. Both suns can set, creating true nighttime.

Turrets

5 endpoints
POST /buildings/<building_id>/turret/fire

Fire turret at target (unified endpoint for all turret types)

Request Body

q: integer (required) - target hex Q coordinate
r: integer (required) - target hex R coordinate
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "q": 50,
  "r": 35
}

Building Type

Laser Turret / Missile Turret / Auto-Cannon Turret

Notes

Server determines turret type from the building. All turret types use the same q/r target interface. Laser Turret: fires laser at target position. Missile Turret: launches a missile at target; missile travels tile-by-tile (2 cycles/tile), fails if turret destroyed mid-flight, cannot be redirected. Auto-Cannon Turret: fires 2 rounds of active ammo type at target; must reload first. Ammo types: armour_piercing (50 dmg, ignores armour), high_explosive (30 dmg + 15 splash), canister_round (instant cone, 5 dmg/tile). Accuracy: 75%-20% by range, capped at 95%. All weapons receive +30% accuracy bonus against LUCAS drones. Min range: 2 tiles.

POST /buildings/<building_id>/turret/reload

Reload turret ammunition

Request Body

ammo_type: string (optional) - required for auto-cannon turret: armour_piercing, high_explosive, canister_round
efficiency: float (0.01-1.0)

Example

{
  "ammo_type": "armour_piercing",
  "efficiency": 1.0
}

Building Type

Missile Turret / Auto-Cannon Turret

Notes

Replenishes ammunition. Costs resources from command center. For auto-cannon, also sets active ammo type. Missile turret reload: 8 titanium_parts + 12 battery_materials. Auto-cannon reload: 4 titanium_parts + 4 battery_materials.

POST /buildings/<building_id>/turret/scan

Scan area with turret sensors

Request Body

efficiency: float (0.01-1.0)

Example

{
  "efficiency": 0.5
}

Building Type

Laser Turret / Missile Turret / Auto-Cannon Turret

Notes

Scans a 15-tile radius around the turret. Coordinates in results are relative to the building's position.

POST /buildings/<building_id>/turret/identify

Identify target at position with turret

Request Body

q: integer (required) - target hex Q coordinate
r: integer (required) - target hex R coordinate
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 0.75,
  "q": 50,
  "r": 35
}

Building Type

Laser Turret / Missile Turret / Auto-Cannon Turret

Notes

Target must be within 15-tile range. Target is identified by position. Returns detailed info about drone at that location.

POST /buildings/<building_id>/turret/mark

Mark target at position with turret

Request Body

q: integer (required) - target hex Q coordinate
r: integer (required) - target hex R coordinate
efficiency: float (0.01-1.0)

Example

{
  "efficiency": 1.0,
  "q": 50,
  "r": 35
}

Building Type

Laser Turret / Missile Turret / Auto-Cannon Turret

Notes

Target must be within 15-tile range. Target is identified by position. Marks drone at location for accuracy bonus. For missile turrets: if missiles are in flight, mark redirects all in-flight missiles to the new target.

Action Queue

/queue

View and manage queued actions

GET /queue/drones

Get all queued drone actions

Response

actions: [array of action objects]
GET /queue/buildings

Get all queued building actions

Response

actions: [array of action objects]
DELETE /queue/drones/<action_id>

Cancel a queued drone action

DELETE /queue/buildings/<action_id>

Cancel a queued building action

POST /queue/reset

Clear all drone and building action queues

Response

message: All queues cleared

Messages

/messages

Retrieve game messages and notifications. Messages are limited to the last 100 per user.

GET /messages

Get messages

Query Parameters

count: integer (optional, returns all if not specified)
consume: boolean (optional, default: false)

Response

messages: [array of message objects]
count: integer
GET /messages/count

Get message count

Response

count: integer
DELETE /messages

Clear all messages

HTTP Status Codes

Code Description
200 Success
201 Action queued successfully
400 Bad request / Action rejected
401 Unauthorized (missing or invalid token)
403 Forbidden (resource not owned by user)
404 Resource not found

Error Response Format

Error responses follow this format

Example

{
  "error": {
    "code": "DRONE_NOT_FOUND",
    "message": "Drone not found",
    "details": {'drone_id': 'abc123'}
  }
}

Drone Battles Admin API

Version 1.0

Administrative API for full control over the game state. All endpoints require admin authentication. The admin API is entirely synchronous: every request is executed immediately and the result is returned in the HTTP response — unlike the game API, there is no action queue and no result message to poll for.

Base URL: /api/v1/admin

Authentication

Type: Bearer Token

All admin endpoints require the Bearer token of an admin account. Register an admin account with the admin registration code, then log in to obtain the token.

Authorization: Bearer <token>

Map & Asset Overview

/admin

View complete map data and all game assets

GET /map Public

Get complete map data including all tiles, terrain, and elevations

Response

width: integer
height: integer
tile_count: integer
tiles: [array of tile objects]
GET /map/summary Public

Get map summary without full tile data

Response

width: integer
height: integer
tile_count: integer
terrain_stats: object
base_count: integer
resource_count: integer
drone_count: integer
building_count: integer
user_count: integer
GET /assets Public

Get all game assets (users, bases, buildings, drones, resources)

Response

users: [array of user summaries]
bases: [array of base objects]
buildings: [array of building objects]
drones: [array of drone objects]
resources: [array of resource objects]
GET /assets/buildings Public

Get all buildings with full details

Response

buildings: [array]
count: integer
GET /assets/drones Public

Get all drones with full details

Response

drones: [array]
count: integer
GET /assets/resources Public

Get all resource nodes

Response

resources: [array]
count: integer
GET /assets/bases Public

Get all bases

Response

bases: [array]
count: integer

Building Management

/admin/buildings

CRUD operations for buildings

POST /buildings Public

Create a new building

Request Body

building_type: string (required) - one of: command_center, solar_panel_array, battery_array, drone_charging_station, refiner, drone_production_plant, silo, shield_array, drone_equipment_production_plant, drone_repair_station, laser_turret, missile_turret, auto_cannon_turret, sensor_array, point_defense_system, lucas_launcher
owner_id: string (required) - user_id of an existing user
origin_q: integer (required) - top-left column of the building footprint; must be on-map with enough unoccupied tiles for the building size
origin_r: integer (required) - top-left row of the building footprint

Example

{
  "building_type": "solar_panel_array",
  "origin_q": 50,
  "origin_r": 50,
  "owner_id": "user-123"
}

Errors

400: MISSING_FIELDS
400: INVALID_BUILDING_TYPE (response details include valid_types)
500: BUILDING_CREATION_FAILED (location occupied or off-map)
GET /buildings/<building_id> Public

Get detailed information about a specific building

PATCH /buildings/<building_id> Public

Modify a building's properties

Request Body

current_health: number (optional) - clamped to the building's max_health
shields: number (optional) - clamped to the building's max_shields
owner_id: string (optional) - user_id of an existing user; transfers ownership

Notes

Only these three fields are applied; any other fields in the body are ignored.

DELETE /buildings/<building_id> Public

Remove a building from the game

POST /buildings/<building_id>/resources Public

Add resources to a building's storage

Request Body

resource_type: string (required) - refined resources: titanium_parts, battery_materials, enriched_unobtanium; raw ore (refiner/silo): titanium_ore, rare_earth_ore, unobtanium_ore
amount: number (required, > 0)

Example

{
  "amount": 50,
  "resource_type": "titanium_parts"
}

Errors

400: MISSING_FIELDS
400: INVALID_FIELDS (amount not a positive number)
400: RESOURCE_ADD_FAILED (building has no storage)
404: building not found

Notes

Storage capacity limits apply - the response's amount_added reports what was actually stored. Battery arrays store the amount as power regardless of resource_type. Buildings without storage return 400.

Drone Management

/admin/drones

CRUD operations for drones

POST /drones Public

Create a new baseline drone with optional starting equipment

Request Body

owner_id: string (required) - user_id of an existing user
location_q: integer (required) - must be an on-map, passable, unoccupied tile
location_r: integer (required)
equipment: array of equipment type strings (optional) - see valid types in notes; the baseline drone has 6 equipment slots

Example

{
  "equipment": [
    "drill",
    "hopper",
    "propulsion",
    "sensors"
  ],
  "location_q": 50,
  "location_r": 50,
  "owner_id": "user-123"
}

Errors

400: MISSING_FIELDS
400: INVALID_EQUIPMENT (response details include valid_types)
400: DRONE_CREATION_FAILED (tile invalid or occupied)

Notes

Drone stats (speed, battery, health, armour, accuracy, slots) come from config/drones.json and cannot be set per-drone. Valid equipment types: drill, sensors, hopper, propulsion, battery, portable_charger, reactive_armour, mobile_repair_kit, shield_generator, laser_cannon, hover_tech, targeting_computer, wire_guided_missile, auto_cannon, armour_piercing_ammunition (alias: ap_ammunition), high_explosive_ammunition (alias: he_ammunition), canister_round_ammunition (alias: canister_ammunition), df_antenna, jammer, land_mine, signature_dampener, decoy_beacon, smoke_launcher, point_defense, seismic_sensor, howitzer, howitzer_shells, howitzer_charges

GET /drones/<drone_id> Public

Get detailed information about a specific drone

PATCH /drones/<drone_id> Public

Modify a drone's properties

Request Body

current_health: number (optional) - clamped to the drone's max_health
current_battery: number (optional) - clamped to the drone's max_battery (baseline max: 4000)
shields: number (optional) - clamped to the drone's max_shields
owner_id: string (optional) - user_id of an existing user; transfers ownership
location_q: integer (optional) - must be sent together with location_r; target tile must be valid and unoccupied
location_r: integer (optional) - must be sent together with location_q
direction: integer (optional) - hex facing 0-5 (0=NE, increments clockwise); other values wrap modulo 6

Notes

Only the listed fields are applied; any other fields in the body are ignored.

DELETE /drones/<drone_id> Public

Remove a drone from the game

POST /drones/<drone_id>/equipment Public

Add equipment to a drone

Request Body

equipment_type: string (required) - same valid types as POST /drones (case-insensitive); fails if the drone has no free equipment slot

Example

{
  "equipment_type": "laser_cannon"
}

Errors

400: INVALID_EQUIPMENT (response details include valid_types)
400: EQUIPMENT_ADD_FAILED (no free slot or drone not found)
POST /drones/<drone_id>/charge Public

Fully charge a drone's battery to maximum

Errors

404: CHARGE_FAILED (drone not found)

Notes

No request body. Response includes battery_before and battery_after.

POST /drones/<drone_id>/repair Public

Fully repair a drone's health, shields, and all equipment durability

Errors

404: REPAIR_FAILED (drone not found)

Notes

No request body. Response includes health_before, health_after, and equipment_repaired count.

Resource Management

/admin/resources

CRUD operations for resource nodes

POST /resources Public

Create a new resource node

Request Body

q: integer (required) - must be an on-map, unoccupied tile
r: integer (required)
ore_type: string (optional, default 'titanium_ore') - one of: titanium_ore, rare_earth_ore, unobtanium_ore
volume: integer (optional, default 500) - units of ore in the node; 'ore_volume' is accepted as an alias

Example

{
  "ore_type": "unobtanium_ore",
  "q": 30,
  "r": 40,
  "volume": 1000
}

Errors

400: MISSING_FIELDS
400: RESOURCE_CREATION_FAILED (invalid ore type, tile occupied, or off-map)
PATCH /resources/<resource_id> Public

Modify a resource's properties

Request Body

volume: number (optional) - remaining ore units; 'ore_volume' is accepted as an alias
ore_type: string (optional) - one of: titanium_ore, rare_earth_ore, unobtanium_ore
DELETE /resources/<resource_id> Public

Remove a resource from the map

Terrain Management

/admin/terrain

View and modify map terrain

GET /terrain/<q>/<r> Public

Get terrain information for a specific tile

Notes

q and r are URL path integers; 404 if the tile is off-map.

PATCH /terrain/<q>/<r> Public

Modify terrain at a specific tile

Request Body

terrain: integer (optional) - 1=normal, 2=difficult, 3=impassable (no other values accepted)
elevation: integer (optional) - the game uses elevations 0-100

Example

{
  "elevation": 40,
  "terrain": 3
}

Errors

400: INVALID_TERRAIN (terrain not in 1/2/3)
404: TILE_NOT_FOUND
PATCH /terrain/bulk Public

Modify terrain for multiple tiles at once

Request Body

tiles: array of objects (required, non-empty) - each: {q: integer (required), r: integer (required), terrain: integer 1/2/3 (optional), elevation: integer (optional)}

Example

{
  "tiles": [
    {
      "elevation": 50,
      "q": 10,
      "r": 10,
      "terrain": 3
    },
    {
      "q": 11,
      "r": 10,
      "terrain": 2
    }
  ]
}

Notes

Terrain values: 1=normal, 2=difficult, 3=impassable. Off-map tiles in the array are skipped; the response reports modified_count.

User & Admin Management

/admin/users

Manage registered users, credentials, and admin status

GET /users Public

Get all registered users with admin status

Response

users: [{username, user_id, is_admin, name?, team?}]
count: integer
POST /users Public

Create a new user (admin bypass - no team registration code required)

Request Body

username: string (required) - 3-64 characters; letters, numbers, underscores, hyphens, and periods only; must be unique
password: string (required) - 6-128 characters
name: string (required) - display name, 1-64 characters, not blank
team: integer (required) - 0-16; team 0 is for admin accounts and gets no base/drones/buildings
is_admin: boolean (optional, default false)

Example

{
  "name": "Player Three",
  "password": "secret123",
  "team": 3,
  "username": "player3"
}

Errors

400: MISSING_FIELDS / INVALID_FIELDS / INVALID_USERNAME / INVALID_PASSWORD / INPUT_TOO_LONG
400: REGISTRATION_FAILED (username exists)
500: SETUP_FAILED (engine unavailable)

Notes

Non-admin users (team 1-16) are provisioned with a base, starter buildings, and 2 starter drones, same as normal registration.

PATCH /users/<user_id> Public

Edit a user's name, team, and/or admin status

Request Body

name: string (optional) - non-empty, max 64 characters
team: integer (optional) - 0-16
is_admin: boolean (optional)

Notes

At least one field is required.

DELETE /users/<user_id> Public

Delete a user and cascade-remove all their game assets

Errors

400: CANNOT_DELETE_SELF
404: DELETE_FAILED

Notes

Cannot delete your own account. Response reports drones_deleted, buildings_deleted, and bases_unassigned.

PUT /users/<user_id>/admin Public

Set or revoke admin status for a user

Request Body

is_admin: boolean (required)

Example

{
  "is_admin": true
}
PUT /users/<user_id>/password Public

Reset a user's password

Request Body

new_password: string (required) - 6-128 characters

Errors

400: MISSING_FIELDS / INVALID_PASSWORD / INPUT_TOO_LONG
404: USER_NOT_FOUND
POST /users/<user_id>/score Public

Apply a score adjustment (penalty or bonus) to a user

Request Body

delta: integer (required) - points to add (negative = penalty)
reason: string (optional, max 500 chars) - shown to the player and security-logged

Response

user_id: string
delta: integer
new_score: integer

Example

{
  "delta": -200,
  "reason": "Friendly-fire griefing (ruling #4)"
}

Notes

The player receives a score_update message with the delta and reason.

POST /users/<user_id>/suspend Public

Suspend a user: blocks logins, revokes all sessions; game assets are kept

Response

user_id: string
tokens_revoked: integer

Errors

400: CANNOT_SUSPEND_SELF
404: USER_NOT_FOUND

Notes

Softer than DELETE - the account and its base/drones remain; lift with DELETE /users/<user_id>/suspend.

DELETE /users/<user_id>/suspend Public

Lift a user's suspension so they can log in again

DELETE /users/<user_id>/tokens Public

Revoke all of a user's active session tokens (they must log in again)

Response

user_id: string
tokens_revoked: integer
GET /users/<user_id>/queue Public

View a user's queued drone and building actions (support/debugging)

Response

user_id: string
drone_actions: [array of queued action objects]
building_actions: [array of queued action objects]
DELETE /users/<user_id>/mfa Public

Disable MFA for a user (account recovery)

Errors

404: USER_NOT_FOUND

Notes

No request body.

POST /users/setup-all Public

Assign bases and create starter buildings/drones for all registered users that lack them

Notes

No request body. Skips admin (team 0) users and users that already have a base.

DELETE /rate-limits Public

Clear login/register/MFA rate-limit state

Request Body

ip: string (optional) - a client IP address; omit to clear rate limits for all IPs

Example

{
  "ip": "203.0.113.7"
}
GET /registration-codes Public

Get current team registration codes, the automation service token, and the admin registration code

Response

admin_token: string
admin_registration_code: string
teams: {code: team_number}
PUT /registration-codes Public

Update team registration codes, the automation service token, and/or the admin registration code

Request Body

admin_token: string (optional) - service token for headless automation (e.g. the fleet CLI); admins authenticate with their own Bearer token, not this. At least 8 characters
admin_registration_code: string (optional) - code that registers admin accounts, at least 8 characters
teams: object (optional) - maps registration code strings to team numbers (integers 1-16); replaces the whole mapping

Example

{
  "teams": {
    "NEW_CODE_A": 1,
    "NEW_CODE_B": 2
  }
}
POST /registration-codes/regenerate Public

Regenerate random registration codes and tokens

Request Body

admin_token: boolean (optional) - rotate the automation service token
admin_registration_code: boolean (optional) - rotate the admin registration code
teams: boolean (optional) - rotate all team codes

Notes

Invalidates the previous values immediately. A missing admin_registration_code is always backfilled (legacy files).

Engine Control

/admin/engine

Monitor the game engine and adjust its cycle time

GET /engine/status Public

Get game engine status

Response

running: boolean
paused: boolean
cycle_count: integer
cycle_time: float
match_state: string - active | frozen | ended
total_errors: integer - engine cycle errors since boot
last_error_cycle: integer - cycle of the most recent error (0 = none)
snapshot_cycle: integer - cycle of the last crash-recovery snapshot
snapshot_time: float - unix timestamp of the last snapshot (alert if stale)
user_count: integer
building_count: integer
drone_count: integer
building_action_queue_size: integer
POST /engine/cycle-time Public

Set the engine cycle time in seconds (0.0001 to 10)

Request Body

cycle_time: float (required)

Example

{
  "cycle_time": 0.25
}

Game & Match Control

/admin

Reset the game, freeze/end the match, and make announcements

POST /game/reset Public

Reset the game to its starting state (WARNING: destroys all progress!)

Request Body

seed: integer (optional) - random seed for reproducible map generation
resource_count: integer (optional, >= 0) - resource nodes to place; defaults to the map scaling config for the current team count
impassable_ratio: float (optional, default 0.15) - 0.0 to 1.0
difficult_ratio: float (optional, default 0.20) - 0.0 to 1.0

Response

message: string
players_provisioned: integer
provisioned_user_ids: array of user IDs
provisioning_errors: array
resource_count: integer
width: integer
height: integer
bases_created: integer

Notes

Regenerates the map, zeroes all scores/stats/team upgrades, clears action queues and message inboxes, then reprovisions every registered player with their starting base, buildings, and 2 starter drones. User accounts and auth tokens are preserved. Also returns an ended/frozen match to active.

POST /match/freeze Public

Freeze the match: all player actions are rejected (HTTP 423) until unfreeze

Request Body

reason: string (optional) - included in the notice players receive

Notes

Enforced at the API layer; the engine keeps cycling (in-flight projectiles land, timers run). Every player gets an announcement message, and match_state appears as 'frozen' in GET /game/status.

POST /match/unfreeze Public

Resume a frozen match

Errors

400: MATCH_STATE_FAILED (match is not frozen)
POST /match/end Public

End the match permanently - actions stay disabled until a game reset

Request Body

reason: string (optional)

Notes

Terminal state: only POST /game/reset returns the server to active play.

POST /broadcast Public

Send an announcement message to every registered player

Request Body

message: string (required, max 2000 chars)
subject: string (optional, max 200 chars, default 'Announcement')

Example

{
  "message": "Match starts in 5 minutes - good luck!"
}

Notes

Delivered through the normal message system: players see it via message polling and WebSocket.

Upgrades

/admin/upgrades

Manage team upgrades directly

GET /upgrades Public

Get all team upgrades

Response

team_upgrades: {team_id: {equipment_type: level}}
POST /upgrades Public

Set a team's upgrade level directly (for testing)

Request Body

team: string (required) - team number as a string, e.g. "1" (integers are rejected)
equipment_type: string (required) - an upgrade key from config/upgrades.json: drill, sensors, battery, portable_charger, reactive_armour, hopper, mobile_repair_kit, shield_generator, laser_cannon, propulsion, hover_tech, targeting_computer, wire_guided_missile, auto_cannon, ammunition, canister_round, df_antenna, jammer, land_mine, decoy_beacon, signature_dampener, smoke_launcher, point_defense, seismic_sensor, howitzer, howitzer_shells, howitzer_charges, baseline_drone, or an unlock tech (unlock_reactive_armour, unlock_hover_tech, unlock_wire_guided_missile, unlock_auto_cannon, unlock_signature_dampener, unlock_decoy_beacon, unlock_smoke_launcher, unlock_point_defense, unlock_land_mine, unlock_shield_generator, unlock_targeting_computer, unlock_df_antenna, unlock_jammer, unlock_seismic_sensor, unlock_howitzer)
level: integer (required, >= 1) - equipment upgrades have tiers 1-3; unlock techs use level 1

Example

{
  "equipment_type": "drill",
  "level": 2,
  "team": "1"
}

Notes

Upgrades are cumulative and apply retroactively to existing drones/equipment on the team, same as researched upgrades.

Tiles

/admin/tiles

Direct tile manipulation

POST /tiles/<q>/<r>/ore Public

Set ore on a specific tile (for testing mining)

Request Body

ore_type: string (optional) - use one of the real ore types: titanium_ore, rare_earth_ore, unobtanium_ore (the legacy default 'iron' is accepted but cannot be refined)
amount: integer (optional, default 100) - units of ore

Example

{
  "amount": 500,
  "ore_type": "titanium_ore"
}

Errors

404: TILE_NOT_FOUND

Game State & Diagnostics

/admin

Debug and support endpoints

GET /state Public

Get full game state for debugging

Response

users: {user_id: user_object}
drones: {drone_id: drone_object}
buildings: {building_id: building_object}
resources: {resource_id: resource_object}
team_upgrades: {team_id: upgrades}
cycle: integer
GET /dlq Public

Inspect the dead-letter queue of malformed actions

Query Parameters

count: integer (optional, default 50, max 500) - newest entries to return

Response

total: integer
returned: integer
entries: [array]

Notes

Entries here usually mean a team's bot is sending malformed action JSON.

DELETE /dlq Public

Clear the dead-letter queue

Response

cleared: integer