Skip to main content
The /watch endpoint upgrades an HTTP connection to a WebSocket and streams StateChangeNotification messages every time the NSP daemon detects that a tracked process’s state has changed. Instead of polling GET /state on a timer, you open one connection and react to events as they arrive. This is the recommended pattern for real-time agent loops, dashboards, and automated test frameworks that need to observe application behaviour continuously.

Endpoint

Parameters

pid
integer
required
The OS process ID to watch. Obtain this from GET /apps.
key
string
required
Your API key. Because many WebSocket client libraries do not support custom HTTP headers at the upgrade handshake phase, the daemon accepts the API key as a key query parameter for this endpoint. If your client does support the X-Substrate-Key header at upgrade, both methods are accepted.

Message Format

The server pushes one JSON message per probe cycle in which a state change is detected. Messages are newline-terminated UTF-8 JSON.
pid
integer
required
OS process ID of the process that changed.
app_id
string
required
Stable semantic identifier for the application.
capture_id
string
required
UUIDv7 identifier for the new state snapshot. Pass this to GET /diff/{pid}?since={capture_id} to retrieve the full field-level diff.
prev_capture_id
string | null
required
UUIDv7 identifier of the previous snapshot. null on the first message after connecting. Use the gap between prev_capture_id and capture_id to detect if any captures were missed (e.g., after a reconnect).
sequence
integer
required
Monotonically increasing counter per connection. If you receive a message where sequence is not exactly previous_sequence + 1, events were dropped (typically during a reconnect). Re-fetch the full state via GET /state to resynchronize.
changed_keys
string[]
required
Array of dot-notation state keys that changed since the previous capture, e.g. ["inbox.unread_count", "inbox.emails[0].is_read"]. Use this list to selectively update your agent’s state model without re-reading the entire objects map.
timestamp
datetime
required
ISO 8601 timestamp of when the state change was captured.

Example Message

Connection Behaviour

  • Event frequency: Messages fire on every probe cycle in which a state change is detected. The default probe interval is 2 seconds.
  • No-change cycles: The server does not send a message if no keys changed since the last capture. The connection stays open silently.
  • Process exit: When the tracked process exits, the server closes the WebSocket with close code 1000 and reason "process_exited".
  • Daemon restart: If the daemon restarts, all open connections are dropped. Your client must reconnect.
  • Heartbeat: The server does not send explicit ping frames. Rely on TCP keepalive and your client’s built-in WebSocket timeout to detect dead connections. Implement exponential-backoff reconnection on your side.

Examples

Reconnection Strategy

After any unexpected close (code other than 1000), reconnect with exponential backoff starting at 1–2 seconds and capping at 30 seconds. On reconnect, call GET /state once to resynchronize your state model — you may have missed capture events during the gap.

Error Responses (HTTP Upgrade Phase)

These errors are returned as HTTP responses before the WebSocket upgrade completes: