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

# The Manifest (hiveku.cms.json)

> Full reference for the hiveku.cms.json file — the single source of truth for every CMS collection in your project

The manifest is a single JSON file at your project root that declares every collection in your CMS. It's the source of truth for collection IDs, field schemas, on-disk paths, URL routes, and webhooks. The CMS panel reads this file to render forms; your build reads it to find entries; the AI reads it to know what's editable.

<Info>
  The manifest is just a file in your repo — `hiveku.cms.json` at the root. You can edit it from the CMS panel's Collection Manager, ask the AI to update it, or open it directly in the code editor.
</Info>

## File Location

```
your-project/
├── hiveku.cms.json        ← the manifest
├── content/
│   ├── blog/              ← entries for the "blog" collection
│   ├── authors/
│   └── products/
├── app/
└── package.json
```

The manifest must be at the repo root and named exactly `hiveku.cms.json`. The CMS panel won't pick it up from a subdirectory.

## Top-Level Schema

```json theme={null}
{
  "version": 1,
  "collections": [...],
  "webhooks": []
}
```

| Key           | Type   | Required | Description                                                                                                                |
| ------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| `version`     | number | Yes      | Schema version. Always `1` for the current format. Bumped if a future release introduces breaking changes.                 |
| `collections` | array  | Yes      | The list of collections. Order doesn't affect behavior but does affect display order in the CMS panel's collection picker. |
| `webhooks`    | array  | No       | Outbound webhooks fired when entries change. Leave as `[]` if not used.                                                    |

## Per-Collection Schema

Each entry in `collections` looks like:

```json theme={null}
{
  "id": "blog",
  "name": "Blog Posts",
  "path": "content/blog",
  "format": "mdx",
  "slugFrom": "filename",
  "fields": [...],
  "routePattern": "/blog/{slug}",
  "defaultSort": "publishedAt desc"
}
```

| Key            | Type                             | Required | Description                                                                                                                                    |
| -------------- | -------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`           | string                           | Yes      | Stable identifier used in references, AI tools, and API URLs. Lowercase, valid identifier characters only. Must be unique across the manifest. |
| `name`         | string                           | Yes      | Human-readable label shown in the CMS panel's collection picker.                                                                               |
| `path`         | string                           | Yes      | Directory (relative to repo root) where entries are stored. By convention `content/<id>` or `content/<plural-id>`.                             |
| `format`       | `"mdx"` or `"json"`              | Yes      | Storage format. `mdx` for content with a long-form body, `json` for purely structured data.                                                    |
| `slugFrom`     | `"filename"` or `"field:<name>"` | No       | How to derive the slug. `"filename"` (default) uses the file's basename; `"field:title"` derives from a field with auto-generated kebab-case.  |
| `fields`       | array                            | Yes      | Field definitions. See [Field Types](/cms/field-types).                                                                                        |
| `routePattern` | string                           | No       | URL pattern using `{slug}` and other field placeholders. Leave off for collections that aren't directly routed.                                |
| `defaultSort`  | string                           | No       | Default sort for the entry list, e.g. `"publishedAt desc"`, `"title asc"`.                                                                     |
| `singleton`    | boolean                          | No       | Set `true` for collections that should only ever have one entry (site settings, homepage hero). The list view becomes a direct edit.           |

## Validation Rules

The CMS panel and the AI tools validate the manifest before saving. Common rules:

* **Unique `id` per collection** — two collections can't share an ID.
* **Valid identifiers** — `id` should match `[a-z][a-z0-9-]*`. Lowercase, no spaces, hyphens allowed.
* **Path must be a relative directory** — no absolute paths, no `..` traversal.
* **Exactly one `isBody: true` field per MDX collection** — the body slot is required for MDX format. JSON collections must not have any `isBody` fields.
* **Field names are unique within a collection** — no duplicate `name` values inside a single collection's `fields` array.
* **References point at real collection IDs** — a `reference` field's `collection` value must match another collection's `id` in the same manifest.
* **`slugFrom: "field:<name>"` requires a string field** — and that field must be `required: true`.

If validation fails when saving from the CMS panel, you'll get a 422 response with a list of errors. The Collection Manager surfaces them inline next to the offending field.

## Full Example

Here's a manifest covering a real site with blog posts, authors, products, and testimonials:

```json theme={null}
{
  "version": 1,
  "collections": [
    {
      "id": "authors",
      "name": "Authors",
      "path": "content/authors",
      "format": "json",
      "slugFrom": "field:name",
      "fields": [
        { "name": "name", "type": "string", "required": true },
        { "name": "bio", "type": "markdown" },
        { "name": "avatar", "type": "image" },
        { "name": "twitter", "type": "url" }
      ]
    },
    {
      "id": "blog",
      "name": "Blog Posts",
      "path": "content/blog",
      "format": "mdx",
      "slugFrom": "filename",
      "fields": [
        { "name": "title", "type": "string", "required": true },
        { "name": "excerpt", "type": "string" },
        { "name": "publishedAt", "type": "date", "required": true },
        { "name": "status", "type": "select", "options": ["draft", "published"], "default": "draft" },
        { "name": "author", "type": "reference", "collection": "authors", "required": true },
        { "name": "heroImage", "type": "image" },
        { "name": "tags", "type": "array", "items": "string" },
        { "name": "body", "type": "markdown", "isBody": true }
      ],
      "routePattern": "/blog/{slug}",
      "defaultSort": "publishedAt desc"
    },
    {
      "id": "products",
      "name": "Products",
      "path": "content/products",
      "format": "json",
      "slugFrom": "filename",
      "fields": [
        { "name": "name", "type": "string", "required": true },
        { "name": "sku", "type": "string", "required": true },
        { "name": "price", "type": "number", "required": true },
        { "name": "currency", "type": "select", "options": ["USD", "EUR", "GBP"], "default": "USD" },
        { "name": "inStock", "type": "boolean", "default": true },
        { "name": "images", "type": "array", "items": "image" },
        { "name": "description", "type": "markdown" },
        { "name": "category", "type": "string" }
      ],
      "routePattern": "/products/{slug}",
      "defaultSort": "name asc"
    },
    {
      "id": "testimonials",
      "name": "Testimonials",
      "path": "content/testimonials",
      "format": "json",
      "slugFrom": "filename",
      "fields": [
        { "name": "name", "type": "string", "required": true },
        { "name": "company", "type": "string" },
        { "name": "quote", "type": "string", "required": true },
        { "name": "avatar", "type": "image" },
        { "name": "featured", "type": "boolean", "default": false }
      ]
    },
    {
      "id": "site-settings",
      "name": "Site Settings",
      "path": "content/site-settings",
      "format": "json",
      "singleton": true,
      "fields": [
        { "name": "siteName", "type": "string", "required": true },
        { "name": "tagline", "type": "string" },
        { "name": "logo", "type": "image" },
        { "name": "primaryColor", "type": "color" }
      ]
    }
  ],
  "webhooks": [
    {
      "id": "rebuild-on-publish",
      "url": "https://api.hiveku.com/projects/your-project-id/rebuild",
      "events": ["entry.created", "entry.updated", "entry.deleted"],
      "collections": ["blog", "products"]
    }
  ]
}
```

## Webhooks

Webhooks fire when CMS entries change. Each webhook entry takes:

| Key           | Type   | Description                                                                     |
| ------------- | ------ | ------------------------------------------------------------------------------- |
| `id`          | string | Unique webhook identifier                                                       |
| `url`         | string | The endpoint to POST to                                                         |
| `events`      | array  | Subset of `entry.created`, `entry.updated`, `entry.deleted`, `manifest.updated` |
| `collections` | array  | Which collection IDs to listen to. Omit or use `["*"]` for all                  |
| `secret`      | string | Optional shared secret; included as `x-hiveku-signature` HMAC header            |

Hiveku retries failed webhook deliveries up to 3 times with exponential backoff.

## Editing the Manifest

<Tabs>
  <Tab title="From the AI">
    Just describe what you want:

    ```
    Add a "case-studies" collection with title, client, summary,
    body, and a hero image. Route it at /case-studies/{slug}.
    ```

    The AI reads the manifest, proposes the addition, and saves it after you confirm.
  </Tab>

  <Tab title="From the CMS Panel">
    Open the CMS panel, click the gear icon (Collection Manager), then **+ New Collection** or pick an existing one to edit. Field types are chosen from a dropdown, validation is inline.
  </Tab>

  <Tab title="From the Code Editor">
    Open `hiveku.cms.json` in the file tree and edit it as JSON. Save — the CMS panel reloads and validates. Invalid manifests show an error banner with the offending field.
  </Tab>
</Tabs>

## Versioning

Every edit to `hiveku.cms.json` is versioned the same way as any other file in your project, through the `builder_code_versions` system. Open the version history drawer for the manifest from the code editor to roll back schema changes.

<Warning>
  Removing a field from the manifest doesn't delete data from existing entries — the data stays in the file but won't show in the form. Re-adding a field with the same name restores the editor for that data. Renaming a field, however, orphans the old data; ask the AI to write a migration if you need to rename safely.
</Warning>

## What's Next?

<CardGroup cols={2}>
  <Card title="Field Types" icon="list" href="/cms/field-types">
    Every supported field type with examples
  </Card>

  <Card title="Collections" icon="layer-group" href="/cms/collections">
    Naming, format choice, and reusable patterns
  </Card>

  <Card title="Add a Collection" icon="plus" href="/how-tos/add-collection">
    Three ways to add a new collection
  </Card>

  <Card title="Initialize Your CMS" icon="rocket" href="/how-tos/initialize-cms">
    Scaffold a manifest from scratch
  </Card>
</CardGroup>
