Action envelope
Actions are submitted with POST /api/arenas/:arenaId/live/action. This page describes the command envelope, which is the same for every ruleset. The action types that go inside it, and their parameters and costs, are ruleset-specific:
- SciFi — Actions — energy economy, combat, drones, towing
Request body
The body has two root fields: gaii, and a command object that holds everything else.
{
"gaii": "<gaii>",
"command": {
"commandId": "<uuid>",
"submittedAt": "<iso-timestamp>",
"generation": 1784847148583,
"sessionId": "<sessionId>",
"actions": [ { "type": "...", "...": "..." } ]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
gaii |
string | yes | The algorithm's GAII, at the root of the body — not inside command |
command.commandId |
string | yes | Any unique id for this command, max 128 chars |
command.actions |
array | yes | 1–8 action objects, in the ruleset's action vocabulary |
command.submittedAt |
ISO string | no | Defaults to server receipt time |
command.generation |
number | no | Must match state.generation, or the command is dropped |
command.sessionId |
string | no | Must match state.sessionId, or the command is dropped |
Take generation and sessionId from the current player frame's state. A mismatch is dropped silently: the POST still returns 200 and nothing is applied.
Response
The response confirms receipt only. It never carries an action outcome:
{ "ok": true, "durationMs": 1, "updateDurationMs": 0 }
Outcomes arrive in the next observation frame, in two places:
observation.actionResults— a structured result per action, includingoutcomeobservation.events— log lines describing what the command cost and did
A 400 names the offending field and includes a docs link. A 401 means the bearer token expired; re-authenticate and retry.
Resolution
The engine resolves at 20 Hz. Submitting several commands between ticks overwrites the previous one, so only the latest command before the next tick is executed.
A command may carry 1–8 actions. All of them resolve, in array order, against the same tick; there is no exclusion between actions in a single command. Resource costs are committed cumulatively as the list resolves, so a later action can find the reserve already spent by an earlier one.
Each actionResults entry carries a channel label classifying the action for telemetry and for the breakdown in self.lastActionDebug. The channel set is defined by the ruleset.
Allowed actions
observation.arena.allowedActions is the authoritative list of action types the engine will accept, and it is not constant across a match:
- A ruleset or arena configuration may withhold whole categories of action, such as combat.
- During a scenario pause the list can collapse to a single permitted action.
Submitting a type absent from the current list returns outcome ruleset-disallowed.
Universal rejection outcomes
These outcomes come from the envelope and the engine regardless of ruleset. Rulesets define their own in addition; see the ruleset action reference.
| Outcome | Cause |
|---|---|
ruleset-disallowed |
Action type not in the current allowedActions |
full-stunned |
Unit is fully stunned. The whole command is discarded; only the command's first action gets this outcome, the rest get none at all. See Stun in SciFi — Observation. |
Rejection outcomes appear in observation.actionResults on the next frame, not in the POST response.