Ship
Release Health & Forensics
Record deploys, count production errors from PostHog, Sentry or a signed webhook, and trace each regression to the commit behind it.
A release can pass every check and still break production; release health watches errors after each deploy and names the top suspect commit, for on-call engineers and service owners.
Beta, early access. A workspace without access gets
403 FEATURE_EARLY_ACCESSand a link to request it. Flags:release_health,forensics.
Bumpkit closes the loop after a deploy. You tell it when a version went live, it counts production errors before and after, decides whether the release regressed, and names the commit that most likely caused it from the errors' stack frames. Separately, "what broke production" takes any error and stack trace and ranks the releases shipped before it.
| Feature | Flag | What it gives you |
|---|---|---|
| Release health | release_health |
Deploy records, error counts, a verdict per deploy, suspect commits, regressions opened as incidents |
| What broke production | forensics |
POST /v1/incidents, bumpkit incidents, the what_broke MCP tool, incident sources |
Record deploys
Release health compares errors around the moment a version went live, so it needs a deploy record. Recording deploys needs forensics or release_health.
From the CLI
Run bumpkit deploy from your deploy pipeline, in a checkout of the deployed commit:
bumpkit deploy --version v2.3.0 --environment productionBy default it sends HEAD as the deployed commit, the origin remote as the repository, a tag on the commit as the version, and every commit since the previous tag with the lines each one changed. Bumpkit uses those changed lines to match errors to commits.
| Flag | Default | Meaning |
|---|---|---|
--sha |
HEAD |
The deployed commit |
--version |
A tag on the commit | The version that went live, e.g. v2.3.0 |
--environment |
production |
Where it was deployed |
--repository |
The origin remote |
owner/name |
--base |
The tag before the commit | The previous release, to send the commits this deploy shipped |
--status |
success |
success, failure or in_progress |
--started-at |
none | When the deploy started (RFC 3339) |
--finished-at |
now | When the deploy finished (RFC 3339) |
--no-changes |
off | Don't send the commits and changed lines |
If there is no earlier tag, no commits are sent. Pass --base so errors can be attributed to individual commits.
From the API
curl -X POST https://bumpkit.dev/v1/deployments \
-H "x-api-key: $BUMPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"repository": "acme/api",
"sha": "4b7a1d2c9e3f",
"environment": "production",
"version": "v2.3.0",
"status": "success",
"commits": [
{
"sha": "4b7a1d2c9e3f",
"subject": "Cache invoice totals",
"files": [
{ "path": "billing/invoice.go", "hunks": [{ "start": 120, "end": 148, "context": "func Total" }] }
]
}
]
}'| Field | Required | Notes |
|---|---|---|
repository |
yes | owner/name, as your release checks record it |
sha |
yes | 7 to 40 hex characters |
environment |
no | Defaults to production; up to 64 characters |
version |
no | Up to 64 letters, digits and . _ + - / @ |
status |
no | success (default), failure or in_progress. succeeded, failed, error, started, pending and queued are accepted as aliases |
deployed_at, started_at, finished_at |
no | RFC 3339. If deployed_at is missing, Bumpkit uses finished_at for a successful deploy, then started_at, then now |
commits |
no | Up to 500 commits, each with sha, subject and files (path and optional hunks with start, end, context) |
The endpoint returns 204 No Content. Each recorded deploy is written to the audit log.
From the GitHub App
If the Bumpkit GitHub App is installed, GitHub deployment_status events are recorded as deploys automatically.
Connect an error source
Bumpkit keeps error counts for 30 days. Connect at least one source; without one, every verdict is unknown.
PostHog
Bumpkit polls PostHog for $exception events with a HogQL query.
- Connect PostHog on the dashboard's Integrations page (see Integrations). Give the personal API key the
query:readscope. - On the Incidents page, in the Release health card, set PostHog errors for to the repository (
owner/name) and PostHog environment to the environment those errors come from (defaultproduction). Leave the repository empty to stop polling.
Bumpkit reads the version from $app_version, falling back to $release, and keeps only in-app stack frames when PostHog marks them. It polls every five minutes, two minutes behind real time, in one-hour slices, and catches up to 24 hours after a gap. When PostHog rate-limits or rejects the key, polling backs off (up to six hours) and the error shows on the Release health card.
Sentry
- In Sentry, create an internal integration with webhooks on, and an alert rule that sends to it.
- On the dashboard's Integrations page, open the Sentry card, enter the repository (
owner/name) and the integration's client secret, and click Save error source. - Bumpkit shows two URLs for the source. Point the Sentry webhook at one of them:
https://bumpkit.dev/hooks/incidents/<token> incidents, traced to a release (forensics)
https://bumpkit.dev/hooks/errors/<token> error counts for release healthBumpkit verifies the Sentry-Hook-Signature header (hex HMAC-SHA256 of the body with the client secret) and reads the Sentry-Hook-Resource header. It handles event_alert resources, and issue resources with action created; it ignores others with 204. Sentry events sent to the incidents URL are also counted for release health.
Signed webhook
Any tool can send errors with a signed webhook.
- In the same Production errors card, enter the repository and leave Sentry client secret empty.
- Click Save error source. Bumpkit shows the webhook secret once. Store it in your secret manager.
- Sign each POST: put
sha256=followed by the hex HMAC-SHA256 of the raw body, keyed with the secret, in theX-Bumpkit-Signatureheader.
body='{"events":[{"fingerprint":"TypeError-invoice-total","type":"TypeError","message":"Cannot read properties of undefined","environment":"production","version":"v2.3.0","frames":[{"file":"billing/invoice.go","function":"Total","line":131}],"count":4,"timestamp":"2026-10-21T09:30:00Z"}]}'
sig=$(printf '%s' "$body" | openssl dgst -sha256 -hmac "$BUMPKIT_ERRORS_SECRET" -hex | sed 's/^.* //')
curl -X POST "https://bumpkit.dev/hooks/errors/$TOKEN" \
-H "Content-Type: application/json" \
-H "X-Bumpkit-Signature: sha256=$sig" \
-d "$body"The errors endpoint takes one event object or {"events": [...]} with up to 500 events, and returns 202 with {"recorded": n}.
| Field | Notes |
|---|---|
fingerprint |
Groups occurrences of the same error |
type, message |
Joined as the error's title |
environment, version |
Where and in which release it happened |
frames |
Stack frames: file, function, line |
count |
Occurrences in this event, at least 1 (default 1) |
timestamp |
RFC 3339 |
Each event needs at least one of type, message, fingerprint or frames. The endpoint allows 600 requests a minute per source; beyond that it returns 429 with Retry-After: 60.
Regression verdicts
For each deploy, Bumpkit compares production errors in the window after it went live with the window before. The window defaults to 60 minutes each side and stops at the next deploy. When the version label is on the errors, only errors tagged with this version (or untagged) count after the deploy. Each error gets a status:
| Status | Meaning |
|---|---|
new |
A clear rise in an error that wasn't seen before the deploy |
regressed |
A clear rise in an error that was already happening |
elevated |
A smaller rise worth watching |
emerging |
A handful of a new error, not yet a clear rise |
The release verdict is:
| Verdict | When |
|---|---|
regressed |
The overall error rate clearly rose, or any error is new or regressed |
degraded |
The overall rate rose a little, or an error is elevated or emerging |
healthy |
Error rate held, or fewer than min_events errors after the deploy |
unknown |
No error source reports for the repository, or the after window is empty yet |
min_events is the fewest errors that can make a verdict, so a few stray errors never flag a release. Up to 10 error regressions are listed, most severe first. partial: true means the after window is still open.
Settings
| Setting | Default | Range |
|---|---|---|
window_minutes |
60 | 15 to 1440 |
min_events |
20 | 1 to 100000 |
posthog_repository |
none | owner/name |
posthog_environment |
production |
One word |
Workspace owners and admins change them on the Incidents page or with the API:
curl -X PUT https://bumpkit.dev/v1/release-health/settings \
-H "x-api-key: $BUMPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"window_minutes": 60, "min_events": 20, "posthog_repository": "acme/web", "posthog_environment": "production"}'GET /v1/release-health/settings also returns last_polled_at and last_poll_error. Setting posthog_repository requires PostHog to be connected first.
Suspect commits
For each regressed error, Bumpkit ranks the deploy's commits by how closely their changed lines match the error's stack trace. Suspects are shown with a likelihood (high, medium or low), not a percentage. Up to five suspects are listed, each with evidence such as billing/invoice.go:131 (Total) → 4b7a1d2 changed lines 120–148 of billing/invoice.go. If the deploy was recorded without commits, Bumpkit uses the files of the release check for the deployed commit (attribution: release_files).
Regressions open incidents
When a deploy's verdict is regressed, Bumpkit:
- opens an incident titled
v2.3.0 regressed in production: <top error>with high likelihood, linked to the release check for the deployed commit; - posts a notice with the top suspect to your Slack, Discord and outbound webhook integrations;
- writes
incident.opento the audit log.
When a later deploy brings that error back down, Bumpkit resolves the incident with that version and writes incident.resolve.
Check a release
bumpkit release health v2.3.0
bumpkit release health v2.3.0 --environment staging --window 30 --jsonIt exits 1 when the release regressed, so you can use it as a post-deploy step. Flags: --environment, --repository, --window (minutes) and --min-events. The version can also be the deployed commit SHA.
The API equivalent is GET /v1/releases/{version}/health with the optional query parameters repository, environment, window and min_events. It returns 404 DEPLOYMENT_NOT_FOUND if no successful deploy of that version is recorded.
{
"repository": "acme/api",
"environment": "production",
"version": "v2.3.0",
"sha": "4b7a1d2c9e3f",
"verdict": "regressed",
"reason": "A new error appeared after v2.3.0: TypeError: Cannot read properties of undefined (42 in 60 minutes, p < 0.001).",
"partial": false,
"min_events": 20,
"before": { "minutes": 60, "errors": 3, "per_hour": 3 },
"after": { "minutes": 60, "errors": 45, "per_hour": 45 },
"rate_ratio": 15,
"regressions": [
{
"fingerprint": "TypeError-invoice-total",
"status": "new",
"before": 0,
"after": 42,
"suspects": [{ "sha": "4b7a1d2c9e3f", "likelihood": "high", "score": 5, "evidence": ["..."] }]
}
],
"attribution": "commits",
"incident_id": "3f2c9a1e-8b7d-4c2a-9e1f-6a5b4c3d2e1f"
}Agents use the release_health MCP tool with version, and optionally repository, environment and window.
What broke production
Beta, early access. A workspace without access gets
403 FEATURE_EARLY_ACCESSand a link to request it. Flag:forensics.
Give Bumpkit an error with its stack trace and it ranks the releases checked or deployed before it started.
bumpkit incidents report --error "$(cat trace.txt)" --since 2026-10-21T09:30:00Z| Flag | Meaning |
|---|---|
--error |
The error message and stack trace |
--file |
A file in the stack trace (repeatable) |
--repository |
Repository URL (default: the origin remote) |
--since |
When the error started (RFC 3339, default now) |
--lookback-days |
How far back to look for releases (1 to 90, default 14) |
The API is POST /v1/incidents with repository, error, files, started_at and lookback_days. Agents use the what_broke MCP tool with the same inputs.
Bumpkit reads the file paths and line numbers in the trace, skips third-party code, and ranks the releases in the lookback window by how strongly their changes and findings line up with the failing code. Suspects are shown with their likelihood, such as Top suspect · high likelihood, rather than a made-up percentage. If you record deploys for the repository, only releases that were actually deployed are ranked, timed from the deploy. Each suspect lists its evidence.
Incident sources
The Sentry and signed webhook sources above also feed incidents. Send to https://bumpkit.dev/hooks/incidents/<token>; Bumpkit returns 202, traces the error, and adds it to the Incidents page with its top suspects. The signed webhook body for incidents is:
| Field | Notes |
|---|---|
error |
The error with its stack trace |
files |
Files involved, if there is no trace |
title |
Defaults to the first line of error |
url |
A link back to the error |
id |
Your ID, used to deduplicate |
started_at |
RFC 3339 |
Send error or files. With codebase memory on (flag codebase_memory), a newly traced incident with a clear suspect is also saved as a memory on the failing path.
PagerDuty and Datadog incident sources are covered on Integrations.
Manage incidents
bumpkit incidents list --limit 50
bumpkit incidents resolve 3f2c9a1e-8b7d-4c2a-9e1f-6a5b4c3d2e1f --version v2.3.1 --sha 4b7a1d2GET /v1/incidents?limit= lists recent incidents (up to 500) and POST /v1/incidents/{id}/resolve takes {"version": "v2.3.1", "sha": "optional commit"}. To plan a revert for an incident, see Rollback & Time Machine.
See the CLI Reference and API Reference for every flag and field.