API
API: Gate
Ask the release gate, read saved decisions, and manage org policies and freezes over the API.
Decide what ships and set the policy behind it.
Saved gate decisions
GET /v1/decisions/{id} accepts a saved release check UUID or gate decision UUID. With a check ID, it returns the latest recorded decision for that check; with a decision ID, it returns that exact record. Both the check and decision must belong to the authenticated key's workspace. Read-only workspace access is sufficient; no approval or write permission is granted by this endpoint.
The response contains recorded, check (the persisted check and retained findings), and decision when present. The decision includes id, outcome, reasons (including recorded CI policy reasons), authorship, retained policy, pull_request, approvals, approvals_source, server_fetched, created_at and org_policy_version. server_fetched is true when Bumpkit read the diff from your repository and false when your CI sent it; only true decisions count for deployments, votes, trains, the approvals inbox and agent track records. A legacy check without a gate decision returns recorded: false and a message; no result is inferred from its check verdict. Unknown and foreign workspace IDs return 404 DECISION_NOT_FOUND.
It reads the saved decision and never re-runs the gate. A null policy text means that text wasn't kept.
POST /v1/decisions/{check_id}/approve and POST /v1/decisions/{check_id}/deny don't record votes. A vote is a person's call, so a key-authenticated request gets 403 HUMAN_APPROVAL_REQUIRED. To vote from a terminal, POST /v1/decisions/{check_id}/confirmations with {"vote": "approve"} or {"vote": "deny"}. It needs workspace write permission and answers with device_code, user_code, verification_url, expires_in and interval. A person opens verification_url signed in to the dashboard and confirms; the vote is theirs. Poll GET /v1/decisions/confirmations/{device_code} with the same key: 202 while pending, then 200 with result (check_id, exact head_sha, vote and the latest gate outcome), 409 VOTE_REFUSED when the confirmed vote was refused, 409 CONFIRMATION_DECLINED when it was canceled, or 410 CONFIRMATION_EXPIRED.
Confirmed votes follow the dashboard's rules: the voter needs a linked GitHub account that can review, the vote must be on the latest commit, and neither the pull request's author nor a coding agent's account can vote. The gate updates its decision after each vote. A second vote from the same person on the same commit replaces the first. Checks outside your workspace return 404.
Pending approvals
The release gate decisions waiting for a person's vote, as bumpkit approvals list shows them. Needs a key tied to a person. Reading never votes.
| Method | Route | Result |
|---|---|---|
| GET | /v1/approvals |
workspace_id, workspace_name and requests, up to 100: each has id, check_id, repository, pull_request, head_sha, summary, reasons, votes ([{login, decision, source, at}]), link to the check in the dashboard and created_at |
| GET | /v1/approvals/{id} |
One request. {id} is the decision or check ID, or a prefix of at least 8 characters. 404 APPROVAL_NOT_FOUND when nothing pending matches |
Gate
The gate checks a release, applies the repository's bumpkit.yml policy and records a decision. See Release Gate and bumpkit.yml Reference.
POST /v1/gate
Takes every POST /v1/checks field, plus:
| Field | Type | Required | Description |
|---|---|---|---|
policy |
string | No | The repository's bumpkit.yml text. Empty means the default policy. When the GitHub App is installed on the repository for your workspace, the policy in your repository applies instead: see Where the policy comes from. |
approvals |
array | No | [{login, source}] approvals that count. When the GitHub App is installed for the repository, Bumpkit ignores these: with pull_request it reads the pull request's reviews instead, and without one no approvals count. |
pull_request |
integer | No | The pull request being gated |
hotfix |
boolean | No | Gate as a hotfix. See Hotfixes & Break Glass. |
findings |
array | No | Findings computed on your side, such as blast radius (flag blast_radius) and missing backports. Bumpkit keeps only the kinds it accepts. |
comment |
boolean | No | Post or update the decision as a pull request comment through the GitHub App. It's marked as sent by a client and is separate from the App's own comment. |
Fields added by features in beta:
| Field | Type | Flag | Description |
|---|---|---|---|
title, branch |
string | jira, servicenow |
Used for change tickets and evidence |
specs |
array | intent_check |
[{source, ref, text}] spec documents to check intent against, at most 20, 64 KB each |
agent_session |
string | change_provenance |
The agent session that made the change. The gate outcome is recorded on the session only when the key that started it, or another key of the same person, runs the gate |
quality |
object | architecture_drift, slop_tracker, performance_diff |
A quality snapshot from the CLI. See Code Quality & Architecture. |
findings also accepts quality kinds (architecture_drift, code_quality, performance, 50 per kind) when their flags are on, and hotspot (flag behavior_checks).
Returns 200 with a decision:
| Field | Description |
|---|---|
id |
Decision ID |
outcome |
pass, needs_approval or fail |
reasons[] |
rule, message, outcome, approvers |
authorship |
writer (human or an agent), tier (verified, declared, inferred or human), agents and committers |
pull_request |
As sent |
check |
The full check |
created_at |
When the decision was made |
commented |
true when the comment was posted |
replayed |
true on an idempotent replay (Beta) |
org_policy_version |
Org policy version applied (Beta, flag org_policies) |
stale_approvals |
Approvals that no longer count on GitLab, Bitbucket or Azure DevOps (Beta) |
intent |
The intent report, when specs were sent (Beta, flag intent_check) |
url |
The release page for this decision on the dashboard |
radar_url |
A link to the blast radius radar on the release page, when the change reaches a product area (Beta, flag blast_radar) |
confidence |
The confidence score (Beta, flag confidence_score) |
policy_source |
Where the policy came from: client, or base_branch:<ref> such as base_branch:main |
approve_in |
Where a person can approve: bumpkit, plus slack when Slack approvals are connected (Beta, flag slack_approvals) |
loop |
The Close the loop items for this branch and pull request (Beta, flag close_the_loop) |
Errors: 400 INVALID_POLICY when policy doesn't parse (the gate fails closed), 400 VALIDATION_ERROR when source is reserved (github, gitlab, bitbucket, azure, azure_devops and dashboard are for checks Bumpkit runs itself) or when repository can't resolve to one name (www.github.com/acme/api, acme//api and the repository's URL all resolve to acme/api; acme/api/extra and acme/../api are refused), 503 POLICY_UNAVAILABLE (see below), plus the check errors. With org policies on, 503 ORG_POLICY_UNAVAILABLE when the org policy can't be read or parsed.
Where the policy comes from
When the Bumpkit GitHub App is installed on the repository for your workspace, the gate reads bumpkit.yml itself and ignores the policy you send, including its agents: section:
| Request | Policy read from |
|---|---|
With pull_request into the default branch |
The pull request's base commit |
With pull_request into any other branch |
The default branch |
No pull_request |
The default branch. base never picks the policy. |
policy_source names the ref, such as base_branch:main. When the policy you sent differs, the decision also carries a passing reason with the rule policy_source: base_branch:main (policy read from your repository). If GitHub doesn't return the file, the gate fails closed with 503 POLICY_UNAVAILABLE.
A decision from this endpoint uses the diff you sent, so with the App installed it's a report: it doesn't change the required check, a deployment, a vote or a release train.
Without the App, the gate uses the policy you send, policy_source is client, and the decision carries a passing reason with the rule policy_source: client, because Bumpkit can't prove a pull request didn't loosen its own rules.
POST /v1/gate/override
Always 403 HUMAN_APPROVAL_REQUIRED for an API key, whatever its owner's role. A break-glass override needs a person signed in to the dashboard: an owner, admin or policy admin, on the commit's check page. The message links to it.
| Field | Type | Required | Description |
|---|---|---|---|
repository |
string | Yes | owner/name |
sha |
string | Yes | The full 40-character commit SHA |
reason |
string | Yes | 10 to 500 characters. Kept in the audit record. |
pull_request |
integer | No | Re-run the gate on this pull request |
The request shape is kept so the refusal is the same for every client.
GET /v1/gate/overrides
Lists overrides. Query limit (1 to 500, default 100) and overdue=true (only overrides whose review is overdue).
Returns {"overrides": [...]} with id, repository, sha, pull_request, reason, actor_email, created_at, review_due_at, reviewed_at, reviewed_by, review_note and status.
POST /v1/gate/overrides/{id}/review
Records the after-the-fact review of an override. Owners, admins, policy admins and approvers only (the gate.review permission). The person who overrode can't review their own override.
| Field | Type | Required | Description |
|---|---|---|---|
note |
string | Yes | What the review found, 10 to 1000 characters |
Returns 204. 404 NOT_FOUND for an unknown override, 409 CONFLICT if it was already reviewed.
GET /v1/gate/connections
Lists the GitLab, Bitbucket and Azure DevOps repositories connected to the gate. Owner only. It answers even when those features are off, with whatever connections exist. Returns {"connections": [{id, provider, host, path, webhook_url, created_at}]}. Secrets are never listed.
POST /v1/gate/connections
Beta, early access.
Connects a repository host to the gate. Owner only.
| Field | Type | Required | Description |
|---|---|---|---|
provider |
string | Yes | gitlab, bitbucket, azure_devops or ghes (GitHub Enterprise Server) |
host |
string | No | Public https API host. Defaults to https://gitlab.com for gitlab and https://api.bitbucket.org/2.0 for bitbucket. Required for azure_devops and ghes. For ghes, give your server's address, like https://github.acme.example. |
path |
string | Yes | group/project (GitLab), workspace/repository (Bitbucket), project/repository (Azure DevOps) or owner/repository, or an owner alone (GitHub Enterprise Server) |
access_token |
string | Yes | Token Bumpkit uses to read the repository and report status. Up to 2048 characters. Stored encrypted. |
Returns 201 with the connection plus webhook_secret, which is shown only here. Point the host's webhook at webhook_url (see Inbound webhooks). 409 CONFLICT if the host and path are already connected.
DELETE /v1/gate/connections/{id}
Needs a person signed in to the dashboard: an API key gets 403 HUMAN_APPROVAL_REQUIRED with the link.
Beta.
Removes a connection. Owner only. It works even when the provider's feature is off. Returns 204, or 404 NOT_FOUND.
POST /v1/rules/compile
Compiles a plain-English rule into bumpkit.yml gate policy. Needs a key tied to a person. Limited to 20 compiles per 10 minutes per workspace.
| Field | Type | Required | Description |
|---|---|---|---|
text |
string | Yes | The rule, up to 500 characters |
repository |
string | Yes | owner/name |
policy |
string | No | The current bumpkit.yml. Read from GitHub through the App when omitted. |
dry_run |
boolean | No | Replay the rule on recent gate decisions |
limit |
integer | No | Decisions to dry-run, 1 to 200, default 50 |
Returns 201 with a rule: id, repository, text, summary, caveats, yaml, diff, policy_file, policy_source, policy_after, status, created_by, approved_by, approved_at, pull_request_url, dry_run (evaluated, skipped, changed[]) and created_at.
Errors: 422 RULE_NOT_REPRESENTABLE (the rule can't be expressed as policy), 503 LLM_NOT_CONFIGURED, 429 RATE_LIMIT_EXCEEDED.
GET /v1/rules
Lists compiled rules. Query limit: 1 to 200, default 50. Returns {"rules": [Rule]}.
GET /v1/rules/{id}
Returns one rule, or 404 NOT_FOUND.
POST /v1/rules/{id}/approve
Approves a rule. Owners, admins and policy admins only (the policy.manage permission). Send {"open_pr": true} to open a pull request with the policy change through the GitHub App. Returns 200 with the rule, status now approved or pr_opened. 409 if it is already approved.
Org policies & freezes
Beta, early access. Flags:
org_policies,change_freezes. Until your workspace has access, these endpoints answer403 FEATURE_EARLY_ACCESS.
An org policy is a bumpkit.yml layer applied to every repository in the workspace. Freezes block or slow releases for a period. See Org Policies & Freezes. These endpoints need a key tied to a person, except POST /v1/policy/effective.
GET /v1/policy/org
Returns the current org policy: version, content, repository, message, actor_email, created_at. 404 NOT_FOUND if there is none.
PUT /v1/policy/org
Needs a person signed in to the dashboard: an API key gets 403 HUMAN_APPROVAL_REQUIRED with the link.
Sets the org policy. Owner only. Send exactly one of content, repository or clear.
| Field | Type | Description |
|---|---|---|
content |
string | The policy YAML, up to 64 KB |
repository |
string | owner/name of a config repository. Bumpkit reads its bumpkit.yml from the default branch through the GitHub App. |
clear |
boolean | Remove the org policy |
message |
string | Why it changed, up to 500 characters |
Each change creates a new version. Returns 200 {"policy": OrgPolicy, "diff": "..."}. 409 CONFLICT if nothing changed, there is nothing to clear, or someone else changed it at the same time.
GET /v1/policy/org/versions
Lists versions, newest first. Query limit: 1 to 200, default 50. Returns {"versions": [OrgPolicy]}.
GET /v1/policy/org/versions/{version}
Returns one version. 400 if version isn't a positive number, 404 NOT_FOUND if it doesn't exist.
POST /v1/policy/effective
Shows the policy the gate applies: the org layer merged with a repository's policy. Works with any key.
| Field | Type | Description |
|---|---|---|
policy |
string | The repository's bumpkit.yml text |
Returns org_version, org_repository, policy (merged YAML) and sources ([{key, value, from, locked, note}], where each key's value came from). 400 INVALID_POLICY if the merge fails, 503 ORG_POLICY_UNAVAILABLE if the org policy can't be read or parsed.
GET /v1/freezes
Lists active and upcoming freezes, plus those created in the last 30 days, up to 100. Returns {"freezes": [Freeze]}.
Each freeze has id, repository (empty means every repository), reason, mode, hotfix, starts_at, ends_at, ended_at, ended_by, actor_email, active and created_at.
POST /v1/freezes
Declares a freeze. Owners, admins and policy admins only (the policy.manage permission). Other roles get 403 FORBIDDEN, and the refusal is written to the audit log as freeze.declare.denied.
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | Yes | 10 to 500 characters |
repository |
string | No | owner/name. Omit to freeze every repository. |
mode |
string | No | block (default) or approve |
hotfix |
string | No | allow, approve (default) or block |
starts_at |
string | No | RFC 3339. Defaults to now. |
ends_at |
string | No | RFC 3339. Must be after starts_at and in the future. |
Returns 201 with the freeze.
POST /v1/freezes/{id}/end
Needs a person signed in to the dashboard: an API key gets 403 HUMAN_APPROVAL_REQUIRED with the link.
Ends a freeze. Owners, admins and policy admins only; other roles get 403 FORBIDDEN, audited as freeze.end.denied. Returns 200 with the freeze. 404 NOT_FOUND, or 409 CONFLICT if it is already over.