> ## Documentation Index
> Fetch the complete documentation index at: https://docs.worlds.io/llms.txt
> Use this file to discover all available pages before exploring further.

# State Machine API

> REST API reference for the state machine service

The state machine exposes a REST API for subscription management, event state, track/zone queries, and replay sessions.

**Base URL:** `http://n8n-state-machine:8080` (default)

## Health

### GET /health

Check if the service is running.

```bash theme={null}
curl http://n8n-state-machine:8080/health
```

```json theme={null}
{ "status": "ok" }
```

## Subscriptions

### POST /api/subscriptions/{id}

Register or update a webhook subscription.

```bash theme={null}
curl -X POST http://n8n-state-machine:8080/api/subscriptions/my-workflow \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "http://n8n:5678/webhook/detections",
    "workflow_name": "obstruction-detection",
    "mode": "streaming",
    "signal_type": "track_state",
    "filters": {
      "datasource_ids": ["507eaeca-cc89-4501-9e0d-0d062a8f355d"],
      "tags": ["forklift", "person"]
    },
    "credentials": {
      "graphql_url": "https://your-instance.worlds.io/graphql",
      "ws_url": "wss://your-instance.worlds.io/graphql",
      "x_token_id": "your-token-id",
      "x_token_value": "your-token-value"
    }
  }'
```

### GET /api/subscriptions

List all active subscriptions.

### GET /api/subscriptions/{id}

Get a specific subscription.

### DELETE /api/subscriptions/{id}

Remove a subscription. The state machine stops delivering signals for this subscription.

<Note>
  Subscriptions are managed automatically by the trigger nodes. You typically don't need to call these endpoints directly.
</Note>

## Events (Event Orchestrator)

### POST /api/events/{scope_id}

Check or create event state for a scope. Used by the Event Orchestrator node.

```bash theme={null}
curl -X POST http://n8n-state-machine:8080/api/events/track-uuid-here \
  -H "Content-Type: application/json" \
  -d '{
    "scope_type": "track",
    "workflow_id": "workflow-uuid",
    "workflow_name": "obstruction-detection",
    "conditions_met": true
  }'
```

### PATCH /api/events/{scope_id}

Update event state (e.g., store the global event ID after creation).

## Track State

### GET /api/tracks/{track_id}

Query the current state of a track.

```bash theme={null}
curl http://n8n-state-machine:8080/api/tracks/019bb7ea-4050-7e9b-8761-f8df773fd3e5
```

Returns the full track state object (position, velocity, zones, etc.).

### GET /api/tracks/closest-frame

Find the detection frame closest to a given timestamp.

```bash theme={null}
curl "http://n8n-state-machine:8080/api/tracks/closest-frame?track_id=019bb7ea&timestamp=2026-01-13T15:12:52Z"
```

### 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.

```bash theme={null}
curl -X POST http://n8n-state-machine:8080/api/tracks/snapshot \
  -H "Content-Type: application/json" \
  -H "x-token-id: your-token-id" \
  -H "x-token-value: your-token-value" \
  -H "x-graphql-url: https://your-instance.worlds.io/graphql" \
  -d '{
    "tracks": [
      {
        "track_id": "019bb7ea-4050-7e9b-8761-f8df773fd3e5",
        "timestamp": "2026-01-13T15:12:52.000Z"
      }
    ],
    "window_seconds": 10
  }'
```

**Request body:**

| Field                | Type   | Required | Description                                                     |
| -------------------- | ------ | -------- | --------------------------------------------------------------- |
| `tracks`             | Array  | Yes      | One or more track queries                                       |
| `tracks[].track_id`  | String | Yes      | Track UUID to query                                             |
| `tracks[].timestamp` | String | Yes      | ISO 8601 timestamp to compute the snapshot at                   |
| `window_seconds`     | Number | No       | Seconds before and after the timestamp to include (default: 10) |

**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:**

| Field                                   | Type   | Description                                                                      |
| --------------------------------------- | ------ | -------------------------------------------------------------------------------- |
| `snapshots`                             | Object | Map of track ID to snapshot (null if the track could not be resolved)            |
| `snapshots[].track_id`                  | String | Track UUID                                                                       |
| `snapshots[].tag`                       | String | Object type tag (e.g., `Person`, `Vehicle`)                                      |
| `snapshots[].timestamp`                 | String | The requested timestamp                                                          |
| `snapshots[].window_seconds`            | Number | The window used for computation                                                  |
| `snapshots[].detections_in_window`      | Number | Number of raw detections found in the window                                     |
| `snapshots[].position`                  | Object | Interpolated position at the timestamp                                           |
| `snapshots[].position.method`           | String | `real`, `linear`, `forward_extrapolation`, or `backward_extrapolation`           |
| `snapshots[].position.offset_ms`        | Number | Milliseconds between the requested timestamp and the nearest real detection      |
| `snapshots[].position.pix`              | Object | Pixel coordinates (`x`, `y`)                                                     |
| `snapshots[].position.geo`              | Object | Geographic coordinates (`lon`, `lat`), if available                              |
| `snapshots[].position.polygon`          | Array  | Interpolated bounding box as a 4-point polygon                                   |
| `snapshots[].motion`                    | Object | Velocity and direction computed from detections in the window                    |
| `snapshots[].motion.pix`                | Object | Pixel-space motion (`avg_velocity`, `max_velocity`, `direction`, `sample_count`) |
| `snapshots[].motion.geo`                | Object | Geographic motion (same fields), if available                                    |
| `snapshots[].zones`                     | Object | Zone membership at the timestamp                                                 |
| `snapshots[].zones.active_at_timestamp` | Object | Map of zone ID to zone data for zones the track intersected at the timestamp     |

## Zone State

### GET /api/zones/{zone_id}

Query the current state of a zone (active tracks, occupancy count).

## Replay

### POST /api/replay/{id}

Start a replay session for testing.

```bash theme={null}
curl -X POST http://n8n-state-machine:8080/api/replay/test-session \
  -H "Content-Type: application/json" \
  -d '{
    "track_ids": ["019bb7ea-4050-7e9b-8761-f8df773fd3e5"],
    "mode": "auto"
  }'
```

### GET /api/replay/{id}

Get replay session status.

### DELETE /api/replay/{id}

Stop and clean up a replay session.

<Note>
  Replay sessions are managed automatically by the [Replay Trigger](/nodes/replay-trigger) node. These endpoints are documented for debugging purposes.
</Note>
