Skip to documentation
InBrief

Public status API

Read current status and the incident ledger as JSON. No key required

No API key

On this page7

A status page is public information by design, so reading it is public too. These two endpoints need no authentication or key: they are the same data your public page itself renders.

Writing is a different matter: see the Incident API for creating and updating incidents. For a rendered badge or an RSS feed rather than JSON, see Badges & feeds.

Base URL

Your own page's hostname, not ours: your subdomain, or your custom domain once it is live:

Request
https://acme.inbrief.sh/api/public/{slug}

The slug appears twice, and it must be the slug belonging to that hostname. Asking one page's host for a different page's slug returns 404 rather than that page's data.

GET/summary

Current state and uptime history for every service on the page.

curl https://acme.inbrief.sh/api/public/{slug}/summary
const response = await fetch('https://acme.inbrief.sh/api/public/{slug}/summary');
if (!response.ok) throw new Error(`InBrief responded ${response.status}`);
const summary = await response.json();
Response
{
  "slug": "acme",
  "lastChecked": "2026-08-20T21:02:09.028Z",
  "monitoringPaused": false,
  "services": [
    {
      "name": "Checkout API",
      "names": {
        "en": "Checkout API",
        "ar": "واجهة الدفع"
      },
      "url": "",
      "icon": "https://status.example.com/icons/checkout.svg",
      "slug": "checkout-api",
      "status": "up",
      "uptime": "99.98%",
      "uptimeDay": "100.00%",
      "uptimeWeek": "99.95%",
      "uptimeMonth": "99.98%",
      "uptimeYear": "99.98%",
      "time": 142,
      "timeDay": 138,
      "timeWeek": 141,
      "timeMonth": 142,
      "timeYear": 142,
      "dailyMinutesDown": {
        "2026-08-14": 7
      },
      "dailyHealth": {
        "2026-08-14": {
          "rate": 0.486
        }
      }
    }
  ]
}
FieldNotes
urlRetained as an empty string for compatibility. Monitor destinations and request credentials are private, including in history and response-time representations.
lastCheckedWhen the most recent check on this page ran. null if nothing has been checked yet.
monitoringPausedtrue when the page is up but checks are not running, which means an account with no active subscription. Uptime figures stop advancing; they are not wrong, just no longer moving.
name / namesThe service name in the page's default language, and in every language it publishes. Use names if you render more than one.
slugThe stable identifier. Use this to match a service across calls, never the name, because names get edited and translated.
status"up" or "down".
uptime*Percentages, as preformatted strings including the %.
time*Average response time in milliseconds, as numbers.
dailyMinutesDownMinutes down per day, keyed YYYY-MM-DD. Days with no downtime are absent, not zero.
dailyHealthPer day, the failed-check percentage as rate (0–100). A day absent from this map was not measured, which is not the same as a day with no failures, and your own rendering should keep them distinct.

How far back the daily maps go follows the page's own history window (7 to 90 days, see Branding).

?include=response-time

Ask for the daily response-time trend as well. Without it the response uses the default service fields; any other include value is a 400 rather than a silent fallback to the default.

curl https://acme.inbrief.sh/api/public/{slug}/summary?include=response-time
const response = await fetch('https://acme.inbrief.sh/api/public/{slug}/summary?include=response-time');
if (!response.ok) throw new Error(`InBrief responded ${response.status}`);
const summary = await response.json();
Response
{
  "responseTime": {
    "windowDays": 30,
    "averageMs": 142,
    "points": [
      {
        "day": "2026-08-18",
        "averageMs": 138
      },
      {
        "day": "2026-08-19",
        "averageMs": 151
      }
    ]
  }
}

This object is added to each service. What it measures, exactly:

FieldNotes
windowDaysThe page's own history window, the same 7–90 days the daily maps cover.
averageMsThe whole period's average, weighted by how many measurements each day actually took, not an average of the daily averages, which would let a quiet day count as much as a busy one. null when the period measured nothing.
pointsOne entry per day that measured something, oldest first, each a day (YYYY-MM-DD) and that day's averageMs, a whole number of milliseconds. Days with nothing to average are absent, not zero.

Only completed outbound checks count. A check inside an announced maintenance window is excluded, exactly as it is excluded from uptime; a cron heartbeat has no outbound duration to measure at all and always reports an empty series. A service with nothing eligible in the period returns "averageMs": null with no points, never 0, which on a latency figure would read as "instant" rather than "not measured". History begins when this representation was first published: earlier days were never recorded in a form that could be separated from maintenance, and they are left out rather than estimated.

GET/incidents

The incident ledger: open and resolved, incidents and scheduled maintenance alike.

curl https://acme.inbrief.sh/api/public/{slug}/incidents
const response = await fetch('https://acme.inbrief.sh/api/public/{slug}/incidents');
if (!response.ok) throw new Error(`InBrief responded ${response.status}`);
const ledger = await response.json();
Response
{
  "slug": "acme",
  "incidents": [
    {
      "id": "inc_example_01",
      "type": "incident",
      "services": [
        "checkout-api"
      ],
      "startedAt": "2026-08-14T09:00:00.000Z",
      "resolvedAt": "2026-08-14T09:41:00.000Z",
      "publishedAt": "2026-08-14T09:00:10.000Z",
      "scheduledEndAt": null,
      "maintenanceState": null,
      "title": {
        "en": "Elevated error rates"
      },
      "summary": {
        "en": "We are investigating elevated error rates."
      },
      "updates": [
        {
          "id": "update_example_01",
          "status": "identified",
          "body": {
            "en": "A failover has been triggered."
          },
          "createdAt": "2026-08-14T09:12:00.000Z"
        }
      ]
    }
  ]
}
FieldNotes
type"incident" or "maintenance".
servicesMonitor slugs this incident was attached to. Empty when it was not attached to any.
resolvedAtnull while the incident is still open.
title / summaryKeyed by language code. Only languages the page publishes are present.
updatesOldest first, because it is a timeline. Each carries a status of investigating, identified, monitoring or resolved. Empty for an incident posted without updates.

GET/history

One past window of the page, the same data the archive on your status page draws. ?until= is required: the last day of the window, as YYYY-MM-DD in UTC. The window is as long as the history your page displays, and windowDays says how long that is.

Request
https://acme.inbrief.sh/api/public/{slug}/history?until=2026-09-30
FieldNotes
daysThe UTC days in the window, oldest first.
earliestDayThe first day the page was monitored. You cannot ask for a window that ends before it.
servicesEach service's uptime over the window, counting monitored days only, with dailyMinutesDown and dailyHealth keyed by day. ?include=coverage adds coverage and dailyCoverage.
incidentsIncidents that started inside the window, in the same shape as the incident ledger above.

A missing, malformed or future until, or one before earliestDay, answers 404. Any other include value answers 400. A window that ended before today is cached with public, max-age=300, s-maxage=1800; one that ends today with public, max-age=60.

GET/incidents/{id}/postmortem

The published postmortem for one incident, with its body rendered as HTML in html, plus its title, startedAt, resolvedAt, publishedAt and publishedUpdatedAt. ?lang= asks for a language. If the postmortem is not written in it, you get the version in its original language or the page's default instead: renderedLanguage says which, and usedFallback is true.

Request
https://acme.inbrief.sh/api/public/{slug}/incidents/{id}/postmortem?lang=ar

An incident without a published postmortem answers 404, and so does every incident on a page that does not publish postmortems. The answer is cached with public, max-age=60.

Caching

The summary and the incident ledger answer with cache-control: public, max-age=60. Poll no faster than that; a shorter interval gets you the same response from a cache, not fresher data.

The ?include=response-time representation is cached differently, because it is what the status page itself reads on a timer: cache-control: public, max-age=0, s-maxage=30, must-revalidate with a strong ETag. Send it back as If-None-Match and an unchanged page answers 304 with no body. The two representations are separate cache entries; asking for one never returns the other.

If you want push rather than poll, webhooks deliver these same events to your endpoint as they happen.

Errors

StatusMeans
400An include value the endpoint does not support.
404The slug in the path does not belong to the hostname you asked, or no such page exists. Body is {"error": "Not found"}. The history and postmortem endpoints also use it for the cases described above.

There is nothing else to handle. No authentication means no 401, and no plan gate means no 403.