Incident and maintenance lifecycle
States, transitions, and the events fired on each.
Observer treats incidents and maintenances as instances of the same
underlying row (the updates table). The lifecycle state is derived
from a grid of timestamp columns rather than an explicit state column;
this keeps the model coherent regardless of whether the transition
happened via API, server action, or cron.
States
| State | Trigger |
|---|---|
draft | Row exists but published_at IS NULL. Invisible to the public. |
published | published_at set. Renders on the public page. |
resolved | resolved_at set. Final lifecycle for an incident. |
scheduled | Maintenance with scheduled_start_at set, actual_start_at NULL. |
in_progress | Maintenance with actual_start_at set, actual_end_at NULL. |
completed | Maintenance with actual_end_at set. |
canceled | Either type with canceled_at set. |
deleted | Soft-delete via deleted_at. Permanent; not displayed anywhere. |
Transitions
draft
│ POST /publish
▼
published ──── POST /resolve ──▶ resolved
│
│ DELETE
▼
deleted
(maintenance only)
scheduled ── cron @ scheduled_start_at ──▶ in_progress
│ cron @ scheduled_end_at
▼
completed
any state ── POST /cancel ──▶ canceled
Webhook events
Each transition fires a webhook event. See Webhook payload reference for exact body shapes.
| Event | Fires when |
|---|---|
incident.created | Row inserted (regardless of draft / publish). |
incident.published | published_at set. |
incident.updated | Title, severity, or affected services patched. |
incident.message_added | Message appended to timeline. |
incident.resolved | resolved_at set. |
incident.deleted | deleted_at set. |
maintenance.scheduled | Row inserted with scheduled_start_at. |
maintenance.starting_soon | Cron fires within 1h of scheduled_start_at. Once per row. |
maintenance.started | actual_start_at set (manual or cron). |
maintenance.completed | actual_end_at set. |
maintenance.canceled | canceled_at set. |
Auto-message side effects
Some lifecycle transitions append a system message to the timeline:
maintenance.started(cron or API) appends an Information message: "Maintenance started."maintenance.completed(cron or API) appends a Resolved message: "Maintenance completed."maintenance.canceled(API) appends an Information message: "Maintenance canceled."
These are visible on the public page exactly like operator-authored messages. They exist so the timeline reflects every state change without requiring the operator to remember.
Was this page helpful?