Loading…
Loading…
An incident timeline carries the vendor’s lifecycle and yours, never merged. This page says what each field means, what the API refuses, and how an incident becomes a ticket.
| Object | Fields | Who writes it |
|---|---|---|
upstream | status (open, monitoring, resolved), max_severity, affected_components, first_seen_at, last_update_at, resolved_at, and for maintenance scheduled_for, scheduled_until | The vendor, through polls and verified email. Read-only. |
ours | status (unreviewed, investigating, affected, cleared), customer_impact (unknown, suspected, confirmed, not_observed), impact_note, reviewed_by, acknowledged_at, muted_until | A person in the app, or an agent through the API. |
open_for_us is computed from ours alone: not cleared by us and not snoozed. A vendor marking an incident resolved changes upstream.status and nothing else. When a vendor reopens an incident you had cleared, it comes back as unreviewed; while they only post updates, your status stands.
customer_impact: confirmed or suspected needs an impact_note that names the evidence: the log line, the metric, the alert, the user report, with time and source. The API refuses the patch otherwise.not_observed deserves a note saying what you checked.unknown for you until checked.curl -X PATCH https://api.changelog.observer/v1/incidents/<incident_id> \
-H "Authorization: Bearer <key>" -H "Content-Type: application/json" \
-d '{"our_status": "affected", "customer_impact": "confirmed",
"impact_note": "Checkout 5xx from 10:05 UTC in Datadog, service payments-api", "acknowledge": true}'POST /v1/projects/{id}/incidents/bulk applies one action to many timelines: acknowledge, affected (sets confirmed impact; add the evidence after), cleared, not_observed, mute_24h, mute_7d. A mute belongs to the dependency: new events from it go to the digest only, for the period. A snooze (muted_until on the incident) hides it from “open for us” for the period.
A timeline with scheduled_for is a maintenance window. The dashboard lists them under Incidents → Upcoming maintenance, soonest first, with a planned, in progress or completed state; the API lists them with GET /v1/workspace/maintenance and the per-project equivalent, fourteen days ahead by default (from and to widen it). They never appear in the incident lists.
GET /v1/incidents/{id}/summary?format=text|markdown composes one block of text for a ticket: a title (“Stripe: Elevated API errors”), both lifecycles, project and dependency, first seen and last update, the dashboard link, your impact note, every update in the vendor’s own words oldest first (repeats left out, capped at twenty), and a closing line saying customer impact is never inferred. Vendor text is escaped in Markdown so it cannot become links or headings. The dashboard’s incident page has “Copy as text” and “Copy as Markdown” for the same.
First add the ticket system as a destination (once per project, with a destinations:write key or from Rules & destinations in the app):
| Type | config | credential |
|---|---|---|
jira | site (https://acme.atlassian.net), email (the account the token belongs to), project_key, issue_type (default Task) | An Atlassian API token |
linear | team_id | A Linear personal API key |
github_issues | repo (owner/name), labels (optional) | A fine-grained token with Issues read and write on the repository |
statuspage | page_id, status (investigating, identified, monitoring), deliver_notifications (default false) | A Statuspage API key |
curl -X POST https://api.changelog.observer/v1/projects/<project_id>/destinations \
-H "Authorization: Bearer <key>" -H "Content-Type: application/json" \
-d '{"type": "github_issues", "name": "Platform issues",
"config": {"repo": "acme/platform", "labels": ["vendor-incident"]},
"credential": "github_pat_…"}'The credential is sealed at rest (AES-256-GCM under a key derived from the platform secret, bound to the destination) and never returned; credential_set_at says one exists, and PATCH …/destinations/{destId} with credential replaces it. Ticket systems never receive routed events; only an escalation reaches them.
Then, for an incident:
curl -X POST https://api.changelog.observer/v1/incidents/<incident_id>/escalate \
-H "Authorization: Bearer <key>" -H "Content-Type: application/json" \
-d '{"destination_id": "dst_…"}'The response carries system, external_key (PAY-123, ENG-42, #17, a Statuspage incident id), external_url and existing. One escalation per incident and destination: calling again returns the first one. The body is the summary above, as text for Jira (as an Atlassian document) and Statuspage, as Markdown for Linear and GitHub; format overrides. A refusal from the vendor comes back as 502 escalation_failed with their status and the first words of their answer. Every escalation is audited and listed on the incident (escalations) and in the dashboard’s Ticket column.
Agents: the MCP tool escalate_incident exists, and its description tells the agent to use it only when you asked for that incident to go to that system. See the agent guide.