Subscriber Reference
A subscriber in openstatus is an entity (typically a user or an integration) that opts to receive real-time notifications and updates regarding incidents and status changes on a specific status page. Subscribers play a crucial role in maintaining transparent communication during service disruptions.
Key functions of subscribers:
- Receive automated alerts when monitor statuses change or incidents are updated.
- Stay informed about service health without actively monitoring the status page.
- Choose preferred notification channels for receiving updates.
Ways to subscribe
Every method delivers the same events: status report updates and scheduled maintenance. Pick the one that matches where the audience wants to receive them.
| Method | Who sets it up | Where it is documented |
|---|---|---|
| The visitor, from the subscribe form on the status page | Public self-subscription below | |
| RSS / Atom / JSON feed | The visitor, in a feed reader or script | Feeds below |
| Slack channel via slash command | Anyone in a Slack workspace with the openstatus app, from the channel itself | Slack subscribers below |
| Email, webhook or Slack from the dashboard | The page owner | Adding subscribers from the dashboard below |
| Slack Connect channel shared with a customer | The page owner, scoped per customer | Deliver status updates to customer Slack channels |
Public self-subscription
Visitors subscribe through the subscribe form on the status page itself. The process involves:
- Entering an email address — the public form is email-only.
- Opt-in confirmation — a verification email is sent, and the subscription only activates once the recipient confirms it.
- Scope selection (optional) — where enabled, choosing which page components to be notified about instead of the whole page.
Webhook subscriptions are only created by the page owner from the dashboard. Slack channels can be subscribed either from the dashboard or from inside Slack with the slash command (see Slack subscribers).
Notification types received
Subscribers receive notifications for key events affecting the monitored services linked to the status page:
- Incident creation — when a new incident is detected and published.
- Incident updates — when status reports are published for an ongoing incident (e.g., status changes from
investigatingtoidentified,monitoring, orresolved). - Scheduled maintenance — when a maintenance window is created on the page, subscribers can be notified that it has been scheduled (see the maintenance reference).
Raw monitor status changes are not sent to status page subscribers. Those go to your configured notification channels. Subscribers only receive status report updates and scheduled maintenance.
Subscriber management
Status page administrators can manage their subscriber lists, including:
- Viewing subscribers — accessing a list of all active subscribers for a status page.
- Adding/removing subscribers — manually adding or removing subscribers.
- Communication — sending ad-hoc notifications to the subscriber list (if supported by the platform).
Adding subscribers from the dashboard
Beyond public self-subscription, administrators can add subscribers directly from the dashboard. This is useful for onboarding partners, internal teams, or automation that should receive updates without going through the public opt-in flow. Each manually added subscriber uses one of three channels:
- Email — delivers updates to a contact address. By adding an email here you confirm the contact has consented to receive status updates; no confirmation email is sent.
- Webhook — POSTs each update to a URL. Slack, Discord, and Microsoft Teams URLs receive channel-native messages; any other URL receives a generic JSON payload.
- Slack — posts each update straight into a Slack channel through the openstatus Slack app. The bot token is resolved from the workspace integration at send time and is never stored on the subscriber. A channel can also subscribe itself with the slash command (see Slack subscribers).
For every channel, you can optionally:
- Set a display label — shown in place of the raw destination in the dashboard (e.g.
Supabase #incidents). - Scope to page components — leave empty to notify for the entire page, or select specific components to only notify on matching reports.

Slack subscribers
A Slack subscriber posts every status report update and scheduled maintenance into one channel through the openstatus Slack app. There are two ways to create one.
From the dashboard
The page owner adds a Slack subscriber from the subscribers section of the status page, picks the channel, and optionally scopes it to components. This is the path for Slack Connect channels shared with customers, where the owner decides which components each customer sees. It requires the Slack agent to be installed in your workspace.
From Slack
Any channel in a Slack workspace that has the openstatus app installed can subscribe itself by running the slash command in that channel:
/openstatus subscribe https://acme.openstatus.dev
The URL is the public status page address, either the *.openstatus.dev subdomain or a custom domain. The subscription is created immediately without a confirmation step, the bot joins the channel if it can, and the channel receives updates for the whole page. Component scoping is not available from the command; use the dashboard for that.
Related commands, all run from the channel:
/openstatus subscriptions— list the pages this channel is subscribed to./openstatus unsubscribe [status-page-url]— stop updates. The URL is optional when the channel follows exactly one page. Each Slack message also ends with a Manage with/openstatus unsubscribehint./openstatus help— show the command list.
Re-running subscribe on a channel that previously unsubscribed reactivates the existing subscription instead of creating a duplicate. Subscribing from Slack works against any page whose workspace is on a plan that includes subscribers; it does not require the channel's workspace to own the page.
For installation and the natural-language incident commands, see Set up the openstatus Slack agent.
Feeds
Every status page exposes read-only feeds that need no subscription at all. Feed readers and scripts poll them directly:
| Path | Format |
|---|---|
/feed/rss | RSS 2.0 |
/feed/atom | Atom 1.0 |
/feed/json | JSON, the full page state including components |
On a password-protected page, append ?pw=your-secret-password to read them. Formats and fields are described in the status page reference.
Webhook subscribers
A webhook subscriber receives a POST request for every status report and scheduled maintenance update. openstatus inspects the destination URL and emits a payload tailored to it.
Slack
A URL starting with https://hooks.slack.com/services/ is treated as a Slack incoming webhook. The payload uses Slack Block Kit attachments — a colored header (red/yellow/green/blue by status), status and page fields, the update message, affected components, and footer links to view, manage, and unsubscribe.
Discord
A URL starting with https://discord.com/api/webhooks/ is treated as a Discord webhook. The payload uses a Discord embed with a status-colored sidebar, a title linking to the event, status and page fields, the update message, affected components, and manage/unsubscribe links.
Microsoft Teams
A URL whose hostname ends with .logic.azure.com, .powerplatform.com, or .webhook.office.com is treated as a Microsoft Teams webhook (Teams Workflows or the legacy Office connector). The payload is an Adaptive Card 1.5 wrapped in the standard Teams message envelope — a status-colored header container, a FactSet with status, page, and timestamp, the update message, affected components, manage/unsubscribe links, and a View details action button.
Generic
Any other URL receives a versioned, channel-agnostic JSON payload. Use this to forward updates into your own systems. The body is shaped as:
{
"version": "1",
"type": "status_report",
"data": {
"status_report": {
"id": 123,
"title": "Database connectivity issues",
"url": "https://acme.openstatus.dev/events/report/123",
"update": {
"id": 456,
"status": "investigating",
"message": "We are investigating elevated error rates.",
"occurred_at": "2026-06-26T10:00:00.000Z"
},
"page": {
"id": 1,
"name": "Acme Status",
"slug": "acme",
"url": "https://acme.openstatus.dev"
},
"components": [
{ "id": 7, "name": "API", "impact": "major_outage" }
]
}
},
"subscription": {
"manage_url": "https://acme.openstatus.dev/manage/<token>",
"unsubscribe_url": "https://acme.openstatus.dev/unsubscribe/<token>"
}
}
page.url is the canonical status page origin, while status_report.url (and maintenance.url below) is the deep link to that specific event. Each entry in components carries an impact. The schema permits null for forward compatibility, but the current sender always emits a concrete impact, falling back to operational for reports created before component impacts existed.
Scheduled maintenance is delivered with type: "maintenance" and a data.maintenance object carrying starts_at / ends_at in place of the update block. The version field is bumped on any breaking change, so pin to it when consuming the payload.
The Send test action emits the same envelope with type: "test" and a data.test object — use the type discriminator to ignore it (it carries no real event):
{
"version": "1",
"type": "test",
"data": {
"test": {
"message": "Your openstatus webhook is configured correctly.",
"timestamp": "2026-06-26T10:38:00.132Z"
}
}
}
You can attach custom request headers to every webhook request — for example, a shared secret or an authorization token your receiver verifies.
Timeout and retries
Each webhook request is given 5 seconds to complete. If your endpoint has not responded within that window, the request is aborted and treated as a failed attempt.
Failed attempts are retried with jittered exponential backoff (≈200 ms base delay), up to 3 retries (4 attempts total). Only transient failures are retried:
- Network errors and timeouts (no HTTP response).
5xxresponses — a server-side error on your end that may clear.429 Too Many Requests— your endpoint asked us to back off.
Any other 4xx response (e.g. 400, 401, 404) is treated as a permanent client error and is not retried, since replaying the same request would fail again. Make your receiver idempotent — a retried delivery is identical to the original, so deduplicate on the update id if you act on each request.
Example: post updates to X / Bluesky
statuspage-socials-notifier is a reference receiver that consumes the generic payload and cross-posts each update to your X (Twitter) and Bluesky accounts. Point a generic webhook subscriber at its endpoint to mirror your status page on social media — it doubles as a worked example of validating and handling the payload.
Validating the payload
The payload shape is defined in packages/subscriptions/src/payload.ts. Copy this zod schema to validate incoming requests in your own receiver:
import { z } from "zod";
export const WEBHOOK_PAYLOAD_VERSION = "1" as const;
const impactSchema = z.enum([
"operational",
"degraded_performance",
"partial_outage",
"major_outage",
]);
const statusSchema = z.enum([
"investigating",
"identified",
"monitoring",
"resolved",
]);
const componentSchema = z.object({
id: z.number().int(),
name: z.string(),
impact: impactSchema.nullish(),
});
const pageSchema = z.object({
id: z.number().int(),
name: z.string(),
slug: z.string(),
url: z.url(),
});
const subscriptionSchema = z.object({
manage_url: z.string().nullish(),
unsubscribe_url: z.string().nullish(),
});
const statusReportWebhookSchema = z.object({
version: z.literal(WEBHOOK_PAYLOAD_VERSION),
type: z.literal("status_report"),
data: z.object({
status_report: z.object({
id: z.number().int(),
title: z.string(),
url: z.url(),
update: z.object({
id: z.number().int(),
status: statusSchema,
message: z.string(),
occurred_at: z.string(),
}),
page: pageSchema,
components: z.array(componentSchema),
}),
}),
subscription: subscriptionSchema,
});
const maintenanceWebhookSchema = z.object({
version: z.literal(WEBHOOK_PAYLOAD_VERSION),
type: z.literal("maintenance"),
data: z.object({
maintenance: z.object({
id: z.number().int(),
title: z.string(),
url: z.url(),
message: z.string(),
starts_at: z.string().optional(),
ends_at: z.string().optional(),
page: pageSchema,
components: z.array(componentSchema),
}),
}),
subscription: subscriptionSchema,
});
const testWebhookSchema = z.object({
version: z.literal(WEBHOOK_PAYLOAD_VERSION),
type: z.literal("test"),
data: z.object({
test: z.object({
message: z.string(),
timestamp: z.string(),
}),
}),
});
export const webhookPayloadSchema = z.discriminatedUnion("type", [
statusReportWebhookSchema,
maintenanceWebhookSchema,
testWebhookSchema,
]);
export type WebhookPayload = z.infer<typeof webhookPayloadSchema>;
Related resources
- Status page reference — detailed information on managing and configuring status pages, including the feed formats.
- Set up the openstatus Slack agent — install the Slack app that powers Slack subscribers and the slash commands.
- Deliver status updates to customer Slack channels — component-scoped subscriptions for Slack Connect channels.
- Notification channels reference — technical specifications for the various notification delivery methods.
- Incident reference — information about incident creation and management.