Files
coder/docs/reference/api/notifications.md
T
Nick Vigilante 0a7bb80a1e feat: make CLI/API doc generators emit front-matter metadata (Phase 2) (#27246)
## Summary

Phase 2 of the H1 → front-matter migration (`DOCS-483`; parent
`DOCS-477`). Makes the two reference-doc generators emit per-page
metadata as YAML front matter instead of a leading `# H1`, so generated
pages are self-describing and `make gen` stops reverting migrated pages
(Phase 3).

Phase 1 (`DOCS-482`) made the coder.com renderers prefer a front-matter
`title` (manifest fallback).

> [!NOTE]
> Rebased onto `main` and fully regenerated, and updated across two
rounds of Coder Agents Review — see **Review follow-ups** below.

## Changes

- **`scripts/clidocgen/command.tpl` + `gen.go` + `main.go`** — front
matter now carries `title` (from `fullName`) and `description` (from the
command's `Short`), and the leading `# H1` is dropped. The CLI index
page's front matter is taken from the manifest `Command Line` route
(title/description/icon_path).
- **`scripts/apidocgen/postprocess/main.go`** — reads the manifest and,
at write time, injects front matter carrying each section's `title` plus
any curated `description`, `state`, and `icon_path`. The API index
page's front matter is taken from the manifest `REST API` route.
- **`scripts/docgenenv`** (new shared code) — one `YAMLScalar`
front-matter escaper, one `Route`/`Manifest` schema +
`LoadManifest`/`FindRoute`, and one `FrontMatter(Route)` emitter, all
imported by both generators (no duplicated helpers, types, or emitters).
- Regenerated all **166 CLI + 31 API** reference pages.

### Metadata → front matter, and what stays in the manifest

Every *per-page* manifest field is mirrored into the page's front
matter: `title`, `description`, `state`, `icon_path`. The **structural**
fields stay in `manifest.json`:

- `children` — the nav tree (explicitly out of scope).
- `path` — the manifest's pointer to the file; a page carrying its own
path is redundant/error-prone, so it's treated like `children`.

The fields are **duplicated** into front matter and **`manifest.json` is
left unchanged**, so this is a **no-op for rendering today** (coder.com
strips front matter for `llms`, and Algolia + the renderer read only
`title`). Removing the fields from the manifest is the natural
follow-up, gated on the renderer reading them from front matter first.

### Why the API side changes the postprocessor, not the `.dot` templates

The issue text suggested editing
`scripts/apidocgen/markdown-template/*`. I deliberately did **not**,
because the postprocessor derives each page's **filename, section title,
and manifest route** from the leading `# {name}` line
(`extractSectionName`). Emitting front matter from the template would
break that extraction. Instead the widdershins templates still emit `#
{name}`, the postprocessor reads it (and now verifies it), and then
swaps the heading for a front-matter block as each section is written.

## Review follow-ups (Coder Agents Review)

### Round 1 — addressed in `e53d5e03` (all threads resolved)

- **CRF-1 / CRF-4** — de-duplicated the escaper and the
`route`/`manifest` schema + traversal into `scripts/docgenenv` (shared
by both generators).
- **CRF-2** — `YAMLScalar` now quotes YAML-reserved scalars
(`true/false/null/…`, numbers); no current value is affected.
- **CRF-3** — added unit tests: a `YAMLScalar` round-trip, `FindRoute`,
and `prependFrontMatter`.
- **CRF-5** — the CLI and API **index** pages now mirror their manifest
route's title/description/icon_path instead of a hardcoded
`coder`/`API`, fixing a rendered-heading regression (`REST API`/`Command
Line` were being overwritten).
- **CRF-6** — dropped the dead `#login` anchor in
`docs/support/support-bundle.md` (the migrated `login.md` no longer
mints that heading anchor).
- **CRF-7 / CRF-8 / CRF-11** — renamed to `prependFrontMatter`, switched
to `bytes.Cut`, and it now strips the first line only when it is the `#
{name}` heading (`extractSectionName` errors otherwise).
- **CRF-9** — removed the orphan `docs/reference/api/chat.md` (not in
the manifest, not linked; the real page is `chats.md`).
- **CRF-10** — the metadata read and the manifest rewrite now share one
`FindRoute` traversal.
- **CRF-13** — moot under squash-merge; this branch is a single
scopeless commit.
- **CRF-15** — the pre-existing `sort.Slice`/`slices.IsSorted`
comparator is left as-is per the review (out of scope; safe today
because section names are unique).

### Round 2 — addressed in `ee796e7107` (all threads resolved)

- **CRF-16** (P1) — removed three em-dashes from new doc comments (the
only `make lint` failure on the prior head); the emdash gate is green.
- **CRF-17 / CRF-18** — unified front-matter emission into one shared
`docgenenv.FrontMatter(Route)`, used by the API postprocessor directly
and by `command.tpl` via a `frontMatter` template func. This retires the
hand-written template YAML and the
`indexTitle`/`indexDescription`/`indexIconPath` closures, so a new
front-matter field is wired in one place, and it gives the CLI index the
`state` arm it previously lacked. Verified byte-identical: a full CLI +
API regen produces zero page changes.
- **CRF-19** — CLI child sort switched to `slices.SortFunc` +
`cmp.Compare` (typed comparator).
- **CRF-20** — reworded the `prependFrontMatter` comment:
`extractSectionName`'s fail-fast is the load-bearing guard; the prefix
check is a defensive backstop.
- **CRF-21** — added `icon_path`/`state` coverage in `docgenenv`'s
`TestFrontMatter/AllFields` (the branch the index page relies on,
previously at 0%).
- **CRF-22** — `YAMLScalar` no longer emits a trailing-space value as a
bare scalar (YAML strips it on read, so it would not round-trip); added
test coverage.
- **CRF-24** — the shared emitter removed the duplicated `cliIndexRoute`
doc comment; the rationale now lives in one place.
- **CRF-23** (Phase 3, out of scope here) — noted: the API generator
wipes and regenerates `reference/api/` from the manifest, so removing
curated metadata from the manifest in Phase 3 needs another source first
(a generator that preserves existing front matter, or metadata carried
alongside the swagger annotations).
- **Process (Mafu-san)** — the verification set below now leads with
`make lint`, the mandatory CI gate that the earlier list omitted.

## Cross-repo dependency

**Resolved — this PR no longer has a hard merge-ordering gate** (CRF-14
was right; the earlier "must merge after #968" note was stale).

The coder.com surfaces that would otherwise leak raw front matter from
`coder/coder` `main` are already front-matter-aware on merged PRs:

- **coder.com#964** (`DOCS-554`, llms-full.txt corpus + Algolia) —
**merged**.
- **coder.com#974** (`DOCS-574`, the `.md` proxy twin + `llms.txt` index
titles) — **merged**.

coder.com#968 (`DOCS-577`) was re-scoped to only the renderer
route-metadata generalization; it's a no-op on today's corpus and its
own description confirms the "deploy before the generators" constraint
no longer applies (that was driven by the llms corpus, now in #964).
Worth a final confirmation that #964/#974 are **deployed** before merge,
but there's no branch/PR ordering blocker left.

## Verification & evidence

AI was the primary author of this PR (see disclosure below); per the [AI
Contribution
Guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
here is manual verification.

- `make lint` (golangci-lint + the emdash gate) passes; `go build` / `go
vet` / `go test` are clean for the generators + `scripts/docgenenv`;
`pnpm check-docs` passes.
- `swagger.json`, `docs.go`, and `manifest.json` are **unchanged** —
metadata is duplicated into front matter; command/section names and
routes did not move.
- The diff is purely additive front matter
(`title`/`description`/`state`/`icon_path`) + the leading H1 removal; no
body reflow. A full CLI + API regen produces **zero** page changes
beyond the two index pages.

<details>
<summary>Terminal evidence</summary>

CLI `description` from the command's `Short` (`YAMLScalar` quotes when
needed, e.g. a `Short` with a colon):

```md
---
title: server
description: Start a Coder server
---
```

API pages inherit curated manifest metadata (only Agents/Chats have any
today):

```md
---
title: Chats
description: "REST endpoints for Coder Agents Chats API (programmatic agent sessions)."
state:
  - early access
---
```

Diff scope + "no body changes" proof (uses an explicit `base..HEAD`
range, so it actually tests the claim):

```
$ git diff --shortstat origin/main
 210 files changed, 1447 insertions(+), 344 deletions(-)
# = 166 CLI + 31 API reference pages + generators + scripts/docgenenv
# swagger.json / docs.go / manifest.json: NOT modified

# Every removed line under docs/reference is a leading "# H1"; nothing else:
$ git diff origin/main..HEAD -- docs/reference/ | grep '^-' | grep -v '^---' | grep -v '^-# '
(empty)

$ pnpm check-docs
Summary: 0 error(s)
```

</details>

Linear: DOCS-483

> This PR was created with AI assistance (Coder Agents).
2026-08-11 15:22:55 +00:00

23 KiB
Generated

title
title
Notifications

Send a custom notification

Code samples

# Example request using curl
curl -X POST http://coder-server:8080/api/v2/notifications/custom \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

POST /api/v2/notifications/custom

Body parameter

{
  "content": {
    "message": "string",
    "title": "string"
  }
}

Parameters

Name In Type Required Description
body body codersdk.CustomNotificationRequest true Provide a non-empty title or message

Example responses

400 Response

{
  "detail": "string",
  "message": "string",
  "validations": [
    {
      "detail": "string",
      "field": "string"
    }
  ]
}

Responses

Status Meaning Description Schema
204 No Content No Content
400 Bad Request Invalid request body codersdk.Response
403 Forbidden System users cannot send custom notifications codersdk.Response
500 Internal Server Error Failed to send custom notification codersdk.Response

To perform this operation, you must be authenticated. Learn more.

Get notification dispatch methods

Code samples

# Example request using curl
curl -X GET http://coder-server:8080/api/v2/notifications/dispatch-methods \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

GET /api/v2/notifications/dispatch-methods

Example responses

200 Response

[
  {
    "available": [
      "string"
    ],
    "default": "string"
  }
]

Responses

Status Meaning Description Schema
200 OK OK array of codersdk.NotificationMethodsResponse

Response Schema

Status Code 200

Name Type Required Restrictions Description
[array item] array false
» available array false
» default string false

To perform this operation, you must be authenticated. Learn more.

List inbox notifications

Code samples

# Example request using curl
curl -X GET http://coder-server:8080/api/v2/notifications/inbox \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

GET /api/v2/notifications/inbox

Parameters

Name In Type Required Description
targets query string false Comma-separated list of target IDs to filter notifications
templates query string false Comma-separated list of template IDs to filter notifications
read_status query string false Filter notifications by read status. Possible values: read, unread, all
starting_before query string(uuid) false ID of the last notification from the current page. Notifications returned will be older than the associated one

Example responses

200 Response

{
  "notifications": [
    {
      "actions": [
        {
          "label": "string",
          "url": "string"
        }
      ],
      "content": "string",
      "created_at": "2019-08-24T14:15:22Z",
      "icon": "string",
      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
      "read_at": "string",
      "targets": [
        "497f6eca-6276-4993-bfeb-53cbbbba6f08"
      ],
      "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc",
      "title": "string",
      "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
    }
  ],
  "unread_count": 0
}

Responses

Status Meaning Description Schema
200 OK OK codersdk.ListInboxNotificationsResponse

To perform this operation, you must be authenticated. Learn more.

Mark all unread notifications as read

Code samples

# Example request using curl
curl -X PUT http://coder-server:8080/api/v2/notifications/inbox/mark-all-as-read \
  -H 'Coder-Session-Token: API_KEY'

PUT /api/v2/notifications/inbox/mark-all-as-read

Responses

Status Meaning Description Schema
204 No Content No Content

To perform this operation, you must be authenticated. Learn more.

Watch for new inbox notifications

Code samples

# Example request using curl
curl -X GET http://coder-server:8080/api/v2/notifications/inbox/watch \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

GET /api/v2/notifications/inbox/watch

Parameters

Name In Type Required Description
targets query string false Comma-separated list of target IDs to filter notifications
templates query string false Comma-separated list of template IDs to filter notifications
read_status query string false Filter notifications by read status. Possible values: read, unread, all
format query string false Define the output format for notifications title and body.

Enumerated Values

Parameter Value(s)
format markdown, plaintext

Example responses

200 Response

{
  "notification": {
    "actions": [
      {
        "label": "string",
        "url": "string"
      }
    ],
    "content": "string",
    "created_at": "2019-08-24T14:15:22Z",
    "icon": "string",
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "read_at": "string",
    "targets": [
      "497f6eca-6276-4993-bfeb-53cbbbba6f08"
    ],
    "template_id": "c6d67e98-83ea-49f0-8812-e4abae2b68bc",
    "title": "string",
    "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
  },
  "unread_count": 0
}

Responses

Status Meaning Description Schema
200 OK OK codersdk.GetInboxNotificationResponse

To perform this operation, you must be authenticated. Learn more.

Update read status of a notification

Code samples

# Example request using curl
curl -X PUT http://coder-server:8080/api/v2/notifications/inbox/{id}/read-status \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

PUT /api/v2/notifications/inbox/{id}/read-status

Parameters

Name In Type Required Description
id path string true id of the notification

Example responses

200 Response

{
  "detail": "string",
  "message": "string",
  "validations": [
    {
      "detail": "string",
      "field": "string"
    }
  ]
}

Responses

Status Meaning Description Schema
200 OK OK codersdk.Response

To perform this operation, you must be authenticated. Learn more.

Get notifications settings

Code samples

# Example request using curl
curl -X GET http://coder-server:8080/api/v2/notifications/settings \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

GET /api/v2/notifications/settings

Example responses

200 Response

{
  "notifier_paused": true
}

Responses

Status Meaning Description Schema
200 OK OK codersdk.NotificationsSettings

To perform this operation, you must be authenticated. Learn more.

Update notifications settings

Code samples

# Example request using curl
curl -X PUT http://coder-server:8080/api/v2/notifications/settings \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

PUT /api/v2/notifications/settings

Body parameter

{
  "notifier_paused": true
}

Parameters

Name In Type Required Description
body body codersdk.NotificationsSettings true Notifications settings request

Example responses

200 Response

{
  "notifier_paused": true
}

Responses

Status Meaning Description Schema
200 OK OK codersdk.NotificationsSettings
304 Not Modified Not Modified

To perform this operation, you must be authenticated. Learn more.

Get custom notification templates

Code samples

# Example request using curl
curl -X GET http://coder-server:8080/api/v2/notifications/templates/custom \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

GET /api/v2/notifications/templates/custom

Example responses

200 Response

[
  {
    "actions": "string",
    "body_template": "string",
    "enabled_by_default": true,
    "group": "string",
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "kind": "string",
    "method": "string",
    "name": "string",
    "title_template": "string"
  }
]

Responses

Status Meaning Description Schema
200 OK OK array of codersdk.NotificationTemplate
500 Internal Server Error Failed to retrieve 'custom' notifications template codersdk.Response

Response Schema

Status Code 200

Name Type Required Restrictions Description
[array item] array false
» actions string false
» body_template string false
» enabled_by_default boolean false
» group string false
» id string(uuid) false
» kind string false
» method string false
» name string false
» title_template string false

To perform this operation, you must be authenticated. Learn more.

Get system notification templates

Code samples

# Example request using curl
curl -X GET http://coder-server:8080/api/v2/notifications/templates/system \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

GET /api/v2/notifications/templates/system

Example responses

200 Response

[
  {
    "actions": "string",
    "body_template": "string",
    "enabled_by_default": true,
    "group": "string",
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "kind": "string",
    "method": "string",
    "name": "string",
    "title_template": "string"
  }
]

Responses

Status Meaning Description Schema
200 OK OK array of codersdk.NotificationTemplate
500 Internal Server Error Failed to retrieve 'system' notifications template codersdk.Response

Response Schema

Status Code 200

Name Type Required Restrictions Description
[array item] array false
» actions string false
» body_template string false
» enabled_by_default boolean false
» group string false
» id string(uuid) false
» kind string false
» method string false
» name string false
» title_template string false

To perform this operation, you must be authenticated. Learn more.

Send a test notification

Code samples

# Example request using curl
curl -X POST http://coder-server:8080/api/v2/notifications/test \
  -H 'Coder-Session-Token: API_KEY'

POST /api/v2/notifications/test

Responses

Status Meaning Description Schema
200 OK OK

To perform this operation, you must be authenticated. Learn more.

Get user notification preferences

Code samples

# Example request using curl
curl -X GET http://coder-server:8080/api/v2/users/{user}/notifications/preferences \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

GET /api/v2/users/{user}/notifications/preferences

Parameters

Name In Type Required Description
user path string true User ID, name, or me

Example responses

200 Response

[
  {
    "disabled": true,
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "updated_at": "2019-08-24T14:15:22Z"
  }
]

Responses

Status Meaning Description Schema
200 OK OK array of codersdk.NotificationPreference

Response Schema

Status Code 200

Name Type Required Restrictions Description
[array item] array false
» disabled boolean false
» id string(uuid) false
» updated_at string(date-time) false

To perform this operation, you must be authenticated. Learn more.

Update user notification preferences

Code samples

# Example request using curl
curl -X PUT http://coder-server:8080/api/v2/users/{user}/notifications/preferences \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Coder-Session-Token: API_KEY'

PUT /api/v2/users/{user}/notifications/preferences

Body parameter

{
  "template_disabled_map": {
    "property1": true,
    "property2": true
  }
}

Parameters

Name In Type Required Description
user path string true User ID, name, or me
body body codersdk.UpdateUserNotificationPreferences true Preferences

Example responses

200 Response

[
  {
    "disabled": true,
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "updated_at": "2019-08-24T14:15:22Z"
  }
]

Responses

Status Meaning Description Schema
200 OK OK array of codersdk.NotificationPreference

Response Schema

Status Code 200

Name Type Required Restrictions Description
[array item] array false
» disabled boolean false
» id string(uuid) false
» updated_at string(date-time) false

To perform this operation, you must be authenticated. Learn more.