Skip to content
Docs/Push & manifest

Status pages

Push & manifest

Not everything is an HTTP endpoint we can check. With push components and manifest monitors, your own systems decide what the status page says.

Push one component

Create a component with "push": true (see Components). The response includes source.pushUrl. POST a status to it. No API key is needed, because the push_… token is the secret.

curl -X POST https://upbutler.com/api/v1/push/push_Qm7vB2xK9pL4sT1nW8cZ3yH6 \
  -H "Content-Type: application/json" \
  -d '{"status": "degraded", "message": "p95 latency above 2s"}'
200 OK
{ "componentId": "cmp_0n3q8kz2p7wd4yx0s3a", "key": "pipeline", "status": "degraded", "changed": false, "pending": true }

changed is true when the component's visible status changed. pending: true means a worse status was recorded but is waiting for confirmation (see blip protection). With the default of 2 confirmations, the first bad push of an outage returns pending, and the second one shows it. Each token accepts up to 120 pushes per minute. An unrecognized status returns 422.

Push many at once

POST /pages/:pageId/status (API key with write scope; MCP tool pages_push) updates any number of components on one page. Each key is matched against a component's key, its id, or its name (case-insensitive). A value is either a status, or an object with status and an optional message.

curl -X POST https://upbutler.com/api/v1/pages/acme/status \
  -H "Authorization: Bearer $UPBUTLER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "statuses": {
      "search": "operational",
      "billing": { "status": "degraded", "message": "Stripe latency", "confirmed": true },
      "Reddit": "fail",
      "exports": true,
      "legacy-api": "teapot"
    }
  }'
200 OK
{
  "page": "acme",
  "results": [
    { "key": "search", "status": "operational", "changed": false },
    { "key": "billing", "status": "degraded", "changed": true },
    { "key": "Reddit", "status": "major_outage", "changed": false, "pending": true },
    { "key": "exports", "status": "operational", "changed": false },
    { "key": "legacy-api", "error": "unrecognized status \"teapot\"" }
  ]
}

Unknown components and unrecognized statuses are reported per key and don't fail the request. Components don't have to be push-sourced to receive a batch push, but a monitor- or manifest-fed component will be overwritten again on its next signal.

Accepted status words

Pushes and manifests accept our vocabulary and most others. Matching is case-insensitive.

BecomesAccepted values
operationalok up operational healthy pass passed passing green online available true 1 200 running active good normal
degradedwarn warning degraded degraded_performance slow yellow partial limited unstable
partial_outagepartial_outage partial-outage partially_down
major_outagefail failed failing failure down outage major_outage error red offline unavailable false 0 critical dead unhealthy broken
maintenancemaintenance under_maintenance scheduled

JSON booleans work too (true → operational, false → major outage). Numbers 200–399 and 1 mean operational, and any other number means major outage.

Going quiet: pushTtlSec

Set pushTtlSec (60 s – 7 days) on a push component and it becomes unknown (“No data”) when no push arrives for that long. The status message reads “No status push received for N minutes”. Without a TTL, the last pushed status stays until the next push.

Blip protection: confirmations and observedAt

A status that is worse than the current one only shows after confirmations consecutive fresh reports agree (default 2, set per component; 1 = immediate). A good report in between discards it, so a 503 that is gone by your next check never reaches the page or your subscribers. Improvements always apply at once.

  • Send observedAt (ISO time of the underlying check) with each status. Re-sending the same observation, for example when you push your whole state after an unrelated job, does not count as a new confirmation.
  • Send "confirmed": true when you already debounced on your side and want the status to show immediately.
  • Manifest monitors count each check as one observation.
  • While a worse status is pending, further bad reports keep the worst one seen. unknown (no data) isn't treated as worse, so it applies immediately.
{ "statuses": { "maps": { "status": "major_outage", "message": "503 upstream", "observedAt": "2026-10-09T12:00:00Z" } } }

Pushes and incidents

When a pushed or manifest-fed component reaches the page's incidentMinStatus (partial_outage by default, configurable to degraded or major_outage), an automatic incident opens on its page (if autoIncidents is on). Several components failing within 30 minutes join the same incident. When a component drops back below that threshold, it is marked recovered in the incident, and the incident resolves once nothing in it is failing. maintenance never opens an incident. Every change emits component.status_changed.

Manifest monitors

A manifest monitor fetches one JSON document on every check and turns it into many component statuses. It suits a /health endpoint that reports each dependency, a scraper fleet that reports each platform, or a third party's Statuspage.io feed.

curl -X POST https://upbutler.com/api/v1/monitors \
  -H "Authorization: Bearer $UPBUTLER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Platform health",
    "kind": "manifest",
    "url": "https://api.example.com/status.json",
    "headers": { "authorization": "Bearer monitoring-token" },
    "intervalSec": 60
  }'

Bind components to it with "manifest": {"monitorId": "mon_…", "key": "reddit"}. The monitor itself is up when the document parses and contains at least one component, degraded when it is valid JSON with nothing recognizable, and down on HTTP errors, invalid JSON or timeouts. When the manifest is down, its components show unknown, because we can't tell. A key missing from the manifest also shows unknown.

Accepted shapes

{
  "reddit": "ok",
  "twitter": "fail",
  "youtube": { "status": "degraded", "message": "rate limited" }
}
  • Container: the first of components, services, checks, platforms or status_components that exists, either as a map or an array. Without one, the top-level object is the map (ignoring its status, updated_at and page keys), or the top-level array is the list.
  • Array items are keyed by key, id, name, slug or component (first one found). If the item also has a name, it is matched by name as well.
  • Values are a status word, a boolean or a number, or an object whose status is read from status, state, health, ok, healthy or up. A message or description becomes the component's status message.

Key matching

Every entry is registered under its key exactly and under a slugified form: lowercased, with runs of other characters turned into -. So “Webhooks Delivery” can be bound as webhooks-delivery, and “API” as api. When binding, use either the exact key from the document or its slug.

curl -X POST https://upbutler.com/api/v1/monitors \
  -H "Authorization: Bearer $UPBUTLER_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "GitHub (upstream)", "kind": "manifest", "url": "https://www.githubstatus.com/api/v2/components.json"}'

# Then mirror one of its components on your page (key = slug of the component name)
curl -X POST https://upbutler.com/api/v1/pages/acme/components \
  -H "Authorization: Bearer $UPBUTLER_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "GitHub Actions", "group": "Third parties", "manifest": {"monitorId": "mon_0n3q8kz1m4hx7c2v9rt", "key": "actions"}}'