---
title: "Service Status"
description: "Public, unauthenticated endpoint that returns Uptimeify's own platform availability — check and alert delivery percentages, component states, and the hash-chained frozen monthly totals."
---

`GET /api/service-status`

No authentication required. Returns no customer data — only the platform's own delivery figures, published component states, and the frozen monthly totals behind them.

## Example (cURL)

```bash
curl -X GET "https://uptimeify.io/api/service-status" \
  -H "Accept: application/json"
```

## Response

```json
{
  "checkDelivery": {
    "pct": 99.97,
    "coveragePct": 99.94,
    "insufficientData": false,
    "windowDays": 90
  },
  "alertDelivery": {
    "pct": 97.31,
    "sampleSize": 2454,
    "endpointFailures": 140,
    "latencyP90Ms": 1727,
    "latencySampleSize": 812,
    "insufficientData": false,
    "latencyInsufficientData": false
  },
  "components": [
    { "key": "de-fra", "displayName": "Frankfurt (DE)", "state": "operational", "coveragePct": 99.94 },
    { "key": "alerting", "displayName": "Alert Delivery", "state": "operational", "coveragePct": 100 }
  ],
  "frozenMonths": [
    {
      "month": "2026-06-01",
      "sliKind": "check_delivery",
      "scopeKey": "mesh",
      "numerator": 4318204,
      "denominator": 4319850,
      "unknownUnits": 612,
      "rowHash": "9f2c1a7e4b8d0f3a6c5e2b1d8a7f4e9c0b3d6a1f8e5c2b9d7a4f1e8c5b2d9a6f"
    },
    {
      "month": "2026-06-01",
      "sliKind": "measurement_coverage",
      "scopeKey": "mesh",
      "numerator": 43176,
      "denominator": 43200,
      "unknownUnits": 24,
      "rowHash": "4d1b8e0cc0a2f7593e6b4c1d8a05f2e7b9c3d6a1f8e5c2b9d7a4f1e8c5b2d9a6"
    }
  ]
}
```

Notes:

- `checkDelivery.pct` and `alertDelivery.pct` are trailing `windowDays` figures (currently 90 days), truncated to two decimals — never rounded up.
- **`unknownUnits` is NOT in the denominator of any percentage on this endpoint.** A percentage is measured over what the platform could actually account for; `denominator` is the expectation over the recorded units and `unknownUnits` is the expectation over the units that were never recorded — a gap in the per-minute recorder, a stalled rollup. The two are separate axes because "we were unavailable" and "we could not measure" are different facts: folding the second into the first would publish an outage that did not happen and make the figure a measure of the recorder's own reliability.
- **`coveragePct` is the second half of every claim and is never omitted.** It is the share of the window the platform can account for, `0`-`100`, truncated to two decimals — `null` only when nothing was owed in the window yet. `checkDelivery.coveragePct` covers the whole mesh; `components[].coveragePct` is per component and is `null` for a component that has never been measured at all. For the conservative reading — the share of the window positively confirmed as delivered — multiply the two: `pct x coveragePct / 100`.
- From any `frozenMonths` row you can recompute coverage yourself as `denominator / (denominator + unknownUnits)`. Rows with `"sliKind": "measurement_coverage"` state it directly in minutes instead: `numerator` is the minutes recorded, `denominator` the minutes owed. That kind exists because a day on which the recorder never ran leaves `check_delivery` at `0/0` — there is no observed rate to price a missing check-minute with — so only a minute-based row keeps such a day visible.
- **Alert delivery is two figures, and neither is a 60-second threshold.** `alertDelivery.pct` is the share of attempted deliveries that **eventually got through**, and `alertDelivery.latencyP90Ms` is the p90 time from the incident opening to the first notification that actually left our systems, in milliseconds. The former answers "did it arrive", the latter "how fast" — one number cannot answer both, and the single figure published before this (the share dispatched inside 60 seconds) answered neither: at a median dispatch under one second it could not tell a fast delivery from one that took most of a minute, and it scored a delivery that never arrived exactly like a slow one.
- **`alertDelivery.pct` counts each delivery once, not each attempt.** The unit is one `(incident, notification channel)` pair; the pair counts as delivered if **any** attempt for it succeeded. A notification that failed twice and got through on the third attempt is therefore one success — counting attempts instead would publish a worse figure the harder our retry mechanism works. `alertDelivery.sampleSize` is the number of such pairs. Where a delivery carries no channel id (an owner-address email, the incident-management bridge) the transport type stands in as its identity.
- **`alertDelivery.pct` counts the failures we are responsible for, and `endpointFailures` counts the ones we are not.** A delivery that never arrived because the receiving endpoint was gone (`404`, `410`), because the credentials you gave us were revoked (`401`, `403`), because your service rate-limited us (`429`), because it returned a `5xx`, or because its host could not be reached at all (DNS failure, refused connection, TLS failure, timeout) is a failure of that endpoint, not of our alerting. Those deliveries leave `pct` — they are in neither its numerator nor its `sampleSize` — and are counted in `alertDelivery.endpointFailures` instead. Failures on our side stay in and count against the figure: a payload one of the chat services rejected as malformed, a channel type our dispatcher had no handler for, our own relay or send-throttle skipping a send, or any failure we cannot confidently attribute to the far end. **The tie-break always goes against us** — anything unattributable counts as ours, because the opposite rule would let the figure improve every time we failed in a new way.
- **`endpointFailures` is never suppressed and never omitted.** It is a count, so it is exactly true at any sample size and carries no publication floor: it is present even when `pct` is `null`. Add it to the failures inside `pct` and you have every alert that did not arrive in the window, for any reason at all — the exclusion above changes which number a failure is reported in, never whether it is reported.
- **What the alert figures exclude, and what they do not.** Incidents that were never owed a notification are outside both: alerting suppressed by a maintenance window, a monitor with no notification channel configured, an incident held back by flap damping, or one that resolved before a notification became due. A delivery whose every attempt **failed on our side** is inside `alertDelivery.pct` and counts against it — a failed send is a delivery failure, not an exclusion. One that failed at your endpoint is in `endpointFailures` instead, per the rule above. It is absent from the p90, because an alert that never left has no dispatch time; charging it one would hide a delivery failure inside a latency figure. `alertDelivery.latencySampleSize` is therefore the number of incidents that were successfully notified, and is always smaller than `sampleSize`.
- **No alert figure reaches back past the date measurement began.** `alertDelivery.pct`, `endpointFailures` and `latencyP90Ms` are all bounded below at the instant our recorder first wrote a sample — the same instant the public status page prints as "Measuring since". Our notification log holds real history from long before that date; it is not counted, because a figure reaching past the start date printed beside it would be a different claim than the one that date supports. The rows stay in the database; they are simply outside the window.
- **Attempts are counted for 24 hours after the incident's day.** A delivery still not through a full day after the incident opened is a failed delivery, whatever happens afterwards. Measured over a real trailing 90 days this bound reclassifies 2 of 2590 delivery pairs, and it reclassifies them against us.
- **Latency is a percentile, so it is neither summed nor frozen.** The published p90 is computed over the window's own distribution, not derived from the daily figures behind it: the sum of daily percentiles is not a percentile, and neither is their weighted mean. For the same reason no `alert_dispatch_latency_p90` row appears in `frozenMonths` — a monthly figure that cannot be computed by summing days is better absent than invented. The daily rows exist in our own evidence table, each carrying its own sample size.
- **A percentage is always published together with its sample size, and is withheld when that sample is too small.** `alertDelivery.pct` and `alertDelivery.latencyP90Ms` are `null` with the matching `insufficientData` / `latencyInsufficientData` flag set to `true` while fewer than **20** events stand behind them. `checkDelivery.pct` is `null` with `checkDelivery.insufficientData` true until our measured series covers **24 hours**: what a minute owes is a per-minute rate and what it delivers is a whole check, so on a short series the figure reads a fraction under 100% with nothing wrong at all. In every case the sample size and `coveragePct` are still published — they are what explains the absence. A `null` with the flag `false` means something different: nothing has been measured yet at all.
- `components[].state` is one of `operational`, `degraded`, `down`, `unknown`. `unknown` means no recent sample exists for that component; the endpoint never fills the gap with the last known state. A component that is `unknown` right now is not a component that is down — check its `coveragePct` to see how much of the window that has been true for.
- `frozenMonths` lists every append-only monthly total from `platform_sli_month`, identified by `month` + `sliKind` + `scopeKey`. `sliKind` is one of `check_delivery`, `component_availability`, `alert_delivery`, `alert_endpoint_failure` or `measurement_coverage` — the dispatch-latency percentile is deliberately not among them (see above). Each row's `rowHash` chains to the previous row for the same `sliKind`/`scopeKey`, and a database trigger rejects any UPDATE or DELETE against the table. If you saved a copy of this response earlier, a `rowHash` that no longer matches for a given `month`/`sliKind`/`scopeKey` means that month's figures were restated after being frozen.

## Common errors

- `500 Unexpected server error` on an internal failure

