Skip to content
Docs/Deploy markers

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"
  }'
201 Created
{
  "_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 "*").

FieldTypeDescription
versionstringRelease version or tag, e.g. "1.4.2"max length 100
commitstringCommit SHA (shown shortened)max length 80
urlstring (url)Link to the release, CI run or PRmax length 2,000
environmentstringe.g. "production", "staging"max length 40
descriptionstringmax length 2,000
servicestringService name; monitors tagged or named like this are linked automaticallymax length 120
monitorIdsstring[]Limit the deploy to these monitors. Also accepts a comma-separated string ("mon_a,mon_b"), which suits CI inputs.max 100 items
monitorIdstringShortcut for one monitor.max length 60
pageIdstringLimit the deploy to one status pagemax length 60
pageIdsstring[]Limit the deploy to these status pages (comma-separated string accepted).max 100 items
atstring (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 monitors api, web, … and pass service from 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.

deploy mode (default)
# .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"
InputDefaultMeaning
api-key—API key with the write scope. Store it as a secret
versiontag name, else short SHARelease version
commitgithub.shaCommit SHA
environmentproductionDeployment 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
urlthis workflow runLink shown with the deploy
api-urlhttps://upbutler.comBase URL
fail-on-errorfalseFail 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\"}" || true

The 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.

ai.opening (excerpt)
"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.