PosthasteDocsGet an API key

Reference

Status feed

The status page in machine-readable form, so you can surface our status inside your own dashboard instead of asking somebody to keep a browser tab open. No API key, no rate limit, no versioning.

The endpoint

GET https://posthastemail.dev/status/data

{
  "overall": "degraded",
  "components": [
    {
      "id": "api",
      "name": "API",
      "description": "Accepting sends and serving the REST API.",
      "state": "operational",
      "note": null
    },
    {
      "id": "smtp",
      "name": "SMTP submission",
      "description": "Accepting mail submitted over SMTP.",
      "state": "operational",
      "note": null
    },
    {
      "id": "outbound",
      "name": "Outbound delivery",
      "description": "Delivering accepted mail to recipient mail servers.",
      "state": "degraded",
      "note": "Delivery is running behind. Mail is queued, not lost."
    },
    {
      "id": "inbound",
      "name": "Inbound mail",
      "description": "Receiving bounces, complaints and replies.",
      "state": "operational",
      "note": null
    },
    {
      "id": "webhooks",
      "name": "Event webhooks",
      "description": "Posting delivery events to your endpoints.",
      "state": "operational",
      "note": null
    }
  ],
  "checkedAt": "2026-08-25T20:54:28.952Z",
  "stale": false
}
It is served from posthastemail.dev and not from api.posthastemail.dev, which is deliberate and is the whole reason it is usable. A status endpoint that lives on the API host goes down with the API — exactly when you need it. This one is served by a different process on a different container, so when the API is unreachable this feed keeps answering and tells you so.

Fields

FieldMeaning
overalloperational, degraded, partial_outage or major_outage. A roll-up of the components, nothing more — read the components if you want to know what is actually wrong.
components[].idStable and safe to key on: api, smtp, outbound, inbound, webhooks. New ids may be added; existing ones will not be renamed.
components[].name, .descriptionHuman-readable, and free to change. Display them if you like, but do not match on them.
components[].stateOne of the four states below. Handle an unrecognised value as unknown rather than as healthy.
components[].noteA short sentence, or null. Drawn from a fixed vocabulary — never an error message, and never anything derived from an exception.
checkedAtRFC 3339, UTC. When the underlying checks last completed — not when you made this request. Compare it against your own clock and stop trusting the payload once it stops moving.
staletrue when our own checks have stopped reporting. Every component reads unknown in that case, because stale good news is worse than an admission of ignorance.

What the four states mean

Green and red alone would lie about this platform. Accepting mail and delivering it are different jobs done by different processes, and the outage customers actually feel is the quiet one where the first keeps working perfectly and the second has stopped — every process alive, every health check passing, and nothing arriving. That is what degraded is for.

StateWhat it means for you
operationalWorking normally, and confirmed within the last few seconds.
degradedStill working, but not properly. On outbound this almost always means we are accepting your mail and delivering it late: the queue is not draining, so messages you sent are sitting waiting rather than being refused or lost. On webhooks it means the same thing about events reaching your endpoint. Nothing is dropped. Nothing needs re-sending — a retry here would duplicate a message that is still on its way.
outageNot working. Anything already accepted stays queued and goes out on recovery, so a send that returned 202 is still our responsibility. New sends against a component in this state will fail.
unknownWe cannot currently tell. Published as its own state rather than guessed at in either direction — usually because a component has not reported since a deploy, or because our own checks have stopped completing.
Do not treat unknown as healthy. It is the state that exists so we never have to round a gap in our knowledge up to green, and collapsing it back to operational in your own code undoes that.

Polling it

// Poll no faster than every 30 seconds. The verdict behind this feed is
// recomputed on a fixed clock, so a tighter loop returns the same bytes and
// only costs you requests.
const res = await fetch('https://posthastemail.dev/status/data')
const status = await res.json()

// Treat anything that is not 'operational' as "tell somebody".
// 'unknown' is deliberately NOT the same as 'operational' — it means we
// could not tell, and silently rounding it up to healthy is how a
// dashboard ends up green through an outage.
const unhappy = status.components.filter((c) => c.state !== 'operational')

// Old data is not current data. If the feed stops moving, say so rather
// than continuing to display whatever it last said.
const ageMs = Date.now() - Date.parse(status.checkedAt)
if (status.stale || ageMs > 120_000) {
  showBanner('Posthaste status is not currently reporting')
}
PropertyValue
AuthenticationNone. Sending an API key is harmless and achieves nothing.
Rate limitNone. The response is a cached snapshot, so it costs us the same whether one person reads it or a hundred thousand do — and throttling a status feed during an incident would turn one problem into two.
Cachings-maxage=10, stale-while-revalidate=30. Expect a verdict up to about twenty seconds old; the checks behind it run on a fixed clock regardless of traffic.
Recommended interval30 seconds. Faster returns the same bytes.
Cross-originAllowed from any origin, so a browser dashboard can read it directly. There is no credential on this response and there never will be.

What it does not cover

Everything here is platform-wide. Delivery to one particular recipient domain can fail while every component is green — receivers defer and block for their own reasons — and no platform status feed can tell you about that. For a specific message, read its waybill through the messages API, which records every attempt and the exact response the receiving server gave.

There are also no incident histories, uptime percentages or scheduled-maintenance windows in this feed. It reports what is true right now, measured, and nothing that would require somebody to have remembered to type it in.

NextErrorsEvery error type the API returns, what causes it, and whether to retry.