# set_chapter_intro

Set the structured front matter of an intro chapter.

`PUT /v1/chapters/{collectionId}/intro` · MCP tool `set_chapter_intro` · scope `write` · stable · since 2026-09-17

Editorial sections the viewer renders as the front matter: { coverKicker?, coverTitle? (default: the guide name), coverAuthor? (byline name, default: the creator), coverAuthorPhotoStorageId?, sections: [{ kind?: prose|chapters|collections, kicker?, title, deck?, paragraphs? (lines starting with "- " render as a list), cells?: [{ title, body }], photoStorageId?, photoCredit?, items? }] }. items only on chapters/collections sections, one per collectionId: chapters rows take { line?, color? (#rrggbb) }, collections cards take { title?, photoStorageId?, photoCredit? }. Rows and cards still derive from the guide; items only dress them. Photo ids come from the media upload endpoint with target=blob. Null clears. Chapters only.

## Path parameters

- `collectionId` (string, required): Id of the chapter.

## Body parameters

- `intro` (ChapterIntro or null, required): The front matter object, or null to clear.
  Fields of ChapterIntro: https://developers.sceniq.earth/fields/chapter-intro.md
- `expectedUpdatedAt` (number, optional): Optional staleness guard: the chapter's updatedAt you last read (list_chapters). The write is refused with stale_editor when the chapter changed since.

## Returns

200.

- `ok` (boolean, required): Always true: the write went through. Always `true`.
- `collectionId` (string, required): The chapter's or collection's id.

## Examples

### Write the guide's front matter

```bash
curl -X PUT "https://sceniq.earth/api/v1/chapters/kn7q65ntqkryzm6y45h5dyxqt0p454y8/intro" \
  -H "Authorization: Bearer $SCENIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "intro": {
    "coverKicker": "A photographer'\''s field guide",
    "sections": [
      {
        "kind": "prose",
        "kicker": "How to use this guide",
        "title": "Timed for the light",
        "deck": "Every spot says when to be there and where to park, so the light and the crowds work for you.",
        "paragraphs": [
          "Each spot lists its best time of day, how crowded it gets and how far it is from the car. Most are short walks; the hikes are marked.",
          "- One day per park? Start with Top picks.\n- Check the park road status before a sunrise drive."
        ],
        "photoStorageId": "kg2gsfxz27kpg15n437vtmc3zj91qz34"
      },
      {
        "kind": "prose",
        "kicker": "Plan",
        "title": "Before you go",
        "cells": [
          {
            "title": "Passes",
            "body": "Each park charges 35 USD per vehicle for 7 days. An annual pass covers both."
          },
          {
            "title": "Seasons",
            "body": "Late May to September suits both parks. Some roads close from November."
          },
          {
            "title": "Driving",
            "body": "Yellowstone to Yosemite is about 1,400 km by road: two long days."
          },
          {
            "title": "Drones",
            "body": "Banned in every US national park."
          }
        ]
      },
      {
        "kind": "chapters",
        "kicker": "The parks",
        "title": "Two parks, a long drive apart",
        "items": [
          {
            "collectionId": "kn7e5r9t3y7v1j5p9p3a7s1d5f9g3h7j",
            "line": "Hot springs, geysers and bison",
            "color": "#b45309"
          },
          {
            "collectionId": "kn73r7x6mp88mxa9swcbshwd3e7zvr39",
            "line": "Granite walls and the valley from above",
            "color": "#1d4ed8"
          }
        ]
      },
      {
        "kind": "collections",
        "title": "Short on time",
        "items": [
          {
            "collectionId": "kn7a2s4d6f8g0h2j4k6k8z0x2c4v6b8n",
            "photoStorageId": "kg29fadcmt160qtrce3h3rm82yc6hhtb"
          }
        ]
      }
    ]
  },
  "expectedUpdatedAt": 1789895640000
}'
```

MCP `tools/call`:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "set_chapter_intro",
    "arguments": {
      "collectionId": "kn7q65ntqkryzm6y45h5dyxqt0p454y8",
      "intro": {
        "coverKicker": "A photographer's field guide",
        "sections": [
          {
            "kind": "prose",
            "kicker": "How to use this guide",
            "title": "Timed for the light",
            "deck": "Every spot says when to be there and where to park, so the light and the crowds work for you.",
            "paragraphs": [
              "Each spot lists its best time of day, how crowded it gets and how far it is from the car. Most are short walks; the hikes are marked.",
              "- One day per park? Start with Top picks.\n- Check the park road status before a sunrise drive."
            ],
            "photoStorageId": "kg2gsfxz27kpg15n437vtmc3zj91qz34"
          },
          {
            "kind": "prose",
            "kicker": "Plan",
            "title": "Before you go",
            "cells": [
              {
                "title": "Passes",
                "body": "Each park charges 35 USD per vehicle for 7 days. An annual pass covers both."
              },
              {
                "title": "Seasons",
                "body": "Late May to September suits both parks. Some roads close from November."
              },
              {
                "title": "Driving",
                "body": "Yellowstone to Yosemite is about 1,400 km by road: two long days."
              },
              {
                "title": "Drones",
                "body": "Banned in every US national park."
              }
            ]
          },
          {
            "kind": "chapters",
            "kicker": "The parks",
            "title": "Two parks, a long drive apart",
            "items": [
              {
                "collectionId": "kn7e5r9t3y7v1j5p9p3a7s1d5f9g3h7j",
                "line": "Hot springs, geysers and bison",
                "color": "#b45309"
              },
              {
                "collectionId": "kn73r7x6mp88mxa9swcbshwd3e7zvr39",
                "line": "Granite walls and the valley from above",
                "color": "#1d4ed8"
              }
            ]
          },
          {
            "kind": "collections",
            "title": "Short on time",
            "items": [
              {
                "collectionId": "kn7a2s4d6f8g0h2j4k6k8z0x2c4v6b8n",
                "photoStorageId": "kg29fadcmt160qtrce3h3rm82yc6hhtb"
              }
            ]
          }
        ]
      },
      "expectedUpdatedAt": 1789895640000
    }
  }
}
```

Response 200:

```json
{
  "ok": true,
  "collectionId": "kn7q65ntqkryzm6y45h5dyxqt0p454y8"
}
```

### Clear the front matter

```bash
curl -X PUT "https://sceniq.earth/api/v1/chapters/kn7q65ntqkryzm6y45h5dyxqt0p454y8/intro" \
  -H "Authorization: Bearer $SCENIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "intro": null
}'
```

MCP `tools/call`:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "set_chapter_intro",
    "arguments": {
      "collectionId": "kn7q65ntqkryzm6y45h5dyxqt0p454y8",
      "intro": null
    }
  }
}
```

Response 200:

```json
{
  "ok": true,
  "collectionId": "kn7q65ntqkryzm6y45h5dyxqt0p454y8"
}
```

## Errors

- `invalid_argument` (400): A required field is missing, a field is unknown, or a value does not match its type (the Convex validator's path and value are in the message).
- `invalid_request` (400): A writer refused a value: a map link on a host that is not allowed, a title that is too long, a date that does not exist, too many items in a list. The message is the writer's own sentence.
- `stale_editor` (409): The row changed after the expectedUpdatedAt you sent (update_chapter, save_sales_page_draft).
- `unauthenticated` (401): The request carries no key. Send it as Authorization: Bearer sk_sceniq_... (or X-Api-Key).
- `invalid_api_key` (401): The key is unknown, revoked or expired.
- `api_scope_required` (403): A read-only key called a write operation, or a key without the publish option called publish_changes.
- `forbidden` (403): The key belongs to a creator account that is no longer active.
- `not_found` (404): The id does not exist or belongs to another creator. The two answers are identical on purpose, so ids never reveal what exists.
- `payload_too_large` (413): A JSON body over 2 MB, an image over 6 MB, a batch upload over 19 MB, or an MCP message over 2 MB.
- `rate_limited` (429): Over a limit: requests per key per minute, per creator per hour, uploads per key per minute, or check_links per guide per hour.
- `internal_error` (500): An unexpected failure. The message is hidden on purpose.

Reference: https://developers.sceniq.earth/reference/set_chapter_intro
