> ## Documentation Index
> Fetch the complete documentation index at: https://docs.use.observer/llms.txt
> Use this file to discover all available pages before exploring further.

# Incident and maintenance lifecycle

> States, transitions, and the events fired on each.

Incidents and maintenances share one lifecycle. State is derived from
timestamp fields, identically whether the transition came from the
API, the console, or a scheduled job. Transitions are idempotent.

## 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` | Maintenance with `canceled_at` set. |
| `deleted` | Soft-delete via `deleted_at`. Permanent; not displayed anywhere. |

## Transitions

```text theme={null}
       draft
         │ POST /publish
         ▼
     published ──── POST /resolve ──▶ resolved
         │
         │ DELETE
         ▼
       deleted

  (maintenance only)
   scheduled ── automatically at scheduled_start_at ──▶ in_progress
                                                              │ automatically at scheduled_end_at
                                                              ▼
                                                           completed

   scheduled or in_progress ── POST /cancel ──▶ canceled (maintenance only)
```

## Webhook events

Each transition fires a webhook event. See
[Webhook payload reference](https://docs.use.observer/docs/reference/webhook-payloads.md) 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. Carries `published: false` on a draft; subscribers and alert channels are not notified for drafts. |
| `incident.resolved` | `resolved_at` set. |
| `incident.deleted` | `deleted_at` set. |
| `maintenance.scheduled` | Maintenance published (created published, or a draft published later). |
| `maintenance.starting_soon` | A scheduled job fires within 1h of `scheduled_start_at`. Once per row. |
| `maintenance.started` | `actual_start_at` set (manually or automatically at `scheduled_start_at`). |
| `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` (automatic or API) appends an Information message: "Maintenance started."
* `maintenance.completed` (automatic 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.

> [!NOTE]
>
> **Idempotency**
>
> Lifecycle transitions reject double-application: `POST /publish`
> on an already-published incident returns 409
> (`already_published`). Same for resolve, start, complete, cancel.
> Soft-delete returns 200 (idempotent).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.