Simulations
Simulations are the core resource of the Kairos Nexus REST API. Kairos Nexus is the hosted service that runs the engine. You can create them from pre-built scenarios or custom configurations. You can advance them tick-by-tick or in bulk and retrieve their full execution trace. You can control agent policies and playback in real time.
All simulation endpoints require authentication. API keys need simulation:write for writes and simulation:read for reads. Scope checks do not apply to JWT callers.
API keys access their own organization’s simulations. JWT callers need organization membership or ownership of a personal simulation.
All endpoints can return 401, 403, 429, or 500. See HTTP Status Codes for shared errors.
Schema-invalid JSON bodies return 400 VALIDATION_ERROR. Unknown top-level body fields are rejected.
Replace <api-key>, <organization-id>, and <simulation-id> in the examples with your key and resource UUIDs.
Responses containing comments are abbreviated examples. State objects, agent snapshots, and additional rows or steps are omitted, rather than empty.
Create Simulation
Section titled “Create Simulation”POST /v1/simulationsCreates a new simulation instance. Provide either a scenarioId (to use a pre-built scenario) or an inline scenario object. Do not provide both.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
scenarioId | string | One of | ID of a pre-built scenario |
scenario | object | One of | Inline scenario configuration |
scenario.seed | number | Yes | Random seed |
scenario.agents | Agent[] | No | Agent configurations (1 to 64 entries if supplied) |
scenario.initialState | any | No | Starting state overrides, without REST structure validation |
organizationId | uuid | For API keys | Key’s organization UUID. Optional for JWT callers |
engineVersion | string | No | Required match to the server’s engine version |
scenario.seed is required when scenario is supplied. Omitting scenario.agents keeps the default primary agent.
Supplying agents requires an explicitly allowlisted JWT user or API key. The key does not inherit its creator’s allowlist entry.
| Agent field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Agent identifier |
marker | {x, y} | Yes | Initial position with numeric coordinates |
policy | object | No | Agent policy |
policy.lambda | number | No | Lambda policy value |
policy.gamma | number | No | Gamma policy value |
attractorPreferences | object | No | Attractor names mapped to numeric weights |
engineVersion must match the server’s reported engine version string. A mismatch returns 400 VALIDATION_ERROR.
The check does not select another build or verify the loaded binary’s identity.
Response 201
Section titled “Response 201”For inline scenarios, scenarioId is inline:<simulation-id>.
{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "scenarioId": "standard", "engineVersion": "1.4.4", "state": { /* State fields omitted */ }}Example
Section titled “Example”curl -X POST https://api.anankelabs.io/v1/simulations \ -H "X-Api-Key: <api-key>" \ -H "Content-Type: application/json" \ -d '{"scenarioId": "standard", "organizationId": "<organization-id>"}'Create Simulation from Domain
Section titled “Create Simulation from Domain”POST /v1/simulations/from-domainCreates a simulation using a domain adapter. Domain adapters translate domain-specific input (for example, AI Safety vocabulary) into the engine’s native scenario format.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
domain | string | Yes | Domain adapter identifier (for example, "ai-safety") |
input | object | Yes | Domain-specific input payload |
organizationId | uuid | For API keys | Key’s organization UUID. Optional for JWT callers |
engineVersion | string | No | Required match to the server’s engine version |
Response 201
Section titled “Response 201”Same field names as Create Simulation, with scenarioId set to domain:<domain>:<simulation-id>.
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Unknown domain adapter |
403 | FORBIDDEN | Caller lacks the custom-collection allowlist entry or organization access |
Domain input must contain an entities array and pass the selected adapter’s validation.
The input and encoded agent collections must each contain 1 to 64 entries. Domain creation requires an explicitly allowlisted caller.
Run Simulation
Section titled “Run Simulation”POST /v1/simulations/runCreates and executes a simulation in a single request, returning the completed run’s trace. This endpoint supports batch workloads.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
scenarioId | string | One of | Pre-built scenario ID |
domain | string | One of | Domain adapter identifier |
input | object | With domain | Domain-specific input payload |
ticks | integer | Yes | Requested tick count (1 to 10,000), with no default |
organizationId | uuid | For API keys | Key’s organization UUID. Optional for JWT callers |
engineVersion | string | No | Required match to the server’s engine version |
Provide exactly one of scenarioId or domain. With domain, also provide input and meet the domain collection requirements.
Usage limits can shorten a run. trace.metadata.ticks and the number of steps show the actual completed count.
The JSON response is the default. Successful JSON responses support an Idempotency-Key.
Streaming Response
Section titled “Streaming Response”Set Accept: application/x-ndjson to receive the trace as NDJSON, where each line is a trace step. The stream contains steps only, without the JSON wrapper, simulation ID, or metadata.
The server retains the steps in memory for trace persistence. Streaming runs do not cache an idempotent response.
An error after streaming starts can terminate the stream before completion.
Response 200 (JSON)
Section titled “Response 200 (JSON)”{ "simulationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "engineVersion": "1.4.4", "trace": { "metadata": {"scenarioId": "standard", "engineVersion": "1.4.4", "ticks": 500}, "initialState": { /* Initial state fields omitted */ }, "steps": [ { "tick": 1, "move": "stay", "state": { /* State fields omitted */ }, "warning": { /* Warning signal fields omitted */ }, "loss": null, "agents": [ { "id": "default", "state": { /* State fields omitted */ }, "policy": { /* Optional lambda and gamma values omitted */ } } ] } // Remaining steps omitted ] }}Example: JSON
Section titled “Example: JSON”curl -X POST https://api.anankelabs.io/v1/simulations/run \ -H "X-Api-Key: <api-key>" \ -H "Content-Type: application/json" \ -d '{"scenarioId": "standard", "ticks": 500, "organizationId": "<organization-id>"}'Example: Streaming NDJSON
Section titled “Example: Streaming NDJSON”curl -X POST https://api.anankelabs.io/v1/simulations/run \ -H "X-Api-Key: <api-key>" \ -H "Accept: application/x-ndjson" \ -H "Content-Type: application/json" \ -d '{"scenarioId": "standard", "ticks": 500, "organizationId": "<organization-id>"}'List Simulations
Section titled “List Simulations”GET /v1/simulationsReturns simulations in descending creation order. API keys list only their own organization.
JWT callers list personal simulations by default. Supplying organizationId lists that organization’s simulations after a membership check.
The API ignores an API key caller’s organizationId query filter.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
organizationId | uuid | None | Organization filter (JWT users only, API keys use their own org) |
limit | integer | 50 | Results per page, clamped to 1 to 200 |
offset | integer | 0 | Pagination offset, clamped to 0 or greater |
limit and offset use integer parsing. Zero or an unparseable limit uses 50. An unparseable offset uses 0.
Response
Section titled “Response”{ "data": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "scenarioId": "standard", "organizationId": "11111111-1111-4111-8111-111111111111", "createdAt": "2025-06-15T10:30:00.000Z", "state": { /* State fields omitted */ } } ], "pagination": { "limit": 50, "offset": 0, "total": 1 }}Example
Section titled “Example”curl "https://api.anankelabs.io/v1/simulations?limit=10" \ -H "X-Api-Key: <api-key>"Get Simulation
Section titled “Get Simulation”GET /v1/simulations/{id}Returns the current state of a single simulation.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | uuid | Simulation ID |
The organizationId field is null for a personal simulation.
Response
Section titled “Response”{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "scenarioId": "standard", "organizationId": "11111111-1111-4111-8111-111111111111", "state": { /* State fields omitted */ }, "createdAt": "2025-06-15T10:30:00.000Z"}Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
401 | UNAUTHORIZED | Missing or invalid authentication |
403 | FORBIDDEN | Missing API key scope or access to this simulation |
404 | NOT_FOUND | Simulation does not exist |
Step Simulation
Section titled “Step Simulation”POST /v1/simulations/{id}/stepAdvances an existing simulation by a given number of ticks.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | uuid | Simulation ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
ticks | integer | Yes | Requested tick count (1 to 10,000), with no default |
The returned ticks is the actual completed count, which usage limits can shorten.
Stepping updates the stored state and creates a prophecy record. It does not create or refresh the persisted trace.
This endpoint does not cache idempotent responses.
Response
Section titled “Response”{ "state": { /* State fields omitted */ }, "ticks": 100}Example
Section titled “Example”curl -X POST "https://api.anankelabs.io/v1/simulations/<simulation-id>/step" \ -H "X-Api-Key: <api-key>" \ -H "Content-Type: application/json" \ -d '{"ticks": 100}'Get Trace
Section titled “Get Trace”GET /v1/simulations/{id}/traceReturns the persisted trace from POST /simulations/run. The default response is JSON.
With Accept: application/x-ndjson, the response contains only the persisted steps, one per line.
Creating or stepping a simulation alone does not persist a trace. Later stepping does not refresh a previous run’s trace.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | uuid | Simulation ID |
Response (JSON)
Section titled “Response (JSON)”{ "metadata": {"scenarioId": "standard", "engineVersion": "1.4.4", "ticks": 500}, "initialState": { /* Initial state fields omitted */ }, "steps": [ { "tick": 1, "move": "stay", "state": { /* State fields omitted */ }, "warning": { /* Warning signal fields omitted */ }, "loss": null, "agents": [ { "id": "default", "state": { /* State fields omitted */ }, "policy": { /* Optional lambda and gamma values omitted */ } } ] } // Remaining steps omitted ]}Trace Step Fields
Section titled “Trace Step Fields”| Field | Type | Description |
|---|---|---|
tick | number | Step count within this run, starting at 1 |
move | string | The move that was applied |
state | object | Full simulation state at this tick |
warning | object | null | Warning signal returned for this step, or null |
loss | object | null | Basin loss event, if one occurred |
agents | Agent[] | Per-agent state and policy snapshot |
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Simulation not found or trace not yet available |
Example: Streaming
Section titled “Example: Streaming”curl "https://api.anankelabs.io/v1/simulations/<simulation-id>/trace" \ -H "X-Api-Key: <api-key>" \ -H "Accept: application/x-ndjson"Update Agent Policy
Section titled “Update Agent Policy”PUT /v1/simulations/{id}/agent-policyUpdates the policy parameters for a specific agent in the cached simulation engine.
If the engine is absent from the server cache, this returns 404 NOT_FOUND. Create or step the simulation first.
The REST schema sets no numeric range for lambda or gamma.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | uuid | Simulation ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | Yes | Agent to update (default for the base policy) |
policy | object | Yes | Policy values to update |
policy.lambda | number | No | Lambda policy value |
policy.gamma | number | No | Gamma policy value |
Response
Section titled “Response”{ "ok": true }Example
Section titled “Example”curl -X PUT "https://api.anankelabs.io/v1/simulations/<simulation-id>/agent-policy" \ -H "X-Api-Key: <api-key>" \ -H "Content-Type: application/json" \ -d '{"agentId": "default", "policy": {"lambda": 0.7}}'Time Control
Section titled “Time Control”PUT /v1/simulations/{id}/time-controlControls playback of a running simulation: play, pause, or single-step.
speed has no REST default. If omitted, it keeps the session’s playback speed.
The session UI state must exist in the server cache. If absent, the endpoint returns 404 NOT_FOUND.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | uuid | Simulation ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | One of "play", "pause", or "step" |
speed | number | No | Playback speed multiplier (0.1 to 10) |
Response
Section titled “Response”{ "ok": true }Example
Section titled “Example”curl -X PUT "https://api.anankelabs.io/v1/simulations/<simulation-id>/time-control" \ -H "X-Api-Key: <api-key>" \ -H "Content-Type: application/json" \ -d '{"action": "play", "speed": 2.0}'