Skip to content

Subscriptions - alert vs report

View as Markdown

A subscription is the link between one monitor and one contact. Creating a contact doesn’t alert anyone on its own, and neither does creating a monitor - a subscription has to exist between the two, and it decides which events actually get delivered.

Think of it as a grid: monitors on one axis, contacts on the other, and at each intersection a set of events that contact hears about for that monitor. There are two independent kinds of subscription on that same pair - alert (event-driven) and report (scheduled) - and a contact can hold both at once.

An alert subscription can fire on:

  • Down - the monitor just failed a confirmed check and a new incident opened.
  • Up - the monitor recovered and the incident closed.
  • Still-down (repeatedlyDown) - the outage is continuing. Instead of one Down alert and then silence for the whole outage, a still-down subscription fires again on every confirmed still-down check - roughly once per monitoring interval - for as long as the monitor stays down, so a long outage doesn’t go quiet. The contact’s alert delay only controls when the first Down alert reaches that contact, not how often still-down repeats.

A report subscription is scheduled rather than event-driven, and delivers a periodic uptime summary regardless of whether anything went wrong: Daily, Weekly or Monthly in the app’s own editor. The API additionally recognizes Quarterly and Yearly as distinct frequencies, but the app doesn’t offer them as separate choices - picking Monthly in the UI actually subscribes the contact to Monthly, Quarterly and Yearly together (so nothing is missed if a longer-period report ever gets scheduled). Reports can only be delivered to email contacts - subscribing an SMS, voice, chat or webhook contact to a report is refused by the API (422 unsupported_report_channel), not silently ignored.

Setting API field (v2) Type / allowed values Default Plan limits What it does for you
Alert types alertTypes (on the PUT body) Subset of up, down, repeatedlyDown None (no subscription exists until written) - Which events this pair fires on - replaces the set for that pair, it’s not a diff
Report frequencies frequencies (on the PUT body) Subset of daily, weekly, monthly, quarterly, yearly None Email contacts only Which scheduled reports this pair produces - also replaces the whole set

There’s no separate “enabled” flag - a pair with an empty event set simply has no subscription row, and setting one writes it; setting an empty set is the same as unsubscribing that pair (DELETE).

  • On a monitor, open its editor and use the Alert Subscriptions / Report Subscriptions groups to see and change which contacts are subscribed to it.
  • On a contact, use subscribe monitors to a contact to see and change which monitors it’s subscribed to, including bulk actions.
  • To subscribe several contacts to a monitor at once, use a contact group.

Every subscription is addressable from either side - by monitor or by contact - and both doors write the same underlying pair, so it doesn’t matter which one a client uses.

Set (replace) the alert types for one monitor/contact pair:

PUT /monitor/{monitorId}/alert/{contactId}
{ "alertTypes": ["down", "up"] }
PUT /monitor/{monitorId}/report/{contactId}
{ "frequencies": ["weekly"] }

The contact-side mirror is identical: PUT /contact/{contactId}/alert/{monitorId} / .../report/{monitorId}. Remove one pair with DELETE on the same path, or every alert subscription a monitor (or contact) has with DELETE /monitor/{monitorId}/alert (no {contactId}) - a DELETE on a pair that has no subscription answers 404, never a silent no-op success.

For many pairs at once, use the diff door instead of looping PUT:

POST /alert/bulk
{
"create": [ { "monitorIds": ["<id1>", "<id2>"], "contactIds": ["<cid>"], "alertTypes": ["down"] } ],
"delete": [ { "monitorIds": ["<id3>"], "contactIds": ["<cid>"], "alertTypes": ["up"] } ],
"allMonitors": false
}

allMonitors/allContacts let a create/delete entry mean “every monitor/contact I own” instead of an explicit list, without enumerating them (and without counting toward the row cap below). POST /report/bulk is the identical shape with frequencies instead of alertTypes. Both return {created, deleted, unchanged, pairs}.

To read the current state: GET /monitor/{monitorId}/alert (every contact subscribed to that monitor, with its alert-type set) and GET /contact/{contactId}/alert (every monitor that contact hears about) - and the report twins. GET /alert and GET /report list the account’s subscriptions flat, unfiltered by either side.

MCP tools: subscribe_contact (pass alertTypes and/or frequencies as comma-separated strings, e.g. alertTypes="down,up"; each replaces that leg for the pair), unsubscribe_contact (kind: alert|report|both), list_subscriptions. There is no curated tool for POST /alert/bulk or POST /report/bulk: call them through api_request with confirmed=true - the MCP server refuses every POST/PATCH/PUT on a /bulk path without it and sends nothing (these two doors have no dry run). Show the user the pairs first, then send:

api_request(method="POST", path="/alert/bulk", confirmed=true,
bodyJson="{\"create\":[{\"monitorIds\":[\"M1\",\"M2\"],\"contactIds\":[\"C1\"],\"alertTypes\":[\"down\",\"up\"]}]}")

Or repeat subscribe_contact once per monitor and contact pair.

A subscription takes effect immediately - the very next confirmed Down/Up/still-down on that monitor is evaluated against every subscribed contact’s own alert delay, active hours and grouping settings. Nothing is retroactive: subscribing mid-outage doesn’t deliver a Down alert for an outage that already started, but it does mean the contact joins the still-down/escalation cycle from that point on, and the eventual Up alert will reach it (since it “already knew” about the outage).

  • An unconfirmed or overlimited contact is silently dropped from delivery, even with a subscription in place - see what a contact is for the confirmation gate.
  • 422 unsupported_report_channel - trying to subscribe a non-email contact to reports.
  • 422 validation_failed (reason conflict) - the same monitor/contact pair appears in both create and delete of one bulk call.
  • The bulk diff door caps a request at 2,000 enumerated rows (monitors x contacts x types); using allMonitors/allContacts for a whole-account wildcard doesn’t count against that cap.
  • PUT always replaces the event set for a pair - to add one event without disturbing the others, read the current set first and send the union back.