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

# Overview

# Nora REST API overview and base URLs

> A complete reference for the Nora REST API: base URLs, authentication, rate limiting, content type, health check, and public config endpoints.

The Nora REST API is a JSON-over-HTTP interface that lets you deploy agents, manage LLM providers, configure channels, and observe platform health programmatically. Every request must be authenticated — with either a session JWT or a workspace API key — except for the public health, config, and webhook endpoints listed below. All request and response bodies use `application/json`.

<Tip>
  Every Nora instance also serves a machine-readable **OpenAPI 3.1 spec** at `GET /api/api.json` and
  an **interactive API reference** at `/api/api-docs`. The spec covers agents, schedules and
  versions, monitoring, LLM providers, auth, workspaces, API keys, alerts, integrations, channels,
  backups, Agent Hub, self-hosted-only remote hosts, and the admin doctor endpoint. CI drift-tests
  those operations against the actual routers, so documented routes cannot silently disappear;
  browser-only admin and experimental surfaces remain intentionally outside the spec.
</Tip>

## Base URL

The Nora API is served under the `/api` path of your Nora origin. In local mode this is:

```
http://localhost:8080/api
```

If you configured a public domain, replace `http://localhost:8080` with your origin — for example:

```
https://app.example.com/api
```

<Note>
  All paths in this reference omit the `/api` prefix for brevity. When making real requests, include
  `/api` — for example `http://localhost:8080/api/agents`.
</Note>

## Authentication

Protected endpoints accept **either** of two credentials.

**1. A session JWT** — supplied as a Bearer token:

```
Authorization: Bearer <jwt>
```

Obtain one via `POST /auth/login`. Tokens are valid for **7 days** and are also accepted as an HttpOnly session cookie. See the [Authentication](/api/authentication) page for the full login flow.

**2. A workspace API key** — supplied as a Bearer token (the key begins with `nora_`) or in the `X-Api-Key` header (alias `X-Nora-Api-Key`):

```
Authorization: Bearer nora_<key>
```

```
X-Api-Key: nora_<key>
```

API keys carry **scopes** that are enforced per endpoint, drawn from the recognized v1 set:

| Scope                | Grants                                         |
| -------------------- | ---------------------------------------------- |
| `agents:read`        | Read agents in the workspace                   |
| `agents:write`       | Create, update, and operate agents             |
| `workspaces:read`    | Read workspace metadata and members            |
| `monitoring:read`    | Read monitoring metrics and events             |
| `integrations:read`  | Read integration configurations                |
| `integrations:write` | Create and remove integrations                 |
| `admin:read`         | Run read-only diagnostics as the issuing admin |

Session-authenticated requests skip scope checks (role-based guards apply instead). A request whose API key lacks the required scope is rejected with `403` and `{ "code": "missing_scope" }`.

Explicit `Authorization`, `X-Api-Key`, and `X-Nora-Api-Key` credentials take precedence over the
ambient session cookie. Nora rejects conflicting explicit credentials with `400` and `{ "code":
"conflicting_auth" }` instead of silently selecting the more privileged identity.

Agent scopes are also workspace-bound: an API key can list or address only agents assigned to the
workspace where the key was issued, even when its issuing user owns or can access agents elsewhere.
Non-Remote agents created through deploy, adopt, or duplicate are atomically assigned to that
workspace before queue or runtime work begins.

The issuing user must remain an active user and a member or owner of the bound workspace. Removing
that issuer makes the key unusable; issue a replacement from another workspace admin instead of
allowing a retained credential to impersonate a deleted or removed account.

<Note>
  Some operations are **session-only** and reject API keys even with a valid scope — including demo
  activation, migration-draft deployment, Remote Docker placement and existing-agent operations,
  user-global LLM-provider credentials, platform-wide performance monitoring, workspace writes, and
  API-key issuance. These return `403` with `{ "code": "session_required" }`. Workspace monitoring
  and cost reads remain available with their documented scopes, but are restricted to the key's
  exact bound workspace.
</Note>

## Rate limiting

Two rate-limit tiers apply to every request:

| Tier                       | Limit          | Window     |
| -------------------------- | -------------- | ---------- |
| Global (all endpoints)     | 1 000 requests | 15 minutes |
| Auth endpoints (`/auth/*`) | 20 requests    | 15 minutes |

When you exceed a limit the API returns `429 Too Many Requests` with:

```json theme={null} theme={null}
{ "error": "Too many attempts, please try again later" }
```

## Content type

All request bodies must be sent as JSON. Set the header on every mutating request:

```
Content-Type: application/json
```

## Public endpoints

These endpoints do not require authentication.

### Health check

```
GET /health
```

Returns `{"status":"ok"}` after the server has finished its startup sequence, including database migrations and catalog seeding. The API does not bind its HTTP listener until that sequence succeeds.

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl http://localhost:8080/api/health
  ```
</CodeGroup>

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

### Platform config

```
GET /config/platform
```

Returns the platform mode, runtime/deploy catalogs, safe public feature capabilities, and, in self-hosted deployments, the operator-configured resource limits.

<ResponseField name="mode" type="string">
  Platform mode. One of `selfhosted` or `paas`.
</ResponseField>

<ResponseField name="selfhosted" type="object | null">
  Resource limits when `mode` is not `paas`, otherwise `null`.

  <Expandable title="properties">
    <ResponseField name="max_vcpu" type="number">Maximum vCPU per agent.</ResponseField>
    <ResponseField name="max_ram_mb" type="number">Maximum RAM in MB per agent.</ResponseField>
    <ResponseField name="max_disk_gb" type="number">Maximum disk in GB per agent.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="billingEnabled" type="boolean">
  Whether Stripe billing is active.
</ResponseField>

<ResponseField name="capabilities.localDockerDemo" type="object">
  Whether the exact `openclaw` + local `docker` + `standard` runtime tuple required by one-click
  demo activation is enabled. Includes the tuple, `requiresLiveDocker: true`, and a public `issue`
  when unavailable. The mutation still checks the Docker daemon live before queueing work.
</ResponseField>

### NemoClaw config

```
GET /config/nemoclaw
```

Returns the NemoClaw sandbox configuration for the current deployment.

<ResponseField name="enabled" type="boolean">
  Whether the NemoClaw sandbox is enabled on this server.
</ResponseField>

<ResponseField name="defaultModel" type="string">
  Default NVIDIA model identifier.
</ResponseField>

<ResponseField name="sandboxImage" type="string">
  OCI image used for NemoClaw sandboxes.
</ResponseField>

<ResponseField name="models" type="string[]">
  List of supported NVIDIA model identifiers.
</ResponseField>

## Quick example

The following request authenticates and lists your agents:

<CodeGroup>
  ```bash curl theme={null} theme={null}
  # 1. Get a token
  TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login \
    -H "Content-Type: application/json" \
    -d '{"email":"you@example.com","password":"yourpassword"}' \
    | jq -r .token)

  # 2. Call a protected endpoint

  curl http://localhost:8080/api/agents \
  -H "Authorization: Bearer $TOKEN"

  ```
</CodeGroup>
