> Public product help. This page has no customer entries, projects, chat transcripts, API keys, or horizon contents.
> Reading that data requires `Authorization: Bearer` (a signed-in app session JWT, or a Headless access token).
> Do not call Supabase `/rest/v1` with a Headless JWT. Data reads go through `/casc/v1/headless/v1/rest`.

# Headless API

The **Headless** feature lets external servers call Zagger programmatically using integration credentials (`client_id` + `client_secret`). It is aimed at product integrations, automation, and multi-tenant backends — not at end users clicking in the UI.

## Who can use it

- **Admin / superadmin** — see the **Headless** top-level menu: Review (by owner user or sub-user) and Integrations (mint credentials).
- **External servers** — use the REST API with tokens minted by an admin. No Supabase login required for API calls.

## Core concepts

| Term | Meaning |
| --- | --- |
| **Integration** | Pairing record with `client_id` and secret; assigned to an **owner Cascade user** whose keys, horizon, and permissions apply. |
| **Sub-user** | Human-readable id your server sends as `sub_user` (e.g. `acme-customer-42`). Unique per integration; unlimited count in v1. |
| **Sub-user project** | Auto-created chat project per sub-user (`headless:{external_key}`). Persists in the database — the same project is reused on every token exchange. All chat, sessions, and message history for that sub-user stay in this project indefinitely. |
| **Access token** | Short-lived Bearer JWT (~1 hour) scoped to integration + sub-user + project. |
| **Refresh token** | Long-lived token; rotates on each refresh. |

## Admin UI

1. **Headless → Integrations** — create integration, pick **owner user** and **default horizon**, copy one-time `client_secret`.
2. **Headless → Review** — sort by owner user (grouped) or sub-user (flat table); filter; see each sub-user’s **horizon**; open linked projects.

The owner user should have API keys configured under **Settings → User**. Agent behavior in headless projects follows **Project Settings → Agent Settings** for that auto-provisioned project (see [settings-system-vs-agent](settings-system-vs-agent.md)).

## Horizon (tools and data)

Every headless auto-project is stored on one horizon (suffix). That horizon is **not** the admin’s current Active Horizon unless they happen to match.

| Source | What it controls |
| --- | --- |
| **Integration → Default horizon** | Used when a new sub-user project is created. Blank means the owner user’s **Settings → User → Active Horizon** at create time. |
| **Sub-user row → Horizon** (Review) | The suffix stored on that auto-project. Tools, ingestion groups, and chat tables for this sub-user live here. |
| **Settings → Tools** on that horizon | The same project global allowlist as a normal project on that horizon (plus account web search). See [tools](tools.md). |

In the Zagger UI, **View project** opens the auto-project even if your Active Horizon is different. **Project settings** shows the bound horizon. Edit **Agent system message** and **Opening message** there the same way as any other project.

## Project load message (opening message)

In the Cascade UI, opening a project with no session selected shows **Start a new chat**. If Project Settings has an **Opening message**, that text is rendered there (markdown when the user has markdown enabled). If it is blank, the default hint is: “Type a message below to create a new session for this project.”

This is **display-only**. It is not the agent system message and is not sent to the model. Set it under **Project settings → Opening message**. Headless `GET /projects` and `GET /projects/:id` return the same field as `opening_description`.

Embeds should show it the same way:

1. Call `GET /casc/v1/headless/v1/projects` (or `/projects/current` / `/projects/:id`).
2. When starting a **new** chat (no `session_id` yet), render `opening_description` above the composer if it is non-empty.
3. If it is null or empty, show a short default hint. Do not inject this text into `POST /chat/send`.

See also [projects](projects.md) (Opening message) and `cascade-node-server/docs/headless-api.md` (Project load message).

## Typical server flow

1. `POST /casc/v1/headless/v1/token` with `client_id`, `client_secret`, `sub_user`.
2. `POST /casc/v1/headless/v1/chat/send` with Bearer token and message content.
3. `GET .../chat/requests/{id}/wait` or `/result` for markdown reply and optional YUML links.
4. Optional: ingest planes → plan → apply on agent messages (previews persist until applied).
5. Retrieve full history: list sessions, then page through `/sessions/{id}/messages` with a valid token for that sub-user.
6. If wait/result returns `needs_ui_action`, show your dialog and `POST /ui-actions/{id}/resolve` (see [headless-user-choice](headless-user-choice.md)).
7. Deep Research: poll `GET /casc/v1/headless/v1/research/jobs/{job_id}` and `.../report` — do not ask chat to “fetch” the job.
8. Entry create/update apply immediately by default (`require_entry_confirmation: false` on the integration). Toggle in Headless → Integrations if you need Cascade-style confirm (no Headless confirm UI yet).
9. Optional: query horizon entries with PostgREST — `GET /casc/v1/headless/v1/rest/entries?raw=cs.{"status":"ok"}` (see [headless-postgrest](headless-postgrest.md)).

## Embed / multi-project (external frontends)

Today each `sub_user` maps to **one** auto-created project (`headless:{external_key}`) with unlimited sessions.
To put a full Zagger chat experience on another website — multiple Zagger-defined projects coded into your UI,
workflow buttons, Find Record / Ingest popups, and a dialog bridge that returns choice or cancel —
start with [headless-user-choice](headless-user-choice.md) (front/back loop) and the integrator handoff guide:

- **Architecture, Phase 0 how-to, UI catalog, dialog-bridge spec, phased backlog** — `cascade-node-server/docs/headless-embed-guide.md` in the repository.
- Keep secrets on your BFF; browsers must not hold `client_secret`.

## API reference

Full endpoint reference, curl examples, security notes, and migration steps: `cascade-node-server/docs/headless-api.md` in the repository.
For multi-project embed and dialog bridge planning, see `cascade-node-server/docs/headless-embed-guide.md`.

## Not the same as…

- **Settings → System** — instance-wide platform config (embedding model, system API keys). See [settings-system-vs-agent](settings-system-vs-agent.md).
- **User Supabase JWT** — browser login tokens; headless uses its own token type. Do not send the headless Bearer token to Supabase `/rest/v1`; use `/casc/v1/headless/v1/rest` instead ([headless-postgrest](headless-postgrest.md)).
- **Research API** — background research jobs; separate from headless chat.
