StackPractices
beginner By Mathias Paulenko

API Status Page Template for Incidents

A template for a public API status page that communicates uptime, incidents, and maintenance windows to consumers.

Overview

A public status page tells your API consumers what is happening before they open a support ticket. It cuts inbound tickets during outages and earns trust through transparency. This template covers the structure, content, and operational practices for running a status page that actually helps.

The template covers:

  1. Page structure — what sections to include and how to organize them
  2. Incident communication — templates for active incidents and post-incident updates
  3. Severity definitions — standardized levels so consumers understand impact
  4. SLA reporting — how to present uptime and response time metrics
  5. Maintenance windows — how to schedule and communicate planned downtime

When to Use

Use this resource when:

  • You run a customer-facing API and consumers open tickets during outages
  • You’re setting up a new public API and want the status page in place before launch
  • Your team posts incident updates ad hoc and the wording drifts every time

Skip it when: the service is internal to a single team — a Slack channel with automated alerts covers it, and a public page advertises availability you never promised. For the underlying resilience work rather than the communication layer, see the incident communication template.

Page Structure Template

<!-- Status Page Layout -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>[Product] Status</title>
    <!-- Must be hosted on a separate domain/infrastructure -->
</head>
<body>
    <header>
        <h1>[Product] Status</h1>
        <p>Real-time status of [Product] services and APIs.</p>
        <!-- Subscribe buttons: Email, RSS, Slack, Webhook -->
        <nav>
            <a href="#current">Current Status</a>
            <a href="#history">Incident History</a>
            <a href="#uptime">Uptime Metrics</a>
            <a href="#maintenance">Scheduled Maintenance</a>
        </nav>
    </header>

    <main>
        <!-- Current Status Section -->
        <section id="current">
            <h2>Current Status</h2>
            <!-- Aggregate status banner -->
            <div class="status-banner operational">
                All systems operational
            </div>
            <!-- Per-service status -->
            <div class="service-list">
                <div class="service">
                    <span class="name">API Gateway</span>
                    <span class="status operational">Operational</span>
                </div>
                <div class="service">
                    <span class="name">Authentication Service</span>
                    <span class="status operational">Operational</span>
                </div>
                <div class="service">
                    <span class="name">Database (Primary)</span>
                    <span class="status operational">Operational</span>
                </div>
                <div class="service">
                    <span class="name">Web Dashboard</span>
                    <span class="status operational">Operational</span>
                </div>
            </div>
        </section>

        <!-- Uptime Metrics Section -->
        <section id="uptime">
            <h2>Uptime (90 days)</h2>
            <div class="uptime-grid">
                <!-- 90 daily bars, colored by status -->
                <div class="uptime-bar operational" title="2026-07-04: 100% uptime"></div>
                <div class="uptime-bar operational" title="2026-07-03: 100% uptime"></div>
                <div class="uptime-bar degraded" title="2026-07-02: 99.2% uptime"></div>
                <!-- ... 87 more bars ... -->
            </div>
            <table class="uptime-table">
                <tr><th>Service</th><th>90-day uptime</th><th>SLA target</th></tr>
                <tr><td>API Gateway</td><td>99.98%</td><td>99.9%</td></tr>
                <tr><td>Authentication</td><td>99.99%</td><td>99.95%</td></tr>
                <tr><td>Database</td><td>99.97%</td><td>99.9%</td></tr>
            </table>
        </section>

        <!-- Incident History Section -->
        <section id="history">
            <h2>Incident History</h2>
            <article class="incident resolved">
                <h3>Database latency spike</h3>
                <p class="date">July 2, 2026 — Resolved</p>
                <p class="severity">Severity: Medium</p>
                <div class="updates">
                    <div class="update">
                        <time>14:32 UTC</time>
                        <p>Investigating elevated database latency on primary cluster.</p>
                    </div>
                    <div class="update">
                        <time>14:45 UTC</time>
                        <p>Identified cause: long-running analytical query blocking writes.</p>
                    </div>
                    <div class="update">
                        <time>15:10 UTC</time>
                        <p>Terminated the blocking query. Latency returning to normal.</p>
                    </div>
                    <div class="update">
                        <time>15:30 UTC</time>
                        <p>Resolved. Latency back to baseline. Adding query timeout to prevent recurrence.</p>
                    </div>
                </div>
            </article>
        </section>

        <!-- Scheduled Maintenance Section -->
        <section id="maintenance">
            <h2>Scheduled Maintenance</h2>
            <article class="maintenance upcoming">
                <h3>Database migration to v16</h3>
                <p class="date">July 15, 2026, 02:00–04:00 UTC</p>
                <p>Impact: Brief API errors during failover (expected < 2 minutes).</p>
                <p>We will fail over to the replica, upgrade the primary, then fail back.</p>
            </article>
        </section>
    </main>
</body>
</html>

Incident Update Templates

Active incident — initial post

## [Incident Title]

**Severity:** [Critical / High / Medium / Low]
**Started:** [timestamp UTC]
**Affected services:** [list]

We are investigating [brief description]. [What users are experiencing].

Next update: [time, typically 15-30 minutes from now].

Post-incident summary

## [Incident Title] — Summary

**Status:** Resolved
**Duration:** [start] – [end] UTC ([N minutes])
**Severity:** [level]

**What happened:** [2-3 sentences, plain language, no jargon]

**What users experienced:** [error rates, affected endpoints, degraded features]

**Root cause:** [one paragraph — what actually broke]

**What we're doing to prevent recurrence:**
- [ ] [action item + owner + due date]
- [ ] [action item + owner + due date]

Full postmortem: [link]

Severity Definitions

SeverityMeaningImpact on usersResponse timeUpdate frequency
CriticalMajor outageService unavailable or data loss for all usersImmediateEvery 15 minutes
HighSignificant degradationCore feature broken or very slow for many users< 15 minutesEvery 30 minutes
MediumPartial degradationNon-critical feature slow or unavailable, workaround exists< 1 hourEvery 1 hour
LowMinor issueCosmetic or edge-case problem, minimal user impact< 4 hoursAt resolution

SLA Definitions

MetricTargetMeasurement window
Uptime99.9%Rolling 30 days
p95 response time< 500 msRolling 7 days
Incident MTTA< 5 minPer incident
Status update cadenceEvery 15-30 min during incidentsPer incident

Operating the Page During an Incident

The status page fails in the same place every time: nobody owns it mid-incident. Before launch, decide who writes updates (usually the incident commander or their deputy, not the engineer fixing the problem), who approves them (one person, not a committee — speed beats polish), and where drafts live (a shared doc beats a Slack thread that scrolls away). Run the whole flow in a drill once a quarter; the first time a team learns the update cadence should not be during a real outage.

A useful internal pattern is the shadow status channel: a private channel where the on-call posts raw findings, and the designated communicator translates them into consumer-facing updates. It keeps the public page honest without leaking internal chaos.

Multi-Channel Communication

The status page is the canonical source, but nobody refreshes a page during their own outage. Push the same update to every subscription channel — email, RSS, Slack webhook — and keep the wording identical. A subscriber who gets a different severity on Slack than on the page assumes the page is stale. For consumers with strict integrations, an RSS feed or a /status.json endpoint (hosted by Statuspage.io and most self-hosted tools) lets their monitoring read status programmatically instead of scraping HTML.

Measuring Whether It Works

Three numbers tell you if the page earns its keep: ticket deflection during incidents (support volume on outage days versus baseline — a good page cuts it sharply), subscriber growth (stalled subscriptions mean consumers don’t know the page exists; link it from the API docs, the developer dashboard, and the 429/5xx error bodies), and time-to-first-update (how fast the first public post lands after detection — under 15 minutes is the bar that builds trust).

Adapting the Template

Cut the HTML down to what your consumers actually read. A five-service API doesn’t need nine rows in the service list — list the services consumers name in tickets, not the ones your architecture diagram has. Drop the uptime grid if you don’t compute it from real data yet; a missing metric reads better than a fake number. Keep the incident history and the subscription buttons regardless — those two sections are why the page exists. For multi-region setups, group services by region and let each region carry its own banner, so an EU outage doesn’t scream “down” to US consumers.

A Worked Incident Example

Watch how a real incident moves through the page — it makes the template concrete. 14:02 UTC: monitoring fires on elevated 5xx rates from the payment endpoint. 14:07: the incident commander posts the first update, severity High — “Investigating elevated error rates on POST /payments. Next update 14:30 UTC.” Consumers refreshing see the banner flip to degraded within minutes of the first failed call, not after a support ticket.

14:25: the team confirms a bad deploy as the cause. Second update goes out — now with root cause, affected scope, and a rollback ETA. This is the update that buys goodwill: it says what broke, who is affected, and when the next answer lands. 14:40: rollback verified, metrics recovering; third update marks “Monitoring” — resolved is withheld until error rates hold baseline for ten minutes, per the common mistakes list. 14:55: resolved banner, green service list, and a promise — “Postmortem within 48 hours.”

48 hours later the post-incident summary replaces the promise with links: what happened, root cause, the two action items with owners. The incident moves to history, where it earns trust retroactively — a consumer evaluating your API next quarter will read exactly that entry and see an honest account instead of an empty page that pretends outages never happen.

Communicating SLA Breaches

When an incident crosses the SLA threshold, the status page becomes a legal-adjacent document. Post the breach acknowledgment the same day — enterprise consumers check uptime math themselves and an unacknowledged breach reads as hiding. State the measured impact (downtime in minutes, percentage of requests failed), the contractual consequence (service credits per the SLA section, how to claim them), and the prevention path. Credits are cheap compared to the trust cost of making customers pull the numbers out of you.

Keep a standing “uptime by month” table on the page itself — publishing a month that came in under target, visibly, signals more confidence than any apology. Stripe’s status page shows the numbers even when they’re bad; that’s why theirs is the reference.

Launch Checklist

Before the page goes live, run through it once end to end:

  • Hosted on separate infrastructure — a status. subdomain on different DNS and a different provider than the API
  • Per-service statuses map to real monitoring, not a manual toggle
  • At least two subscription channels wired (email + RSS at minimum)
  • Severity table published and agreed with support — they will answer questions with it
  • First incident update reaches the page in under 15 minutes in a drill
  • Postmortem template ready and the link destination exists
  • The page itself has its own fallback channel documented
  • Someone is named as the update writer for the next incident

Items left unchecked at launch become incident-time improvisations — exactly the moment improvisation hurts most.

Keeping the Page Honest Over Time

Status pages rot quietly. Services get renamed while the page keeps the old names, a service is retired but its row stays green for months, and uptime bars keep rendering on a database nobody calls anymore. The drift is invisible because nothing errors — the page just slowly stops describing the system it claims to describe. Put the page on the same review cadence as the architecture diagram it mirrors: when a service ships, dies, or changes name, the status list changes in the same PR or the same sprint.

Two maintenance habits keep it credible. First, link every resolved incident to its postmortem, and do it while the incident is fresh — an unresolved promise to publish is worse than no promise. Second, test the boring path quarterly: publish a fake maintenance window on staging, verify it shows up in email, RSS, and Slack, then take it down. The channels you never exercise are the ones that fail during the real outage. A status page is infrastructure that earns trust only while it stays boring and true — the day it lies, it costs you more than having no page at all.

A Word on Tone

The hardest part of the page isn’t the HTML — it’s the writing during a live incident. Keep updates in plain language: state what is broken, who is affected, and when the next update lands, in that order. Avoid internal jargon (“the payment worker queue is backing up”) in favor of consumer-visible impact (“payment requests are failing or delayed”). Say “we” not “the system” — accountability reads as competence. And never speculate on cause in an early update; “we have identified a database connectivity issue” is fine, “we think the provider is having problems” gets quoted back at you if it turns out wrong. The template’s update structure exists precisely to keep that discipline when the pressure is on.

That discipline extends to the quiet periods too: a page that only wakes up during outages trains consumers to check it only after something breaks. Post the maintenance windows, the resolved incident summaries, and the uptime table even when nothing is on fire — the boring regularity is what makes the alarming update credible when it finally lands — consumers who watched you report quietly for six months believe you when you say “resolved” on day seven of the worst outage your API has ever had — that accumulated credibility is the entire return on running the page honestly month after month, even when the metrics are unflattering to publish — the numbers you hide are the ones that matter most to consumers deciding whether to trust you with their production traffic every single day, without fail.

What Works

The practices below come from watching real status pages succeed and fail — most teams get the structure right and the discipline wrong:

  • Update every 15-30 minutes during active incidents — Silence makes consumers assume the worst
  • Post scheduled maintenance 7 days in advance — Gives consumers time to prepare workarounds
  • Use a separate domain — status.example.com must not depend on the API it monitors. See Circuit Breaker for resilience patterns.
  • Offer RSS / email / Slack subscriptions — Let consumers choose how they receive updates
  • Show historical uptime — A 30-day or 90-day chart builds confidence
  • Be honest about degraded performance — Do not mark a service “operational” when latency is 10x normal. See Performance Optimization for monitoring metrics.
  • Link to incident postmortems — Transparency after resolution builds long-term trust
  • Define severity levels publicly — Consumers need to understand what “High severity” means for their integration
  • Include per-service status, not just a single aggregate status — A single “all operational” banner hides partial degradations
  • Timestamp every update in UTC — Consumers across timezones need a consistent reference

Common Mistakes

  • Using the same infrastructure as the API for the status page — if the API is down, the status page is down too
  • Marking incidents resolved too early — wait until metrics confirm recovery for at least 10 minutes
  • Deleting or editing resolved incident history — consumers need to reference past incidents
  • Vague updates like “we’re looking into it” — share what you know and what you don’t know
  • Not defining severity levels — consumers can’t gauge impact without clear severity definitions
  • Hoping nobody notices — consumers monitor your API with their own checks; they will know
  • Forgetting to post the postmortem link after resolution — follow-through matters
  • Not testing the status page during drills — if you’ve never used it under pressure, it will fail when you need it

Variants

Hosted status pages (Statuspage.io, BetterStack, Instatus)

Hosted services handle the infrastructure, uptime monitoring, and subscriber management. They cost money but eliminate the risk of your status page going down with your API. Pick them when you don’t want to run separate infra.

Self-hosted (Cachet, Upptime)

Self-hosted options like Cachet or Upptime run on GitHub Pages or a simple static host. They cost nothing but you run and patch them yourself. Upptime uses GitHub Actions for monitoring and stores history in the repository.

Internal-only status pages

For internal services, use a simpler page without subscriptions or public incident history. A Slack channel with automated alerts often suffices. See Alert Management Guide for alerting strategies.

Mermaid flowchart LR diagram

Further Reading

Frequently Asked Questions

Should the status page be public or internal-only?

Public for customer-facing APIs. Internal-only for purely internal services. Public status pages reduce support tickets and demonstrate operational maturity.

How often should I update during an incident?

Every 15-30 minutes, even when there's nothing new. "Still investigating root cause, next update at 15:00 UTC" beats silence.

What should I do if an incident exceeds the SLA?

Communicate proactively. Don't wait for customers to complain. See the incident communication template for structured incident updates. Issue a summary explaining what happened, why it breached SLA, and what measures are being taken to prevent recurrence. Some companies offer service credits for SLA breaches.

Should I show response time metrics on the status page?

Yes, if your API has latency SLAs. Show p95 and p99 response times alongside uptime. Latency degradation affects consumers even when the API is technically "up."

How do I handle multi-region status?

List each region separately (us-east, eu-west, ap-southeast) with its own status indicator. An incident in one region shouldn't show the entire service as down. Consumers in a given region care about one thing: is their region down?

What if the status page itself goes down?

Keep a secondary status channel — a dedicated Twitter/X account or Slack channel as a fallback. Document the fallback URL on the main status page and in your API documentation. The fallback must be on entirely separate infrastructure.

Should I post maintenance windows even for zero-downtime deployments?

Yes. Even zero-downtime deployments can cause brief latency spikes or minor errors. A maintenance window posted with "expected impact: none" sets expectations and gives consumers a window to avoid deploying their own changes simultaneously.