Skip to main content
This is the canonical webhook payload: the body you receive when no Handlebars template is set. If you’ve templated the body, this is still the variable set you interpolate from.

Headers

Every POST carries:

Top-level body

event
string
required
incident.opened, incident.acknowledged, incident.resolved, or test. Always present.
occurred_at
string (ISO 8601)
required
When the event happened, UTC, millisecond precision.
org
object
required
The workspace that owns the monitor.
monitor
object
required
The monitor the event is about. Always present. For test, it’s a synthetic demo monitor.
incident
object | null
The incident this event belongs to. Present on incident.opened, incident.acknowledged, and incident.resolved. null on test.
check
object | null
The latest check result behind the event. Present on incident events, null on test.

org

org.id
string (UUID)
required
Stable identifier for the workspace.
org.name
string
required
The workspace name as shown in the dashboard.
org.slug
string
required
The URL-safe slug. Resolves to <slug>.tallwatch.com for the public status page.

monitor

monitor.id
string (UUID)
required
Stable identifier for the monitor. Use this to correlate the opened and resolved events for one outage.
monitor.name
string
required
The display name the user gave the monitor.
monitor.type
string
required
One of http, tcp, ping, dns, ssl, keyword, heartbeat.
monitor.url
string
Present for http and keyword monitors. Omitted for the others, which carry no URL. For protocol-specific details on other types, query the API rather than expecting them here.

incident

incident.id
string
required
Identifies this specific incident event. The opened and resolved events for one outage carry different ids, so use it as a dedup key, not to correlate the two. The same id is never delivered to the same channel twice.
incident.status
string
required
open, acknowledged, or resolved. Tracks event: open on incident.opened, acknowledged on incident.acknowledged, resolved on incident.resolved.
incident.opened_at
string (ISO 8601)
required
When the incident first opened. Present on both events.
incident.acknowledged_at
string (ISO 8601) | null
When a user acknowledged in the dashboard, or null if no one has yet. Set on the incident.acknowledged event and carried through on the resolved event.
incident.resolved_at
string (ISO 8601) | null
When the incident resolved. null on incident.opened, set on incident.resolved.
incident.duration_sec
number | null
Seconds between opened_at and resolved_at. null on incident.opened.
incident.failing_regions
string[]
Regions that voted the monitor down when the event fired. Codes are fra1, iad1, hil1, sgp1, syd1, gru1. Empty on incident.resolved.

check

check.last_response_ms
number | null
Latency of the latest probe, in milliseconds, or null if none recorded.
check.last_error_class
string | null
A stable error category like timeout, dns_resolve, tls_cert_invalid, or http_status. null on resolve.
check.last_error_message
string | null
A human-readable detail string. null on resolve.

Example: incident.opened

Example: incident.resolved

Example: test

Fires when you click Send test alert on the channel form. The monitor is a synthetic demo, and incident and check are null, so your receiver can branch on event === "test" and short-circuit.

Deduplicating on your side

alert_dispatches guarantees Tallwatch sends each (incident_event_id, channel_id) once. But an inline retry after your receiver times out mid-response can still land the same POST twice, so make your receiver idempotent.
  • Cheap: keep the last N incident.id values in a TTL’d set and reject repeats.
  • Proper: upsert on incident.id so any number of retries converge to one row.
The proper option is the right shape for production.

With a Handlebars template

The body becomes whatever your template renders. The variables mirror this shape ({{monitor.name}}, {{incident.failing_regions}}, and so on). See Templates.