# API: Releases

Ship, watch and roll back releases.

## Release trains

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `release_trains`.

A release train groups pull requests that ship together as one named release. See [Release Trains](/docs/release-trains).

| Endpoint | What it does |
|----------|--------------|
| `GET /v1/trains` | Lists trains |
| `POST /v1/trains` | Creates a train: `repository`, `name`, and optional `target_branch`, `label` and `milestone` |
| `GET /v1/trains/{id}` | A train with its `members` and, once cut, its `cut` |
| `DELETE /v1/trains/{id}` | Abandons a train. Needs a key tied to a person. |
| `POST /v1/trains/{id}/members` | Adds a pull request: `pull_request`, and `head_sha` when the GitHub App isn't installed |
| `DELETE /v1/trains/{id}/members/{number}` | Removes a pull request |
| `POST /v1/trains/{id}/sync` | Refreshes the train's pull requests |
| `GET /v1/trains/{id}/readiness` | The `verdict` with its `reasons`, each pull request's gate outcome, counts, combined findings and approvals |
| `POST /v1/trains/{id}/approve` | Always `403 HUMAN_APPROVAL_REQUIRED` for a key. A person approves in the dashboard. |
| `POST /v1/trains/{id}/cut` | Always `403 HUMAN_APPROVAL_REQUIRED` for a key. A person cuts in the dashboard. |

## Multi-repo trains

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `multi_repo_trains`. It also needs `release_trains` and `cross_repo_impact`.

One release across repositories, in dependency order. See [Release across repositories](/docs/release-trains#release-across-repositories).

| Endpoint | What it does |
|----------|--------------|
| `GET /v1/multi-trains` | Lists multi-repo trains, each with its `repositories` and how many have `released` |
| `POST /v1/multi-trains` | Starts one: `name`, `repositories` (2 to 20) and an optional `milestone`. Needs a key tied to a person. |
| `GET /v1/multi-trains/{id}` | The train's `state`, `summary`, `needs_order`, `gaps`, `edges` and `steps`, each with its `position`, `state`, `why`, `verdict`, `counts`, `held_by`, `last_error` and `attempts` |
| `PUT /v1/multi-trains/{id}/order` | Always `403 HUMAN_APPROVAL_REQUIRED` for a key. A person sets the order in the dashboard. |
| `POST /v1/multi-trains/{id}/sync` | Refreshes every step. A repository that fails shows `sync_error` on its step. Needs a key tied to a person. |
| `DELETE /v1/multi-trains/{id}` | Abandons the train and every step that hasn't released. Needs an owner's or admin's key. |
| `GET /v1/service-map` | `services`, each with `health` and `last_release`, plus `edges` and `cycles` |

Approve and cut a step through its `train_id` on the routes above, which refuse a key the same way. A step out of turn answers `409 MULTI_TRAIN_HELD`, and a train with no proven order answers `409 MULTI_TRAIN_ORDER_NEEDED`.

## Grouped releases

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `grouped_releases`.

A grouped release is everything merged to the default branch since the last release tag. See [Grouped releases](/docs/release-trains#grouped-releases).

| Endpoint | What it does |
|----------|--------------|
| `POST /v1/releases` | Prepares or refreshes the open draft for `repository`. Needs a key tied to a person. |
| `GET /v1/releases` | Lists releases, newest first. Optional `repository`, `status` and `limit` |
| `GET /v1/releases/{id}` | A release: range, version, `pull_requests`, `commits`, `notes`, `verdict`, `risk`, `status` and each publish `targets` entry. Optional `audience` (default `customer`) picks whose notes `notes` and `audience_notes` hold; `publish_audiences` maps each target to its audience. A draft or approved release without notes for that audience gets them written on this first read |
| `POST /v1/releases/{id}/notes` | Replaces one audience's notes: `{"audience": "customer", "body": "..."}`, or writes them again: `{"audience": "customer", "regenerate": true}`. Returns the `audience_notes` shape. Anyone with `write` can edit a draft; an approved release needs `gate.review` (owners, admins, policy admins and approvers). Published and denied releases answer `409`; correct a published release's notes with `POST /v1/releases/{id}/corrections`. Every change is audited as `release.notes.edit` or `release.notes.regenerate` |
| `POST /v1/releases/{id}/corrections` | Corrects one audience's notes on a published release: `{"audience": "customer", "body": "...", "reason": "..."}`. Updates the stored notes and every target that got them: the changelog entry, the GitHub Release body, and Notion or Confluence pages. Slack messages aren't edited (`not_edited`). Returns `changed`, `before_sha256`, `after_sha256`, `failed` and one `targets` entry per target with `status` `updated`, `failed` or `not_edited`. Send it again to retry a failed target. Owners and admins only (`403` otherwise); `reason` needs at least 10 characters (`400`); a release that isn't published answers `409`. Audited as `release.notes.correct` |
| `POST /v1/changelog/corrections` | Corrects an older changelog entry by version: `{"version": "v1.2.0", "repository": "acme/api", "body": "...", "reason": "..."}`. `repository` defaults to the one the public changelog shows. Same response shape, roles and `reason` rule; `404 CHANGELOG_ENTRY_NOT_FOUND` when there's no such entry. Audited as `changelog.correct` |
| `POST /v1/releases/{id}/approve` | Always `403 HUMAN_APPROVAL_REQUIRED`. A person votes in the dashboard, in Slack or through the browser confirmation below. |
| `POST /v1/releases/{id}/deny` | Always `403 HUMAN_APPROVAL_REQUIRED`, for the same reason |
| `POST /v1/releases/{id}/confirmations` | Starts a browser-confirmed vote: `{"vote": "approve"}` or `deny`. Returns a `device_code`, `user_code` and `verification_url` |
| `GET /v1/release-confirmations/{device_code}` | `202` while pending, `200` with the release once recorded |
| `POST /v1/releases/{id}/publish` | Publishes an approved release, each target with its audience's notes (see `publish_audiences` in `PATCH /v1/settings`). Safe to retry: only failed targets run again. |

## History & time machine

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flags: `release_time_machine`, `regression_predictor`, `history_queries`.

See [Rollback & Time Machine](/docs/rollback).

### GET /v1/when

Flag `release_time_machine`. Finds the first release where something appeared.

| Query | Description |
|-------|-------------|
| `repository` | Required |
| `kind` | A finding kind, such as `secret` |
| `text` | Text to look for |
| `path` | Limit to a path |

Send `kind` or `text`. Symbols, files and dependencies between areas are answered from a checkout by `bumpkit when`.

Returns `query`, `kind`, `found`, `first_release`, `introduced_by`, `later_changes`, `evidence` and `releases_scanned`. Each change has `release`, `sha`, `check_id`, `author`, `agents`, `date` and `summary`.

### POST /v1/predict

Flag `regression_predictor`. Ranks the existing functionality a change can break. `bumpkit predict` builds the request from the blast radius.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `repository` | string | Yes | `owner/name` |
| `changed_files` | string[] | Yes | Files the change touches |
| `changed_symbols` | string[] | No | Functions the change touches |
| `areas` | array | Yes | `[{area, files, symbols, path}]` areas the change reaches |

Returns `{"repository", "areas": [...], "note"}`. Each area has `area`, `score`, `likelihood` and `evidence`.

### GET /v1/history/changes

Flag `history_queries`. What changed in an area.

| Query | Description |
|-------|-------------|
| `repository` | Required |
| `path` | The area |
| `since` | A date (`2026-10-01`), days (`7d`) or a checked release (`v1.4.0`) |

Returns `repository`, `path`, `since`, `releases` (checks), `agents` and `summary`.

### GET /v1/history/regressions

Flag `history_queries`. Whether an area has regressed in production. Query `repository` and `path`. Returns `repository`, `path`, `regressed`, `incidents` (each an incident plus `frames`, `release` and `why`) and `summary`.

## Health & incidents

See [Release Health & Forensics](/docs/release-health).

### POST /v1/deployments

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flags: `forensics` or `release_health`.

Records a deployment.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `repository` | string | Yes | `owner/name`, as your checks record it |
| `sha` | string | Yes | The deployed commit, 7 to 40 hex characters |
| `environment` | string | No | Such as `production` |
| `version` | string | No | A release label such as `v2.3.0`, up to 64 characters |
| `status` | string | No | `success`, `failure` or `in_progress` |
| `deployed_at`, `started_at`, `finished_at` | string | No | RFC 3339 |
| `commits` | array | No | `[{sha, subject, files: [{path, hunks: [{start, end, context}]}]}]` for line-level attribution |

Returns `204`. The body limit is 10 MB.

### GET /v1/releases/{version}/health

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `release_health`.

Compares the error rate after a deploy with before. `{version}` is the deployed version or commit SHA.

| Query | Description |
|-------|-------------|
| `repository` | `owner/name` |
| `environment` | Deployment environment |
| `window` | Minutes to compare, 15 to 1440. Defaults to the workspace setting. |
| `min_events` | Minimum errors before a verdict, 1 to 100000 |

Returns `repository`, `environment`, `version`, `sha`, `deployed_at`, `verdict`, `reason`, `partial`, `min_events`, `before` and `after` (`from`, `to`, `minutes`, `errors`, `per_hour`), `rate_ratio`, `regressions`, `attribution`, `incident_id`, `resolved_version` and `evaluated_at`.

Each regression has `fingerprint`, `title`, `status`, `before`, `after`, `rate_ratio`, `frames` and `suspects` (`[{sha, subject, likelihood, score, evidence}]`).

`404 DEPLOYMENT_NOT_FOUND` if no successful deployment of that version is recorded.

### GET /v1/release-health/settings

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `release_health`.

Needs a key tied to a person. Returns `window_minutes` (default 60), `min_events` (default 20), `posthog_repository`, `posthog_environment`, `last_polled_at` and `last_poll_error`.

### PUT /v1/release-health/settings

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Send `window_minutes` (15 to 1440), `min_events` (1 to 100000), and optionally `posthog_repository` (`owner/name`) and `posthog_environment` (one word, default `production`). Reading errors from PostHog needs the PostHog integration connected. Returns the settings.

### POST /v1/incidents

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `forensics`.

Ranks the releases behind a production failure, each with a likelihood and its evidence.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `repository` | string | Yes | `owner/name` |
| `error` | string | One of | The error with its stack trace |
| `files` | string[] | One of | Files involved in the failure |
| `started_at` | string | No | RFC 3339 |
| `lookback_days` | integer | No | How far back to look |

Returns `repository`, `started_at`, `frames` (`[{file, line}]`) and `suspects` (`[{likelihood, score, evidence, check}]`).

### GET /v1/incidents

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `forensics`.

Needs a key tied to a person. Query `limit`: 1 to 500, default 100. Returns `{"incidents": [...]}` with `id`, `repository`, `title`, `url`, `started_at`, `likelihood` (the top suspect's), `top_check_id`, `resolved_version`, `resolved_sha`, `resolved_at`, `resolved_by_email` and, when known, `likely_release`.

### POST /v1/incidents/{id}/resolve

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `forensics`.

Records the release that fixed an incident. Needs a key tied to a person (the `incident.resolve` permission with roles).

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `version` | string | Yes | Such as `v2.3.1` |
| `sha` | string | No | The fixing commit, 7 to 40 hex characters |

Returns `204`, or `404 NOT_FOUND`.

### GET /v1/incidents/{id}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `intelligent_rollback`.

Returns an incident with its context: `{"incident", "frames", "suspects"}`.

## Rollbacks

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `intelligent_rollback`.

`bumpkit rollback plan` builds a plan locally and stores it here; `bumpkit rollback open` opens it. See [Rollback & Time Machine](/docs/rollback).

### POST /v1/rollbacks

Stores a rollback plan.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `repository` | string | Yes | `owner/name` |
| `base`, `head` | string | Yes | The release the plan reverts |
| `head_sha` | string | Yes | The full commit the plan was made on |
| `revert` | string[] | Yes | 1 to 20 full commit SHAs to revert |
| `incident_id` | string | No | The incident this answers |
| `frames`, `suspects`, `dependents`, `conflicts`, `files` | array | No | The plan's evidence and affected files, up to 300 files |
| `feasible` | boolean | No | Whether the revert applies cleanly |
| `command`, `summary` | string | No | The manual revert command and a summary |

Returns `201` with the plan, including `id` and `status`.

### GET /v1/rollbacks/{id}

Returns a plan.

### POST /v1/rollbacks/{id}/open

Opens the revert pull request through the GitHub App. Owner only.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `branch` | string | Yes | The branch the revert merges into, such as `main` |
| `files` | array | Yes | Exactly the files the revert changes: `[{path, mode, content, deleted}]`, with base64 `content` and mode `100644`, `100755` or `120000` |

Returns `200` with the plan, `pull_request` set. `409 CONFLICT` if it is already open, `409 GITHUB_APP_REQUIRED` if the App isn't installed. The body limit is 8 MB.

## Close the loop

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `close_the_loop`.

Bumpkit opens the rollback or back-port pull request, the gate re-verifies it, and each item records that it is proven done. Bumpkit never merges anything. Opening pull requests needs the Bumpkit GitHub App on the repository for your workspace, or `409 GITHUB_APP_REQUIRED`. At most 30 Bumpkit-opened pull requests per workspace per hour, then `429 RATE_LIMIT_EXCEEDED`. See [Rollback & Time Machine](/docs/rollback).

Each item has `id`, `repository`, `kind` (`rollback`, `backport` or `fix_task`), `title`, `finding_kind`, `fingerprint`, `check_id`, `incident_id`, `branch`, `pull_request`, `status` (`found`, `routed`, `fix_opened`, `verified` or `blocked`), `open`, `routed_to`, `fix_kind`, `fix_url`, `fix_branch`, `fix_number`, `conflicts`, `summary`, `onto`, `commits` (`[{sha, subject, author, evidence}]`), `task`, `verified_by`, `verified_sha`, `verified_at`, `created_by`, `created_at`, `updated_at` and `events` (`[{stage, actor, note, check_id, decision_id, sha, at}]`). A fix task's `task` has `change` (`rename`, `remove_param`, `signature` or `removed`), `symbol`, `name`, `old_signature`, `new_signature`, `new_name`, `removed_param`, `mechanical`, `reason`, `expected_edit` and `places` (`[{file, line, caller, before, after}]`).

### POST /v1/loop/rollbacks

Opens the revert of a release as a pull request. Needs a key created by an owner or admin.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `incident_id` | string | One of | An incident traced to a release |
| `check_id` | string | One of | The release check to revert |
| `release` | string | One of | A release version such as `v1.1.0`, with `repository` |
| `target` | string | One of | Any of the three above |
| `to` | string | One of | Roll the branch back to this release, such as `v1.0.0`, with `repository` |
| `repository` | string | With `release` or `to` | `owner/name` |
| `branch` | string | No | The branch to revert onto. Defaults to the default branch. |
| `dry_run` | boolean | No | Return what the rollback reverts (`releases`, `commits`, `files`) and open nothing |

Returns `201` with the item. One rollback pull request per incident, release or `to` version: when one is already open, `200` with it and `"existing": true`. `409 CONFLICT` when the revert conflicts or there is nothing to revert, `403 FORBIDDEN` for other roles.

With `to`, Bumpkit reverts every non-merge commit in the releases it checked after `to`, up to the newest checked release on `branch`, as one pull request on `bumpkit/rollback/to-<version>`. Commits on the branch after that release that no checked release shipped stay. The item adds `to`, `releases` (`[{name, check_id}]`, newest first) and `files`. A dry run returns `200` with `"dry_run": true`. `404 NOT_FOUND` when `to` is neither a release Bumpkit checked nor a tag, and `409 CONFLICT` when `to` isn't on the branch, nothing was released after it, or the revert is over 100 commits or 300 files; the message gives the `git revert` command to run by hand.

### POST /v1/loop/backports

Cherry-picks a shipped hotfix onto a branch and opens the pull request. Needs a key from a member or a role above it (the `write` permission).

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `repository` | string | Yes | `owner/name` |
| `commit` | string | Yes | The hotfix commit |
| `onto` | string | No | The target branch. Defaults to the default branch. |
| `check_id` | string | No | On a conflict, Bumpkit comments on this check's pull request |

Same statuses as rollbacks. A conflict returns `409 CONFLICT` naming the conflicting files and the hotfix's author, and opens nothing. If the hotfix is already on the branch, the item is marked verified and nothing opens.

### GET /v1/loop

Lists items, open ones only unless `status=all`. Query `repository`, `branch`, `kind`, `check_id`, `status` and `limit` (default 50, maximum 200). Returns `{"items": [Item]}`.

### GET /v1/loop/{id}

Returns one item with its events, or `404 NOT_FOUND`.

## Stacked pull requests

> **Beta, early access.** Flag: `stacked_prs`.

A pull request is stacked when its base branch is another open pull request's head branch. Works with any key. Both routes need the query `repository` as `owner/name`. Use `bumpkit stack list` and `show`, or the `list_stacks` and `get_stack` MCP tools.

### GET /v1/stacks

Returns `{"repository", "stacks": [{repository, root, merge_next, chain, pull_requests}]}`. Each pull request has `number`, `title`, `author`, `branch`, `base`, `head_sha`, `base_sha`, `state`, `parent`, `depth`, `decision`, `outcome`, `check_id`, `gate_id`, `decided_at` and `details_url`.

### GET /v1/stacks/{number}

One pull request's stack. Returns `repository`, `pull_request`, `stack`, and `position` with `number`, `position`, `size`, `parent`, `parent_open`, `merge_first` (the open pull requests to merge before it, in order) and `chain`. `404 NOT_FOUND` when it isn't part of a stack Bumpkit has seen.

## Verification

Evidence the gate can use: how a change behaved, which tests it left behind, and whether it did what the ticket asked. Each route works with any key. The read routes take the query `repository` and `sha`, and answer `404 NOT_FOUND` when nothing is stored for that commit.

### POST /v1/behavior

> **Beta, early access.** Flag: `behavioral_verification`.

Stores a behavioral report from [`bumpkit verify`](/docs/cli-checks#bumpkit-verify), which runs the checks in your CI and signs the report with your key. Send `repository`, `base`, `head`, `base_sha` and `head_sha` (full commit hashes), `runner`, `sandbox`, `checks`, `started_at`, `finished_at` and `signature`. Add `attestation` with a GitHub Actions OIDC `token` to prove the run came from CI.

Returns `201` with `id`, `repository`, `head_sha`, `outcome`, `summary`, `counts`, `verified`, `proof` (`api_key` or `github_oidc`), `attestation` (with the token's `workflow`, `workflow_ref`, `run_id` and `ref`) and `created_at`. A signature that doesn't match answers `400 INVALID_SIGNATURE`, a token that doesn't verify `400 INVALID_ATTESTATION`, and a token used before `409 ATTESTATION_REUSED`. The body limit is 4 MB.

### GET /v1/behavior

> **Beta, early access.** Flag: `behavioral_verification`.

The stored report for a commit: the fields above plus `report`. The `verify_report` MCP tool reads it.

### POST /v1/tests/evidence

> **Beta, early access.** Flag: `regression_evidence`.

Stores test results and the tests expected for a commit. [`bumpkit test attest`](/docs/cli-agents#bumpkit-test) sends this for you. Send `repository`, `base_sha` and `head_sha` (full commit hashes), `expected`, `content` (JUnit XML or `go test -json` output), and optionally `format` (`junit` or `go-test-json`). Add `attestation` with a GitHub Actions OIDC `token`; without it the upload is stored and doesn't count as evidence.

`expected` has `base`, `head`, `tests` (up to 5,000, each with `name`, `file`, `line`, `because`, `symbol`, `path` and `ambiguous`), `changed` (up to 20,000 paths), `code_files`, `undetermined` (up to 500, each with `path` and `reason`) and `truncated`.

Returns `201` with `id`, `proof` (`api_key` or `github_oidc`), `counts`, `results`, `parse_error`, `evidence` and `created_at`. A results file that can't be read is stored and returned in `parse_error`, so the gate can say so. A token that doesn't verify answers `400 INVALID_ATTESTATION`, and a token used before `409 ATTESTATION_REUSED`. The body limit is 32 MB.

### GET /v1/tests/evidence

> **Beta, early access.** Flag: `regression_evidence`.

The regression evidence for a commit. Takes `repository`, `sha` (a full commit hash) and optionally `pull_request`, which counts merge-commit attestations only from that pull request. Unlike the other read routes, it answers `200` with `state: "missing"` when nothing is stored. Returns `repository`, `head_sha`, `state`, `summary`, `next_step`, `expected`, `passed`, `failed`, `not_run`, `tests` (each with `name`, `file`, `line`, `verdict`, `because`, `symbol`, `path` and `note`), `omitted`, `undetermined`, `proof`, `attestation`, `uploads` and `reported_at`. `tests` lists up to 500, failures first. See [the states](/docs/release-checks#regression-evidence). The `test_evidence` MCP tool reads it.

### POST /v1/tests/observations

> **Beta, early access.** Flag: `test_evolution`.

Records which tests each release ran, so Bumpkit can flag the ones that stopped mattering. Send `repository`, `head` (a full commit hash), `tests` (up to 5,000, each with `name`, `file`, `touched` and `failed`) and optionally `releases` (2 to 100, default 10). Returns `{"recorded", "releases", "quiet": [{name, file, releases}]}`. `quiet` lists tests that neither failed nor were touched over at least that many releases. [`bumpkit tests`](/docs/cli-checks#bumpkit-tests) sends this for you.

### POST /v1/tests/suggest

> **Beta, early access.** Flag: `test_evolution`.

Fills in a test skeleton for a changed function. Send `language`, `symbol`, `file`, `source` and `template`. `language` is `go`, `js`, `python`, `ruby`, `php`, `java`, `kotlin`, `scala`, `csharp`, `dart`, `elixir`, `rust`, `c`, `cpp` or `swift`, and `template` must be valid for it. Returns `{"code", "generator"}`. `generator` is `template` when Bumpkit returns your skeleton unchanged. `bumpkit tests` calls it.

### POST /v1/tests/report

> **Beta, early access.** Flag: `test_evolution`.

Stores a test evolution report. Send `repository`, `head` (a full commit hash), `coverage` (boolean), `untested` and `obsolete`, up to 500 each. An `untested` entry has `symbol`, `kind`, `lang`, `file`, `line`, `end`, `reason`, `branches` and `evidence`. An `obsolete` entry has `test`, `file`, `line` and `reason`. Returns `201` with the report and `created_at`. `bumpkit tests` uploads it.

### GET /v1/tests/report

> **Beta, early access.** Flag: `test_evolution`.

The stored report for a commit. The `untested_behavior` MCP tool reads it.

### POST /v1/intent

> **Beta, early access.** Flag: `intent_check`.

Maps a ticket's acceptance criteria to the evidence in a change. Send the fields of [`POST /v1/checks`](/docs/api-checks), plus `specs` (up to 20, each with `source`, `ref` and `text`, at most 64 KB) and `llm` (boolean) to extract criteria from free-form text. Returns `extractor`, `criteria`, `covered` and `not_evidenced`. Each criterion has `id`, `text`, `source`, `status`, `missing` and `evidence`. With `repository` and a full `head` hash, the report is stored for that commit. [`bumpkit criteria`](/docs/cli-checks#bumpkit-criteria) builds the request from a checkout.

### GET /v1/intent

> **Beta, early access.** Flag: `intent_check`.

The stored report for a commit. The `intent_coverage` MCP tool reads it.

## Playground

> **Beta, early access.** Flag: `try_release_check`.

Previews a release check on a sample, without saving it or posting anywhere. A run counts toward your per-minute limit. Needs a key tied to a person. Use [`bumpkit playground`](/docs/cli-checks#playground-previews), or the `playground_examples` and `playground_run` MCP tools.

### GET /v1/playground/examples

Returns `examples` (each with `id`, `title`, `summary`, `author`, `message` and `previous`), `default_policy`, `max_diff`, `max_policy`, `max_message` and `max_version`.

### POST /v1/playground/run

Read-only roles get `403 FORBIDDEN`. Send `example`, or `diff` and `message`, not both. Optional: `policy` (a `bumpkit.yml`, default `default_policy`) and `previous` (the previous version). Limited per workspace: `429 RATE_LIMIT_EXCEEDED` with `Retry-After` when you run them too fast.

Returns `gate`, `comment`, `radar`, `blast`, `diff`, `example`, and `preview: true`, `saved: false` and `posted: false`.

## Notes & docs

See [Release Notes & Digests](/docs/release-notes) and [Changelog Pages](/docs/changelog-pages).

### GET /v1/checks/{id}/docs

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `release_docs`.

Release documentation for a checked release. Add `regenerate=true` to build it again. Returns `check_id`, `version`, `engineering`, `customer`, `migration`, `created_at` and `publications` (`[{target, url, by, at}]`).

### POST /v1/checks/{id}/publish

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `release_docs`.

Publishes the release docs. Send `targets`: any of `changelog`, `github_release`, `slack` and `discord`, plus `notion` and `confluence` with the `notion_confluence` flag (without it they are refused with `400 VALIDATION_ERROR`). Returns the docs with their publications. `400` if the release has no version, `502 PUBLISH_FAILED` if a target failed. Needs a key created by an owner, admin, policy admin, approver or member; other roles and keys not tied to a person get `403 ROLE_REQUIRED`, and the refusal is in the audit log as `release.publish.denied`.

### POST /v1/checks/{id}/notes

> **Beta, early access.** Requires the `audience_notes` flag. Use `bumpkit login` or an API key created by an active member with write access to this workspace. Ownerless keys and read-only roles cannot generate drafts.

Generate and save release notes from an existing check in the authenticated workspace. Existing drafts are reused unless regeneration is explicitly requested. This action does not publish notes or send messages.

| JSON field | Description |
|------------|-------------|
| `audience` | `customer` (default), `engineering`, `support`, `security`, `leadership`, or a custom audience |
| `segment` | Optional defined customer segment |
| `regenerate` | Optional boolean, default `false`. Replace the saved draft with a newly generated draft |

Returns `check_id`, `version`, `audience`, `audience_name`, `segment`, `body` (Markdown) and `created_at`. A check belonging to another workspace is not accessible. If regeneration fails for an existing draft, the request returns an error and leaves the saved draft unchanged.

### GET /v1/checks/{id}/notes

> **Beta, early access.** Requires the `audience_notes` flag and an API key tied to an active member of the authenticated workspace.

Use `saved=true` to read an existing draft without generating or replacing it. Active members with read-only access can use this mode. Returns `404` when no saved draft exists for the check, audience and segment.

| Query | Description |
|-------|-------------|
| `saved` | Set to `true` for a read-only request that never generates notes |
| `audience` | `customer` (default), `engineering`, `support`, `security`, `leadership`, or a custom audience |
| `segment` | Optional defined customer segment |
| `regenerate` | Legacy generation mode only. Cannot be combined with `saved=true` |

The response has the same fields as the POST endpoint. For compatibility, a request without `saved=true` can generate and persist a draft, or regenerate it with `regenerate=true`. That legacy mode requires write access. Prefer POST for generation and `GET ...?saved=true` for viewing or downloading a saved draft.

### GET /v1/checks/{id}/consumer-guide

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `consumer_migration_guide`.

A migration guide for people who consume the release. Returns `check_id`, `version`, `breaking`, `steps` (`[{title, change, before, after, language, source, url}]`) and `markdown`. Steps are filled only for breaking releases.

### GET /v1/release-notes/config

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `audience_notes`.

Needs a key tied to a person. Returns `audiences` (`[{key, name, tone, sections, exclude, hide_paths, custom}]`), `segments` (`[{key, name, plan, features}]`) and `features` (`[{feature, paths, keywords}]`).

### PUT /v1/release-notes/audiences/{key}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `audience_notes`.

Creates or replaces an audience. Owner only. `{key}` is lowercase, starts with a letter, up to 32 characters; a built-in key overrides the built-in.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Up to 80 characters |
| `sections` | string[] | Yes | Any of `breaking`, `new`, `improved`, `fixed`, `security`, `database`, `config`, `dependencies`, `risk`, `internal` |
| `tone` | string | No | Up to 500 characters |
| `exclude` | string[] | No | Path globs to leave out, up to 50 |
| `hide_paths` | boolean | No | Leave file paths out of the notes |

Returns `200` with the full config.

### DELETE /v1/release-notes/audiences/{key}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Removes a custom audience or override. Returns `200` with the config, or `404 NOT_FOUND`.

### PUT /v1/release-notes/segments/{key}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Send `name` (required, up to 80 characters), `plan` (up to 64) and `features` (up to 50 feature names). Returns `200` with the config.

### DELETE /v1/release-notes/segments/{key}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Returns `200` with the config, or `404 NOT_FOUND`.

### PUT /v1/release-notes/features/{name}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Tags changes with a feature name, for segments. Send `paths` (globs) and/or `keywords`, up to 50 each. Returns `200` with the config.

### DELETE /v1/release-notes/features/{name}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Returns `200` with the config, or `404 NOT_FOUND`.

### GET /v1/digests

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `weekly_digest`.

Needs a key tied to a person. Returns `{"digests": [...]}`. Each has `id`, `repository`, `timezone`, `weekday`, `hour`, `minute`, `targets`, `emails`, `enabled`, `next_run_at` and `last_sent_at`.

### PUT /v1/digests

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `weekly_digest`.

Creates or replaces the weekly digest for a repository. Owner only.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `repository` | string | Yes | `owner/name` |
| `timezone` | string | No | IANA name, default `UTC` |
| `weekday` | integer | Yes | 0 (Sunday) to 6 |
| `hour`, `minute` | integer | Yes | 0 to 23, 0 to 59 |
| `targets` | string[] | Yes | Any of `slack`, `discord`, `email` |
| `emails` | string[] | With `email` | Up to 20 addresses |
| `enabled` | boolean | No | Send on schedule |

Returns `200` with the digest, `next_run_at` computed.

### DELETE /v1/digests/{id}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Returns `204`, or `404 NOT_FOUND`.

### GET /v1/digests/{id}/preview

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

The digest as it would go out now, covering the time since it was last sent (or 7 days). Returns `repository`, `since`, `until`, `releases`, `risky`, `agents`, `blocked`, `gate_failed`, `needs_approval`, `overridden` and `markdown`.

### POST /v1/digests/{id}/send

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Sends the digest now. Returns `200` with the digest. `502 DIGEST_FAILED` if a target failed.

### GET /v1/doc-connections

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `notion_confluence`.

Needs a key tied to a person. Returns `{"connections": [{provider, auth, base_url, email, parent, parent_page, created_by, created_at}]}`. Tokens are never returned.

### PUT /v1/doc-connections/{provider}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `notion_confluence`.

Connects Notion or Confluence. Owner only. `{provider}` is `notion` or `confluence`.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `token` | string | Yes | API token. Checked live, stored encrypted. |
| `parent` | string | Yes | Notion: the parent page link or ID. Confluence: the space key. |
| `base_url` | string | Confluence | Your Confluence https URL |
| `email` | string | Confluence | The account the token belongs to |
| `parent_page` | string | No | Confluence parent page ID |

Returns `200` with the connections.

### DELETE /v1/doc-connections/{provider}

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Owner only. Returns `204`, or `404 NOT_FOUND`.
