http://n8n-state-machine:8080 (default)
Health
GET /health
Check if the service is running.Subscriptions
POST /api/subscriptions/
Register or update a webhook subscription.
See Composition emit mode for the full suppression and heartbeat behavior.
GET /api/subscriptions
List all active subscriptions.GET /api/subscriptions/
Get a specific subscription.DELETE /api/subscriptions/
Remove a subscription. The state machine stops delivering signals for this subscription.Subscriptions are managed automatically by the trigger nodes. You typically don’t need to call these endpoints directly.
Events (Event Orchestrator)
POST /api/events/
Check or create event state for a scope. Used by the Event Orchestrator node.PATCH /api/events/
Update event state (e.g., store the global event ID after creation).Track State
GET /api/tracks/
Query the current state of a track.GET /api/tracks/closest-frame
Find the detection frame closest to a given timestamp.POST /api/tracks/snapshot
Compute position, velocity, and zone membership for one or more tracks at specific historical timestamps. The endpoint analyzes raw detections within a configurable time window around each requested timestamp.
Headers:
x-token-id, x-token-value, and x-graphql-url are required when the track’s detections are not already cached in Redis (DB 3). The endpoint will fall back to the Worlds GraphQL API to fetch detections.
Response body:
Zone State
GET /api/zones/
Query the current state of a zone (active tracks, occupancy count).Chronicle Queries
POST /api/chronicles/query
Query and aggregate activity or event chronicles over a time range. Used by the Chronicle Aggregation node resource. Pull-style: the endpoint chunks the requested window into ≤24h GraphQL fetches (bounded concurrency 4), dedupes across chunks, then filters and aggregates locally. Credentials are per-request and never persisted. Per-request timeout: 90s.The total fetch span (
end_time - start_time, plus overlap_lookback_seconds when time_match: "overlap") is capped at 100 24h chunks. Requests exceeding it are rejected with a 400 — narrow the window or reduce the lookback.record_kind: "event_chronicle" maps each chronicle internally to a zero-length interval (start = end = timestamp) so window matching and aggregation work uniformly. This means include_open is a no-op for event chronicles (they’re always closed), and site_ids/datasource_ids filters never match them (event chronicles carry no site/data-source data). An event chronicle’s producer is still a chronicle producer, so producer_ids applies normally.aggregation.output: "grouped" shown; raw returns chronicles instead of groups, raw_and_rollup returns both chronicles and rollup):
aggregation.output: "rollup" (or raw_and_rollup, alongside chronicles) returns a rollup summary instead of groups:
earliest_start/latest_end are omitted when the retained set is empty. latest_end only advances for chronicles with a known end_time (open chronicles don’t contribute).
Errors:
400 on validation failure (bad JSON, missing/invalid fields), 502 on upstream GraphQL failure (error text surfaced in the response body), 503 if the handler isn’t wired for this deployment, 405 on non-POST.
Replay
POST /api/replay/
Start a replay session for testing.GET /api/replay/
Get replay session status.DELETE /api/replay/
Stop and clean up a replay session.Replay sessions are managed automatically by the Replay Trigger node. These endpoints are documented for debugging purposes.

