---
order: 20
title: Docs API
path: reference/api-endpoints/docs
status: published
---

# Docs API

Endpoints under `/api/v1/docs/*` for managing namespaces, versions, pages,
mounts, and search. Authentication: JWT (superuser) or an API key with the
`docs.manage` permission slug (see [API keys](/docs/scaicms/concepts/api-keys)).

For the full conceptual model, see [Docs subsystem](/docs/scaicms/concepts/docs-subsystem).

## `POST /api/v1/docs/_namespaces`

Create a doc namespace. By default this just inserts a `doc_namespaces` row —
no version, no mount. Two query flags let you fold setup into a single call.

### Body

```json
{
  "slug": "scaidial",
  "name": "ScaiDial",
  "description": "ScaiDial — outbound notification dispatch.",
  "visibility": "public",
  "parent_namespace_id": "<uuid-or-null>",
  "theme": {"primary": "#…"}
}
```

### `?bootstrap=true` — one-shot namespace + v1 + mount

Atomically creates the namespace, a default `v1` version
(`status=published`, `is_default=1`), and a `site_doc_mounts` row on the
site identified by the `X-Site-ID` header.

- Top-level namespace → mount path `/docs/<slug>`
- Sub-product (when `parent_namespace_id` is set) → `/docs/<parent_slug>/<slug>`

Requires `X-Site-ID`. If the resolved mount path is already in use on the
site, the call is rejected with 409 — nothing is partially created.

```bash
curl -X POST "$BASE/api/v1/docs/_namespaces?bootstrap=true" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Site-ID: $SITE_ID" \
  -H "Content-Type: application/json" \
  -d '{"slug":"scaidial","name":"ScaiDial","visibility":"public"}'
```

Response (201) gains a `bootstrap` sub-object:

```json
{
  "id": "…", "slug": "scaidial", "default_version_id": "…",
  "bootstrap": {
    "version_id": "…",
    "version_slug": "v1",
    "mount_id": "…",
    "mount_path": "/docs/scaidial"
  }
}
```

### `?extend_group_scopes=true` — auto-grant scopes to issuing key's group

Appends the six docs/downloads scopes for the new slug
(`docs:{read,write,manage}:<slug>`, `downloads:{read,write,delete}:<slug>`)
to **every active API key in the caller's group**. Idempotent — keys that
already carry a scope are left unchanged. Every mutated key gets an
`api_key_scope_audits` row recording the addition with
`reason=bootstrap_extend:<slug>`.

Constraints:

- Caller **must** authenticate with an API key (JWTs cannot use this flag).
- That API key **must** be group-bound (`group_id IS NOT NULL`).
  User-bound keys cannot self-extend.

Response gains an `extended_scopes` map keyed by `api_key_id`:

```json
{
  "extended_scopes": {
    "<api-key-uuid>": [
      "docs:read:scaidial", "docs:write:scaidial", "docs:manage:scaidial",
      "downloads:read:scaidial", "downloads:write:scaidial", "downloads:delete:scaidial"
    ]
  }
}
```

If no keys needed extending (everyone already had the scopes), the map is
present but empty.

### Combining the flags

The typical "create a sub-product end-to-end" call uses both:

```bash
curl -X POST "$BASE/api/v1/docs/_namespaces?bootstrap=true&extend_group_scopes=true" \
  -H "Authorization: Bearer $GROUP_API_KEY" \
  -H "X-Site-ID: $SITE_ID" \
  -H "Content-Type: application/json" \
  -d '{"slug":"scaidial","name":"ScaiDial","description":"…","visibility":"public"}'
```

One round-trip yields the namespace, its published `v1`, the mount on the
site, and the scopes propagated across the issuing group's fleet of keys.

### Error codes

| Code | HTTP | Meaning |
|---|---|---|
| `BOOTSTRAP_REQUIRES_SITE` | 400 | `?bootstrap=true` was sent without `X-Site-ID`. |
| `EXTEND_REQUIRES_API_KEY` | 400 | `?extend_group_scopes=true` was sent on a JWT request. |
| `EXTEND_REQUIRES_GROUP_KEY` | 400 | Caller's API key is user-bound, not group-bound. |
| `BOOTSTRAP_MOUNT_CONFLICT` | 409 | Resolved mount path already exists on this site. |

## Other routes

See the FastAPI Swagger at `$BASE/docs` for the full surface area. Highlights:

- `POST /api/v1/docs/{ns}/versions` — fork a version
- `PUT  /api/v1/docs/{ns}/{ver}/{path}` — upsert a page
- `GET  /api/v1/docs/{ns}/{ver}/tree` — full page tree
- `POST /api/v1/docs/{ns}/{ver}/search` — hybrid Weaviate search
- `POST /api/v1/docs/{ns}/{ver}/mounts` — attach to additional sites
