---
title: "Update the site brief"
method: PUT
path: "/api/v1/sites/{slug}/brief"
tags: ["Brief"]
---

# Update the site brief

`PUT /api/v1/sites/{slug}/brief`

## Request

- Base URL: `https://api.dotsy.ai`
- URL: `https://api.dotsy.ai/api/v1/sites/{slug}/brief`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

Edits the site brief and leaves what you do not send alone. Send at least one of: `head` (fields to change; `null` clears an optional field, `purpose` can only be replaced), `body` (replaces the free text), `goal` and `notes` (replace their sections), `rules` (add or change rules & guidelines, matched by `id`), `removeRules` (delete lines by `id`) and `appendHandoff` (adds a line to the hand-off log). `notes` is a string (the free-form notes) or `{ design, content, seo }` (per-section notes; `null` clears one). `rules` patch-merges by id (omitted fields keep stored values; `check: null` clears). A site's first brief write must include `head.purpose` (5–280 characters).

A locked rule can only be changed, removed, locked or unlocked by a person in the dashboard: any other caller gets 403 `rule_locked` or `lock_is_for_people`. The `X-Dotsy-Brief-Actor` header is ignored.

With `?page=<content key>` the same call edits that page's brief instead (`head`, `body`, `goal`, `notes`). The page must exist.

## Path parameters

- `slug` string, required

## Query parameters

- `page` string

## Request body

- union: A site brief edit, or — with `?page=` — a page brief edit.
  - UpdateSiteBriefRequest: Site brief edit. Send at least one of `head`, `body`, `goal`, `notes`, `rules`, `removeRules`, `appendHandoff`. A first write must include `head.purpose`. `notes` is a string (the free-form notes) or `{ design, content, seo }` (per-section notes). `rules` upserts rules and guidelines by id; `removeRules` deletes by id.
    - `head` object
      - `purpose` string
      - `audience` string, nullable
      - `kind` 'exploration' | 'production', nullable
      - `priorities` object, nullable
        - `seo` 'must' | 'nice' | 'skip'
        - `analytics` 'must' | 'nice' | 'skip'
        - `design` 'must' | 'nice' | 'skip'
        - `mobile` 'must' | 'nice' | 'skip'
      - `editingModel` 'ai' | 'simple-editor' | 'creator', nullable
      - `sections` string[], nullable
      - `design` object, nullable
        - `palette` string[]
        - `type` string[]
        - `layout` string
      - `rules` object[], nullable
        - `id` string, required
        - `card` 'intent' | 'design' | 'content' | 'seo', required
        - `text` string, required
        - `level` 'block' | 'warn'
        - `locked` boolean
        - `check` union
          - object
            - `type` 'title-max', required
            - `max` integer, required
          - object
            - `type` 'title-suffix', required
            - `suffix` string, required
          - object
            - `type` 'description-length', required
            - `min` integer, required
            - `max` integer, required
          - object
            - `type` 'one-h1', required
          - object
            - `type` 'banned-words', required
            - `words` string[], required
          - object
            - `type` 'image-alt', required
          - object
            - `type` 'allowed-colors', required
            - `colors` string[], required
          - object
            - `type` 'allowed-fonts', required
            - `fonts` string[], required
    - `body` string
    - `goal` string
    - `notes` union
      - string
      - object
        - `design` string, nullable
        - `content` string, nullable
        - `seo` string, nullable
    - `appendHandoff` string
    - `rules` object[]
      - `id` string, required
      - `card` 'intent' | 'design' | 'content' | 'seo', required
      - `text` string, required
      - `level` 'block' | 'warn'
      - `locked` boolean
      - `check` union
        - object
          - `type` 'title-max', required
          - `max` integer, required
        - object
          - `type` 'title-suffix', required
          - `suffix` string, required
        - object
          - `type` 'description-length', required
          - `min` integer, required
          - `max` integer, required
        - object
          - `type` 'one-h1', required
        - object
          - `type` 'banned-words', required
          - `words` string[], required
        - object
          - `type` 'image-alt', required
        - object
          - `type` 'allowed-colors', required
          - `colors` string[], required
        - object
          - `type` 'allowed-fonts', required
          - `fonts` string[], required
    - `removeRules` string[]
  - UpdatePageBriefRequest: Page brief edit (with `?page=`). Send at least one of `head`, `body`, `goal`, `notes`.
    - `head` object
      - `purpose` string, nullable
      - `design` object, nullable
        - `direction` string
        - `hero` string
    - `body` string
    - `goal` string
    - `notes` string

## Response `200`

Saved. The merged brief.

- union: The site brief, or the page brief when the request carried `?page=`.
  - SiteBrief
    - `head` SiteBriefHead, required: The structured top of the site brief. `purpose` is required (5–280 characters).
      - `purpose` string, required
      - `audience` string
      - `kind` 'exploration' | 'production'
      - `priorities` object
        - `seo` 'must' | 'nice' | 'skip'
        - `analytics` 'must' | 'nice' | 'skip'
        - `design` 'must' | 'nice' | 'skip'
        - `mobile` 'must' | 'nice' | 'skip'
      - `editingModel` 'ai' | 'simple-editor' | 'creator'
      - `sections` string[]
      - `design` object
        - `palette` string[]
        - `type` string[]
        - `layout` string
      - `rules` object[]
        - `id` string, required
        - `card` 'intent' | 'design' | 'content' | 'seo', required
        - `text` string, required
        - `level` 'block' | 'warn'
        - `locked` boolean
        - `check` object
          - `type` string, required
      - `updated` string
      - `updatedBy` 'agent' | 'human'
    - `notes` string, nullable, required: The brief's `## Notes` section, or null when it has none.
    - `hash` string: Content hash of the stored brief (changes on every write).
    - `sectionNotes` object: The Design system, Content & CMS and SEO notes that are set (full text). Omitted when none is.
      - `design` string
      - `content` string
      - `seo` string
    - `hasBody` boolean: True when the brief has narrative text beyond the head and notes. Omitted on a site-brief write.
    - `aiAccess` 'viewer' | 'contributor' | 'editor' | 'owner': GET only (also on the 404 `brief_not_set` and 409 `brief_head_corrupt` answers): what AI agents and API keys may do on this site — the effective level, `owner` (Full access) by default.
    - `ruleFailures` object: GET only (also on the 404 and 409 answers): how many Rules failed in each section on the site's last check. Omitted until a check has counted any.
      - `intent` integer, required
      - `design` integer, required
      - `content` integer, required
      - `seo` integer, required
    - `ruleFailuresCheckedAt` integer: GET only, with `ruleFailures`: when that check ran (epoch ms). A Rule added after it has not been checked yet.
  - PageBrief
    - `head` PageBriefHead, required
      - `purpose` string
      - `design` object
        - `direction` string
        - `hero` string
    - `notes` string, nullable, required: The page brief's `## Notes` section, or null when it has none.
    - `hash` string
    - `hasBody` boolean, required

## Other responses

- `400`: The body is not valid (`invalid_body`, with `issues`), `page` is not a usable content key (`invalid_page`), the site has no purpose yet (`purpose_required`), or the head or a section is over its size limit (`brief_too_large`).
- `401`: Missing or invalid credentials.
- `403`: The key or token cannot edit this site (viewer role, read-only key, or a key scoped to other sites), the edit would change or remove a locked rule (`rule_locked`), or it would lock one (`lock_is_for_people`).
- `404`: No such site (`not_found`), or `page` names content that does not exist (`page_not_found`).
- `409`: The brief's stored head cannot be parsed (`brief_head_corrupt`; only a person can overwrite it), or the site is being renamed (`rename_active`).
- `429`: Over the account's write budget (`rate_limited`). Retry after the `Retry-After` seconds.

---

[API](https://skmtc.dev/dotsy/apis/dotsy-api.md) · [All operations](https://skmtc.dev/dotsy/apis/dotsy-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dotsy/apis/dotsy-api/revisions/4272ed3b85d7?raw)
