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

# NSP API Reference — endpoints, auth, and error format

> All NSP REST and WebSocket endpoints in one place: base URL, API key auth, rate limits, HTTP status codes, error envelope format, and request ID tracing.

The NSP daemon exposes a REST and WebSocket API on `http://127.0.0.1:7842`. All endpoints except `GET /health` are prefixed with `/substrate/v1` and require an API key. You interact with this API from any HTTP client, the official Python SDK, or directly from an AI agent's tool calls.

## Base URL

```text theme={null}
http://127.0.0.1:7842
```

The daemon binds to the loopback address by default. You cannot reach it over the public network unless you configure a reverse proxy — this is intentional. All endpoint paths (except `/health`) share the `/substrate/v1` prefix.

## Authentication

Pass your API key in the `X-Substrate-Key` request header on every call:

```bash theme={null}
curl -H "X-Substrate-Key: sk_nsp_a1b2c3d4e5f6..." \
     http://127.0.0.1:7842/substrate/v1/apps
```

If `auth_enabled` is `false` in `/health`, you may omit the header. For the WebSocket endpoint (`/watch`), pass the key as a `key` query parameter instead, since not all WebSocket clients support custom headers at the upgrade phase.

<Note>
  Generate and revoke API keys from the [Nelieo platform dashboard](https://platform.nelieo.com/keys). Keys are scoped to your daemon instance — they are not global credentials.
</Note>

## Endpoint Summary

| Method | Path                         | Auth | Rate Limit | Description                                |
| ------ | ---------------------------- | ---- | ---------- | ------------------------------------------ |
| `GET`  | `/health`                    | None | None       | Daemon liveness probe                      |
| `GET`  | `/substrate/v1/apps`         | ✓    | 100/s      | List all tracked applications              |
| `GET`  | `/substrate/v1/state/{pid}`  | ✓    | 100/s      | Full semantic state snapshot for a process |
| `GET`  | `/substrate/v1/schema/{pid}` | ✓    | 100/s      | Available actions for a process            |
| `POST` | `/substrate/v1/action`       | ✓    | 10/s       | Execute a typed action on a process        |
| `WS`   | `/substrate/v1/watch/{pid}`  | ✓    | —          | Real-time state-change stream              |
| `GET`  | `/substrate/v1/diff/{pid}`   | ✓    | 100/s      | Field-level diff since a capture ID        |
| `POST` | `/substrate/v1/verify`       | ✓    | 100/s      | Evaluate a state expression inline         |
| `POST` | `/substrate/v1/learn/{pid}`  | ✓    | 10/s       | Submit labeling corrections                |

## Content Type

Send `Content-Type: application/json` on all `POST` requests. Every response body is `application/json`. Requests that omit the content-type header on `POST` routes return `400 Bad Request`.

## Request IDs

Every response — including errors — includes an `X-Request-Id` header containing a time-ordered unique identifier:

```text theme={null}
X-Request-Id: 01j5abc123def456
```

Include this value whenever you file a bug report or open a support ticket. The daemon logs every request with its ID, so the support team can correlate your report with the exact trace.

## Common HTTP Response Codes

| Code  | Meaning                                                       |
| ----- | ------------------------------------------------------------- |
| `200` | Request succeeded                                             |
| `400` | Bad request — missing or invalid parameters                   |
| `401` | Unauthorized — API key missing or invalid                     |
| `404` | Resource not found — PID or app not tracked                   |
| `422` | Unprocessable — action ran but post-state verification failed |
| `426` | Upgrade Required — WebSocket endpoint called over plain HTTP  |
| `429` | Rate limit exceeded                                           |
| `503` | Daemon overloaded, or probe not yet ready                     |
| `504` | Timeout — request exceeded 30 s (or 120 s for `/action`)      |

## Error Response Format

All error responses use a consistent JSON envelope. Parse the `error.code` field programmatically; use `error.message` for human-readable context:

```json theme={null}
{
  "error": {
    "code": "not_found",
    "message": "Process 12847 is not tracked. Is the app running?",
    "request_id": "01j5abc123def456"
  }
}
```

<ResponseField name="error.code" type="string">
  Machine-readable error identifier. Use this in `switch` / `match` logic. Examples: `not_found`, `rate_limited`, `verification_failed`, `probe_timeout`, `missing_verify_expression`.
</ResponseField>

<ResponseField name="error.message" type="string">
  Human-readable description of what went wrong and, where possible, how to fix it.
</ResponseField>

<ResponseField name="error.request_id" type="string">
  The same value sent in the `X-Request-Id` response header. Always include this in bug reports.
</ResponseField>

## Authentication Example

<CodeGroup>
  ```bash curl theme={null}
  curl -i \
    -H "X-Substrate-Key: sk_nsp_a1b2c3d4e5f6..." \
    http://127.0.0.1:7842/substrate/v1/apps
  ```

  ```python Python SDK theme={null}
  from nelieo import NspClient

  client = NspClient(api_key="sk_nsp_a1b2c3d4e5f6...", base_url="http://127.0.0.1:7842")

  # The SDK injects X-Substrate-Key automatically on every request.
  apps = await client.list_apps()
  ```
</CodeGroup>
