Incidents & alerts
Insights: digests, postmortems & templates
Beyond alerts: a weekly summary of what changed, blameless postmortem drafts written from the incident's own data, and templates so every update sounds right under pressure.
Weekly digest
Every Monday at 08:00 in the workspace timezone, UpButler sums up the previous week: availability per monitor and component, incidents and their duration, the slowest endpoints, latency regressions and improvements week over week, flapping monitors, TLS certificates expiring within 30 days, quota usage, and a short “what to look at this week” list.
- Email: workspace owners get it by default. Every member can opt in or out under
/app/settings/digest. - Webhook and other channels: subscribe a channel to
digest.weekly(ordigest.*/"*") to receive the full JSON. Handy for agents that plan the week. - The “what to look at” list is written by AI (one report from the monthly quota) when someone receives the digest. Otherwise, or if AI is unavailable, it is built from rules: expiring certificates, open incidents, the least available monitor, flapping, latency regressions, and quotas above 80%.
- Each workspace gets at most one digest per ISO week. Workspaces without monitors or status pages are skipped.
GET /digest/settings shows the timezone, recipients, subscribed channels, nextSendAt and the last runs. PATCH /digest/settings sets the workspace timezone (owner or admin; IANA name such as Europe/Berlin, default UTC) or your own subscribed flag (signed-in members only).
Build one on demand
curl -X POST https://upbutler.com/api/v1/digest/preview \
-H "Authorization: Bearer $UPBUTLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"period": "last_7_days", "ai": true}'{
"version": 1,
"workspace": { "id": "ws_0n3q8jy8r5t1y3u7i9o", "name": "Acme", "timezone": "Europe/Berlin" },
"week": "2026-W41",
"period": { "start": "2026-10-05T06:00:00.000Z", "end": "2026-10-12T06:00:00.000Z", "label": "Oct 5 – Oct 11", "days": 7 },
"summary": { "monitors": 14, "avgUptime": 99.962, "checks": 61840, "incidents": 2, "incidentMinutes": 23, "openIncidents": 0, "monitorsDownNow": 0 },
"monitors": [ { "id": "mon_0n3q8kz1m4hx7c2v9rt", "name": "Search API", "uptime": 99.861, "prevUptime": 100, "avgLatencyMs": 412, "prevAvgLatencyMs": 288, "latencyChangePct": 43, ... } ],
"components": [ ... ],
"incidents": [ { "id": "inc_0n3q9a11v8k2h5n0qzc", "title": "Elevated errors on the Search API", "impact": "critical", "durationText": "14 minutes", ... } ],
"slowest": [ ... ],
"latencyTrends": { "regressed": [ ... ], "improved": [ ... ] },
"flapping": [ { "id": "mon_0n3q8kz9z8y7x6w5v4u", "name": "Webhooks", "transitions": 9, "downs": 4 } ],
"tls": [ { "monitorId": "mon_0n3q8kzc4d5e6f7g8h9", "name": "Marketing site", "host": "acme.com", "daysLeft": 12, "validTo": "2026-10-23T23:59:59.000Z" } ],
"quota": { "month": "2026-10", "plan": "pro", "aiReports": { "used": 41, "limit": 1000, "pct": 4 }, ... },
"focus": {
"source": "ai",
"headline": "Search API got slower and Webhooks is flapping.",
"items": [
{ "title": "Search API got slower", "detail": "Average response time went from 288ms to 412ms (+43%) week over week.", "severity": "warn" },
{ "title": "Renew the TLS certificate for acme.com", "detail": "It expires in 12 day(s) (2026-10-23).", "severity": "warn" }
]
},
"url": "https://upbutler.com/app"
}period: last_week is exactly what Monday's email covers, and last_7_days (default) is rolling. ai: true uses one AI report and needs a write-scoped key. format: "html" also returns the rendered email (subject, html, text).
Postmortems
POST /incidents/:id/postmortem drafts a blameless postmortem in markdown from the incident's own data: its updates, the checks around it, and deploy markers from the hour before through the end. The draft covers summary, impact, timeline, root-cause hypotheses, contributing factors, what went well and poorly, and action items. It also writes a separate customer-safe summary. Each draft uses one AI report.
curl -X POST https://upbutler.com/api/v1/incidents/inc_0n3q9a11v8k2h5n0qzc/postmortem \
-H "Authorization: Bearer $UPBUTLER_API_KEY"- Every draft or edit is stored as a new version (the last 30 are kept).
GET /incidents/:id/postmortemreturns the current markdown, the public summary, publish state and the version list. Addhistory=truefor every version's markdown. - When AI is unavailable or the quota is used up, the call returns
503with a ready-to-edit template inerror.details.template.mode: "template"returns that template draft directly, without using AI or saving anything. - Postmortems are for incidents, not maintenance windows.
Edit
curl -X PUT https://upbutler.com/api/v1/incidents/inc_0n3q9a11v8k2h5n0qzc/postmortem \
-H "Authorization: Bearer $UPBUTLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"markdown": "# Search API outage, 9 October\n…", "publicSummary": "On 9 October, search was unavailable for 14 minutes…", "baseVersion": 1}'Send baseVersion (the version you edited) to get 409 conflict instead of silently overwriting a teammate's or agent's newer version.
Publish to status pages
curl -X POST https://upbutler.com/api/v1/incidents/inc_0n3q9a11v8k2h5n0qzc/postmortem/publish \
-H "Authorization: Bearer $UPBUTLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"notify": true}'Publishing posts the public summary (or your body) as a public update on the resolved incident and notifies subscribers (notify: false to skip). The incident must be on a status page. Before anything goes out, the text is checked, and it is rejected with 422 and the reasons when it contains an IP address, an internal hostname (*.internal, *.local, localhost, …), a hostname you monitor, a stack trace, or a term the page's AI policy forbids.
Incident templates
Templates are reusable presets for title, status, impact, message and affected components, so the 3 a.m. update reads like the one you wrote calmly. New workspaces start with these defaults:
| Template | Status | Impact | Title |
|---|---|---|---|
| Investigating elevated errors | investigating | major | Elevated error rates on {{component}} |
| Degraded performance | investigating | minor | Degraded performance on {{component}} |
| Identified – fix in progress | identified | major | Issue identified with {{component}} |
| Monitoring a fix | monitoring | minor | Monitoring a fix for {{component}} |
| Resolved | resolved | none | {{title}} |
| Scheduled maintenance | scheduled | maintenance | Scheduled maintenance for {{component}} |
Placeholders
Titles and bodies support {{component}}, {{components}}, {{duration}}, {{status_page}}, {{title}}. Missing values fall back to neutral wording, so a rendered template is always publishable:
| Placeholder | Becomes | Fallback |
|---|---|---|
{{component}}, {{components}} | Affected component names: “Search”, “Search and Billing”, “A, B and C” | “some of our services” |
{{duration}} | Incident duration so far: “14 minutes”, “2 hours 5 minutes” | “a short period” |
{{status_page}} | The status page name | “our status page” |
{{title}} | The incident title | “this incident” |
Manage and use
curl -X POST https://upbutler.com/api/v1/incident-templates \
-H "Authorization: Bearer $UPBUTLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Payment provider outage",
"title": "Payments delayed on {{component}}",
"status": "identified",
"impact": "major",
"body": "Our payment provider is having issues. {{component}} payments may be delayed; no charges are lost.",
"componentIds": ["cmp_0n3q8kz2p7wd4yx0s3a"]
}'| Field | Type | Description |
|---|---|---|
namerequired | string | Shown in the "Use template" pickermax length 80 |
kind | string | Default incidentincidentmaintenance |
titlerequired | string | Incident title; placeholders allowedmax length 200 |
status | string | investigatingidentifiedmonitoringresolvedscheduledin_progresscompleted |
impact | string | noneminormajorcriticalmaintenance |
bodyrequired | string | Update text. Placeholders: {{component}}, {{duration}}, {{status_page}}, {{title}}max length 10,000 |
componentIds | string[] | Components affected by defaultmax 200 items |
componentStatus | string | Status the default components get (default partial_outage)operationaldegradedpartial_outagemajor_outagemaintenance |
order | integer | min 0 · max 10,000 |
POST /incident-templates/:id/render fills a template and returns title, message, status, impact and components, ready for POST /incidents or POST /incidents/:id/updates. With incidentId, that incident's components and duration fill the placeholders. Otherwise componentIds (or the template's defaults) do. Up to 100 templates per workspace. List, update and delete with GET, PATCH and DELETE /incident-templates.
curl -X POST https://upbutler.com/api/v1/incident-templates/tpl_0n3qe5a6b7c8d9e0f1g/render \
-H "Authorization: Bearer $UPBUTLER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"componentIds": ["cmp_0n3q8kz2p7wd4yx0s3a"]}'
# → { "title": "Payments delayed on Checkout", "message": "Our payment provider is having issues. Checkout payments may be delayed; …",
# "status": "identified", "impact": "major", "components": [{ "componentId": "cmp_0n3q8kz2p7wd4yx0s3a", "status": "partial_outage" }], ... }