Monitoring
Deploy markers
Tell UpButler when you ship. Incidents that start right after a release are then traced back to it in AI reports, charts and timelines, so the first question in every outage answers itself.
Record a deploy
curl -X POST https://upbutler.com/api/v1/deploys \
-H "Authorization: Bearer $UPBUTLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"version": "1.4.2",
"commit": "abc1234def5678",
"environment": "production",
"service": "api",
"url": "https://github.com/acme/api/releases/tag/v1.4.2"
}'{
"_id": "dep_0n3qc1a2b3c4d5e6f7g",
"workspaceId": "ws_0n3q8jy8r5t1y3u7i9o",
"version": "1.4.2",
"commit": "abc1234def5678",
"url": "https://github.com/acme/api/releases/tag/v1.4.2",
"environment": "production",
"service": "api",
"monitorIds": ["mon_0n3q8kz1m4hx7c2v9rt"],
"pageIds": [],
"at": "2026-10-09T08:10:02.000Z",
"createdBy": { "type": "api_key", "id": "key_0n3q8jy9x1c4v7b2n6m", "name": "CI" },
"createdAt": "2026-10-09T08:10:02.311Z",
"scope": "targeted",
"shortCommit": "abc1234"
}The minimal input is {"version": "1.4.2"}. A deploy needs at least a version, commit or description. at defaults to now, may be up to 90 days in the past, and may not be more than 5 minutes in the future. Over MCP, the tool is deploys_create. Every deploy emits a deploy.created event, which channels receive when they list it (or "*").
| Field | Type | Description |
|---|---|---|
version | string | Release version or tag, e.g. "1.4.2"max length 100 |
commit | string | Commit SHA (shown shortened)max length 80 |
url | string (url) | Link to the release, CI run or PRmax length 2,000 |
environment | string | e.g. "production", "staging"max length 40 |
description | string | max length 2,000 |
service | string | Service name; monitors tagged or named like this are linked automaticallymax length 120 |
monitorIds | string[] | Limit the deploy to these monitors. Also accepts a comma-separated string ("mon_a,mon_b"), which suits CI inputs.max 100 items |
monitorId | string | Shortcut for one monitor.max length 60 |
pageId | string | Limit the deploy to one status pagemax length 60 |
pageIds | string[] | Limit the deploy to these status pages (comma-separated string accepted).max 100 items |
at | string (date-time) | When it went out (ISO 8601, default now)pattern ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
Scope
By default a deploy applies to the whole workspace (scope: "workspace"). Narrow it when you run several services:
service: every monitor whose tag or name equals the service name (case-insensitive) is linked automatically. Tag your monitorsapi,web, … and passservicefrom CI.monitorIds/monitorId: specific monitors.pageId/pageIds: everything on a status page.
A targeted deploy (scope: "targeted") only shows up for incidents on those monitors, or on pages they feed. Workspace-wide deploys show up everywhere.
GitHub Action
The official action records a deploy at the end of your deploy job. It never fails your workflow unless you set fail-on-error: true.
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh
- uses: upbutler/action@v1
with:
api-key: ${{ secrets.UPBUTLER_API_KEY }}
version: ${{ github.ref_name }} # optional: defaults to the tag, else the short SHA
environment: production # optional (default production)
service: api # optional: links monitors tagged or named "api"| Input | Default | Meaning |
|---|---|---|
api-key | — | API key with the write scope. Store it as a secret |
version | tag name, else short SHA | Release version |
commit | github.sha | Commit SHA |
environment | production | Deployment environment |
service | — | Links monitors tagged or named like this |
monitor | — | Comma-separated mon_… ids |
page | — | Status page id (pg_…) |
description | “Deployed by <actor> via GitHub Actions” | Free-form note |
url | this workflow run | Link shown with the deploy |
api-url | https://upbutler.com | Base URL |
fail-on-error | false | Fail the step when UpButler can't be reached |
The step output deploy-id holds the new deploy's id. Each run sends an Idempotency-Key derived from the run id, attempt and job, so a retried step never records a deploy twice.
Heartbeat mode
The same action can ping a heartbeat from scheduled workflows. status: ${{ job.status }} reports failures to /fail:
- run: ./backup.sh
- uses: upbutler/action@v1
if: always()
with:
mode: heartbeat
heartbeat-url: ${{ secrets.UPBUTLER_BACKUP_HEARTBEAT }}
status: ${{ job.status }}Other CI and agents
# Any CI or deploy script
curl -fsS -X POST https://upbutler.com/api/v1/deploys \
-H "Authorization: Bearer $UPBUTLER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploy-$CI_PIPELINE_ID" \
-d "{\"version\": \"$VERSION\", \"commit\": \"$GIT_SHA\", \"service\": \"api\"}" || trueThe SDKs and the CLI also have deploy helpers. See SDKs and CLI.
How deploys are used
AI incident reports
When an incident opens, the AI gets every relevant deploy from the hour before the failures began, each with its timing (“2 min before the incident started”). It is told to mention a deploy when the timing fits, and to weigh it in the likely causes without claiming causation the data doesn't support. Recovery reports and postmortem drafts get the deploys from the hour before through the end of the incident.
"analysis": "Errors began 2 minutes after deploy 1.4.2 (commit abc1234): every check since 08:12 returns HTTP 500 with \"TypeError: cannot read properties of undefined\" in the body, while the previous 3 hours were clean. TLS and DNS are healthy.",
"likelyCauses": ["Regression in deploy 1.4.2", "Missing configuration for the new release"],
"suggestedActions": ["Roll back to the previous release and confirm recovery", "Check the 1.4.2 changelog for the search handler"]Listing deploys
curl "https://upbutler.com/api/v1/deploys?incidentId=inc_0n3q9a11v8k2h5n0qzc" \
-H "Authorization: Bearer $UPBUTLER_API_KEY"GET /deploys is newest first. Filter with monitorId or pageId (both include workspace-wide deploys), environment, since/until (ISO 8601) and limit (max 500). incidentId returns the deploys relevant to that incident, from an hour before it started through its end. The dashboard shows them as ticks on monitor charts and as “Deploy 1.4.2, 2m before” in incident timelines. DELETE /deploys/:id removes a marker.