/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
The OS process ID to watch. Obtain this from
GET /apps.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.OS process ID of the process that changed.
Stable semantic identifier for the application.
UUIDv7 identifier for the new state snapshot. Pass this to
GET /diff/{pid}?since={capture_id} to retrieve the full field-level diff.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).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.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.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
1000and 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 than1000), 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.
