Documentation
Alerts
POST as `application/json` to the URL the channel was created with, HTTPS only. Redirects are not followed: a subscriber answering `301` would send the payload, and the signature, to a host the account never authorised. A webhook channel chooses one of three formats when it is created, and this document describes the default. `up24` is the signed payload below. `slack` is an incoming-webhook body and `pagerduty` is an Events v2 call to PagerDuty's endpoint for the service region — neither reads a signature and neither is sent one, because for both of them the URL or the routing key is itself the credential. Everything below is true of the `up24` format; the retry policy, the delivery log and the severity floor are true of all three. An `up24` request carries `x-up24-signature: t=<unix seconds>,v1=<hex hmac>`, an HMAC-SHA256 over `<t>.<raw body>` with the secret shown once when the channel was created. Verify it against the raw bytes, before parsing: re-serialising the JSON changes them. The timestamp is inside the signed string, so a captured body cannot be replayed later under its original signature — reject anything more than 300s out of date. Answer 2xx within 10 seconds. Anything else is a failure and is retried with exponential backoff up to 5 attempts; 10 consecutive failures switch the channel off and say so on the dashboard. Delivery is at-least-once, so de-duplicate on `eventId` — and a test alert carries `eventId: 0` and `test: true`, which means a subscriber keying off that id ignores tests without a special case.
1 endpoint · all GET · no key · samples in curl, Python, JavaScript and Go
GET /v1/notify
How long an alert takes to arrive after the fault started
package main
import (
"encoding/json"
"net/http"
)
func main() {
res, err := http.Get("https://api.up24.app/v1/notify")
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
}The product’s own claim, measured from the delivery log rather than asserted: p50 and p95 of the gap between an incident’s startedAt and the moment the alert about it reached a channel, over the last 30 days, every account and every channel pooled.
Openings only. An alert carries the *incident’s* start, so the same subtraction on a close measures how long the outage lasted rather than how fast anyone was told — a twelve-hour outage would land in this median as twelve hours of notification latency. Escalations are excluded on the same argument, and a retracted incident is out of this count as it is out of every other.
It measures from the fault, not from the decision: the incident engine needs consecutive bad samples before it opens anything, and that detection time is inside these numbers. It also counts only deliveries that actually left — a skip is a row in the log with a reason, but it is not a notification, and counting the ones that never went as instant would flatter the number.
And a backfill is not a notification either. The dispatcher expands every event that has not been dispatched yet, however old, so an event written while there was nobody to deliver to is sent the moment somebody appears — which is history arriving at once, not up24 paging anybody late. An event that waited over an hour in the outbox before a delivery was claimed for it is out of these percentiles. It keeps its row in the delivery log.
Read both percentiles against deliveries. With nothing delivered in the window deliveries is 0 and both are null: "not yet measured" is the answer, and a zero would read as instant.
Responses
200Time to notify over the window.429Over the origin rate limit.
200 returns NotifyResponse
generatedAtstring (date-time)daysintegermin 1
deliveriesintegerHow many delivered openings the two percentiles are over — deliveries only, not the skips and failures the delivery log also records, and not a backfilled event. Zero means nothing has been delivered in the window and both percentiles are
null.p50Msinteger | nullMedian of
delivered_at − startedAtacross every opening alert delivered in the window, over every channel and every account. Includes detection — the engine needs consecutive bad samples before it opens anything — so this is time from the fault, not time from the decision. Excludes any event that waited over an hour in the outbox before a delivery was claimed for it, which is a backfill rather than a notification. Null whendeliveriesis 0.p95Msinteger | nullThe same measurement at the 95th percentile: the slow tail, which is the one worth quoting at a desk. Read it against
deliveries— a p95 over a handful of alerts is that handful’s worst one. Null whendeliveriesis 0.