Documentation

HyperMonitors is API-first: everything you can do in the dashboard, you can do over a JSON REST API or expose to an AI agent via the Model Context Protocol. Create a free account to mint an API key.

MCP server

Let AI agents (Claude Code, Cursor, VS Code, …) inspect and manage your monitors over the Model Context Protocol. Enable it in Settings → MCP and connect with an API key. Read-only keys can use the read tools; creating, updating, or pausing monitors needs a read-write key.

Server URL (Streamable HTTP)
https://api.hypermonitors.com/v1/mcp
Client configuration (Claude Code, Cursor, VS Code, …)
{
  "mcpServers": {
    "hypermonitor": {
      "type": "http",
      "url": "https://api.hypermonitors.com/v1/mcp",
      "headers": {
        "Authorization": "Bearer hm_live_YOUR_KEY"
      }
    }
  }
}

Or with Claude Code: claude mcp add --transport http hypermonitor https://api.hypermonitors.com/v1/mcp--header "Authorization: Bearer hm_live_…"

Available tools
list_monitorsget_monitorget_monitor_uptimeget_monitor_checkslist_incidentslist_alert_channelsget_usagecreate_monitorupdate_monitorpause_monitorresume_monitorset_monitor_channels

Deleting monitors is intentionally not exposed over MCP.

Getting started

All endpoints live under the base URL and speak JSON. Authenticate by passing your key as a bearer token:

curl -H "Authorization: Bearer hm_live_…" \
  https://api.hypermonitors.com/v1/monitors
  • Key scopes. read-only keys may only make GET requests — anything else returns 403. read-write keys can use every method. Routes marked owner · read-write additionally require your org role to be owner, and dashboard only routes only work from the web app — keys can't mint or revoke keys.
  • Organizations. Requests act on your default organization. To target another org you belong to, send its id in an x-org-id header.
  • Conventions. List responses are shaped { items: [...] }; timestamps are ISO 8601 UTC; ids are UUIDs. Only /v1/notifications paginates (via cursor / nextCursor).
  • OpenAPI. A machine-readable spec is served at https://api.hypermonitors.com/docs/json.

Monitors

GET/v1/monitorsany key

List every monitor in the organization, newest first.

Returns { items: Monitor[], nextCursor: null }
POST/v1/monitorsread-write key

Create a monitor. The body is discriminated on type — see the field reference below.

Body
  • typehttp | port | ping | heartbeat | dns | ssl | domain (required)
  • namestring, 1–100 chars (required)
  • intervalSecondsint 30–86400, must be ≥ your plan's minimum (required; ignored for domain, which is daily)
  • timeoutMsint 1000–60000, default 30000
  • confirmationThresholdconsecutive failures before an incident opens, int 1–10, default 2
  • regionsregion ids to check from; capped to your plan. Omit to use the plan default
  • homeRegionAutobool, default false — check from every region your plan allows and order them by response time
  • tagsstring[], max 20 — free-form labels for grouping/filtering
  • plus the type-specific fields listed in the monitor type reference
Returns 201 Monitor

Plan gates: tier_type_not_allowed if the type isn't on your plan, tier_interval_too_low if the interval is below the plan minimum, tier_limit_exceeded when the monitor limit is reached.

New monitors are automatically routed to every enabled alert channel (opt-out) — narrow it with PUT /v1/monitors/:id/channels.

GET/v1/monitors/:idany key

Fetch one monitor.

Returns Monitor
PATCH/v1/monitors/:idread-write key

Partially update a monitor. Accepts any create field for the monitor's type except type itself (immutable). The merged result is re-validated in full.

Returns Monitor
DELETE/v1/monitors/:idread-write key

Delete a monitor and its history.

Returns { deleted: boolean }
POST/v1/monitors/:id/pauseread-write key

Pause checking. Status becomes paused.

Returns Monitor
POST/v1/monitors/:id/resumeread-write key

Resume a paused monitor. 409 conflict if it isn't paused.

Returns Monitor
POST/v1/monitors/:id/check-nowread-write key

Run an on-demand check now in every selected region. Rate-limited; not available for heartbeat monitors.

Returns { enqueued: number }
GET/v1/monitors/:id/checksany key

Raw check results, newest first.

Query
  • hoursint 1–17520, default 24
  • limitint 1–1000, default 500
Returns { items: [{ id, monitorId, checkedAt, ok, responseTimeMs, statusCode, error }] }
GET/v1/monitors/:id/checks/seriesany key

Checks aggregated into time buckets — ideal for charts and uptime bars.

Query
  • hoursint 1–17520, default 24
  • bucketMinutesint 1–1440, default 5
Returns { items: [{ bucketStart, avgResponseTimeMs, upCount, downCount }] }
GET/v1/monitors/:id/checks/region-statusany key

Current up/down and latest latency per region — for multi-region monitors.

Returns { items: [{ region, status: "up"|"down"|"pending", latestResponseMs, lastCheckAt }] }
GET/v1/monitors/:id/checks.csvany key

Export raw checks as CSV (attachment).

Query
  • hoursint 1–17520, default 720
Returns text/csv
GET/v1/monitors/:id/uptimeany key

Uptime percentage over fixed windows. Always returns all four windows.

Returns { windows: [{ window: "1d"|"7d"|"30d"|"365d", uptimePct: number|null, totalChecks }] }
GET/v1/monitors/:id/incidentsany key

Incidents for this monitor, newest first (up to 200).

Returns { items: [{ id, monitorId, kind, startedAt, resolvedAt, cause }] }
GET/v1/monitors/:id/channelsany key

The alert channels currently assigned to this monitor.

Returns { channelIds: uuid[] }
PUT/v1/monitors/:id/channelsread-write key

Replace the monitor's alert-channel assignment wholesale.

Body
  • channelIdsuuid[], max 100 — every id must be a channel in your org
Returns { channelIds: uuid[] }
Monitor type reference

Fields accepted by POST /v1/monitors in addition to the common ones, per type. Types marked “paid plans” aren't available on Free.

http
  • urlhttp(s) URL (required)
  • httpMethodGET | HEAD | POST | PUT, default GET
  • httpBodyrequest body sent with POST/PUT (max 10000 chars)
  • expectedStatusCodesint[] (100–599, max 50) — default: any 2xx/3xx is up
  • keywordType"exists" | "absent" — pair with keywordValue
  • keywordValuestring 1–500 — pair with keywordType
  • followRedirectsboolean, default true
  • customHeadersobject of header name → value (max 20) sent with every check, e.g. { "Authorization": "Bearer …" } — request-framing headers (host, content-length, connection, …) can't be set
  • sslCheckEnabledboolean, default true — capture HTTPS cert stats + expiry reminders
  • sslAlertDaysint 1–90, default 14
port
  • hosthostname or IPv4 (required)
  • portint 1–65535 (required)
ping
  • hosthostname or IPv4 (required)
heartbeatpaid plans
  • heartbeatGraceSecondsint 0–86400, default 300 — how long a ping may be overdue before the monitor goes down
dnspaid plans
  • hosthostname (required)
  • dnsRecordTypeA | AAAA | CNAME | MX | TXT | NS (required)
  • dnsExpectedValuestring 1–255 — resolved records must include it
sslpaid plans
  • hosthostname (required)
  • portint 1–65535, default 443
  • sslAlertDaysint 1–90, default 14
  • sslWatchChangesboolean, default false — alert when the certificate fingerprint changes
domainpaid plans
  • hostregistrable domain, e.g. example.com (required) — expiry resolved via RDAP
  • domainAlertDaysint 1–90, default 30 — remind this many days before the registration expires

Incidents

GET/v1/incidentsany key

Incidents across all monitors in the organization, newest first.

Query
  • status"open" | "resolved" — open means resolvedAt is null (optional)
  • limitint 1–200, default 50
Returns { items: [{ id, monitorId, monitorName, kind: "down"|"ssl_expiring"|"ssl_changed", startedAt, resolvedAt, cause }] }
POST/v1/incidents/acknowledgeread-write key

Acknowledge an open incident to stop repeat/escalation notifications.

Body
  • incidentIduuid of an open incident
Returns Incident
GET/v1/incidents.csvany key

Export incidents as CSV (attachment).

Returns text/csv

Alert channels

GET/v1/alert-channelsany key

List the organization's alert channels.

Returns { items: [{ id, type, name, enabled, verified, config, createdAt, … }] }
POST/v1/alert-channelsread-write key

Create an alert channel. It is automatically assigned to every existing monitor (opt-out). tier_limit_exceeded when the plan's channel limit is reached.

Body
  • typein_app | telegram | email | slack | discord | sms (required)
  • namestring, 1–100 chars (required)
  • configemail → { address }; slack/discord → { webhookUrl } (https); sms → { phone } in international format (e.g. +41791234567 — stored digits-only). in_app and telegram need none — telegram starts unverified until linked
Returns 201 AlertChannel
PATCH/v1/alert-channels/:idread-write key

Rename or enable/disable a channel.

Body
  • namestring, 1–100 chars (optional)
  • enabledboolean (optional)
Returns AlertChannel
DELETE/v1/alert-channels/:idread-write key

Delete a channel.

Returns { deleted: boolean }
POST/v1/alert-channels/:id/testread-write key

Send a test alert through the channel. 409 if the channel is disabled or Telegram isn't linked yet.

Returns { delivered: boolean, message: string|null }
POST/v1/alert-channels/:id/send-coderead-write key

(Re)send the 6-digit confirmation code for an email or SMS channel. 409 if the channel type needs no verification, or is already verified.

Returns { sent: boolean }

Email and SMS channels are double opt-in: they receive no alerts until verified.

POST/v1/alert-channels/:id/verifyread-write key

Verify an email or SMS channel with the code that was sent to it. Returns { verified: true } immediately if it was already verified; 409 if the channel type needs no verification.

Body
  • codethe 6-digit code, digits only (required)
Returns { verified: boolean }
POST/v1/alert-channels/telegram/link-coderead-write key

Mint a one-time code that binds a Telegram chat to a channel. Send /start <code> to the bot, or open the returned deep link. Codes expire after 10 minutes.

Body
  • channelIduuid of a telegram channel (required)
Returns { code, deepLink, expiresAt }

Notifications

GET/v1/notificationsany key

Your in-app notifications (per user, not per org). The only cursor-paginated endpoint.

Query
  • unreadOnlyboolean, default false
  • limitint 1–200, default 50
  • cursorid from a previous page's nextCursor (optional)
Returns { items: [{ id, incidentId, title, body, readAt, createdAt }], nextCursor: string|null }
POST/v1/notifications/readread-write key

Mark notifications as read.

Body
  • idsnumber[], 1–500 entries (required)
Returns { updated: number }

Account & usage

GET/v1/meany key

The authenticated user and their active organization's tier.

Returns { id, email, tier, telegramLinked, createdAt }
PATCH/v1/medashboard only

Update account settings: the per-account MCP toggle and/or the active organization. Only available with a browser session. 404 if you are not a member of the given organization. Enabling MCP auto-creates a dedicated API key (its secret is returned once as mcpKeySecret); disabling revokes that key again.

Body
  • mcpEnabledboolean (optional)
  • activeOrganizationIduuid of an organization you belong to (optional) — switches the active org
Returns MeResponse (tier reflects the new active organization)
DELETE/v1/medashboard only

Permanently delete the account. Only available with a browser session — never with an API key. Organizations where you are the sole member are deleted outright, taking their monitors, channels, status pages and history with them; any Stripe subscription is cancelled.

Returns { deleted: true, organizationsDeleted: number }

409 if you still own an organization that has other members — transfer ownership or remove them first, so a shared org is never orphaned.

This cannot be undone.

GET/v1/usageany key

Current usage against plan limits.

Returns { tier, monitors: {used, limit}, alertChannels: {used, limit}, minIntervalSeconds, logRetentionDays, allowedMonitorTypes }
GET/v1/analyticsany key

Org-wide analytics for a trailing window, compared with the window before it: time-weighted uptime, down-incident count/downtime/avg/longest/MTBF, response-time stats with >1s peaks, and every monitor sorted by lowest uptime.

Query
  • days7 or 30, default 7
Returns { windowDays, since, until, uptimePct, prevUptimePct, incidents, prevIncidents, downtimeMs, avgIncidentMs, longestIncidentMs, mtbfMs, avgResponseMs, prevAvgResponseMs, minResponseMs, maxResponseMs, peaksAbove1s, monitors: [{ id, name, uptimePct, incidents, avgResponseMs }] }
GET/v1/analytics/sla.csvany key

Export the per-monitor SLA/uptime breakdown as CSV (attachment).

Query
  • days7 or 30, default 7
Returns text/csv

MCP server

A Model Context Protocol server for AI agents (Claude Code, Cursor, …). Enable it per account in Settings → MCP, then point your client at the endpoint with an API key in the Authorization header.

POST/v1/mcpany key

MCP Streamable HTTP endpoint (stateless JSON-RPC 2.0). Tools: list_monitors, get_monitor, get_monitor_uptime, get_monitor_checks, list_incidents, list_alert_channels, get_usage, create_monitor, update_monitor, pause_monitor, resume_monitor, set_monitor_channels. Read tools work with read-only keys; write tools need a read-write key. 403 until the account enables MCP in Settings.

Returns JSON-RPC 2.0 responses

Organization & team

GET/v1/organizationsany key

Every organization the authenticated user belongs to (drives the org switcher).

Returns { items: [{ id, name, tier, role: "owner"|"member" }] }
POST/v1/organizationsread-write key

Create a new organization (the caller becomes its owner and it becomes active).

Body
  • namestring, 1–100 chars (required)
Returns 201 Organization
GET/v1/organizationany key

The active organization.

Returns { id, name, tier, timezone, createdAt }
PATCH/v1/organizationowner · read-write

Update the organization's name and/or timezone.

Body
  • namestring, 1–100 chars (optional)
  • timezoneIANA timezone, e.g. Europe/Vienna (optional)
Returns Organization
POST/v1/organization/transferowner · read-write

Transfer ownership to an existing member (the caller becomes a member).

Body
  • userIduuid of a current member (required)
Returns { transferred: boolean }
POST/v1/organization/leaveread-write key

Leave the active organization. Owners must transfer ownership (or delete the org) first.

Returns { organizationId: uuid } — the org you're switched to after leaving
GET/v1/organization/membersany key

List members.

Returns { items: [{ userId, email, role: "owner"|"member", joinedAt }] }
DELETE/v1/organization/members/:idowner · read-write

Remove a member. 409 when trying to remove yourself.

Returns { removed: boolean }
GET/v1/organization/invitesany key

List pending invites.

Returns { items: [{ id, email, role, expiresAt, createdAt }] }
POST/v1/organization/invitesowner · read-write

Invite a teammate. Invites expire after 7 days. 409 if they're already a member.

Body
  • emailemail, max 255 (required)
  • role"owner" | "member", default "member"
Returns 201 { id, token, email, role, acceptUrl, expiresAt }
DELETE/v1/organization/invites/:idowner · read-write

Revoke a pending invite.

Returns { revoked: boolean }
POST/v1/organization/invites/acceptread-write key

Accept an invite as the calling user. 404 if the token is invalid or expired.

Body
  • tokeninvite token, min 8 chars (required)
Returns { organizationId: uuid }
POST/v1/organization/seed-home-regiondashboard only

Report the checking region nearest the signing-up user, so a brand-new organization starts monitoring close to them instead of defaulting to Europe. Adopted only while the organization is still on the untouched default, so repeat calls are harmless.

Body
  • homeRegiona region id, e.g. eu1 (required)
Returns { homeRegion }
GET/v1/organization/ssoowner · read-write

The organization's SAML SSO configuration, plus the SP endpoints to hand to your identity provider. Enterprise only — 403 on other tiers.

Returns { configured, providerId, metadataUrl, domains, updatedAt, acsUrl, metadataSpUrl }
POST/v1/organization/ssoowner · read-write

Create or replace the SAML SSO configuration. Users signing up with a matching email domain are then routed to your IdP and joined to this organization automatically. Enterprise only — 403 on other tiers.

Body
  • metadataUrlhttps:// URL of the IdP's SAML 2.0 metadata, fetched and auto-refreshed (required)
  • domains1–20 work email domains that route to this IdP (required)
Returns SsoConfig
DELETE/v1/organization/ssoowner · read-write

Remove SAML SSO. Existing members keep their accounts; new signups on those domains stop being routed to the IdP. Enterprise only — 403 on other tiers.

Returns { deleted: boolean }

Maintenance windows

Planned windows during which alerts are suppressed (checks still run and data is recorded). A window covers the whole org (empty monitorIds) or a specific set, with optional weekly recurrence.

GET/v1/maintenance-windowsany key

List the org's maintenance windows.

Returns { items: [{ id, name, monitorIds, startsAt, endsAt, recurrence, note }] }
POST/v1/maintenance-windowsread-write key

Create a maintenance window.

Body
  • namestring, 1–100 (required)
  • monitorIdsuuid[], max 500 — empty = whole org
  • startsAtISO datetime (required)
  • endsAtISO datetime, after startsAt (required)
  • recurrencenull (one-off) or weekly recurrence object
  • notestring, max 500 (optional)
Returns 201 MaintenanceWindow
PATCH/v1/maintenance-windows/:idread-write key

Update a maintenance window.

Returns MaintenanceWindow
DELETE/v1/maintenance-windows/:idread-write key

Delete a maintenance window.

Returns { deleted: boolean }

On-call schedules

Team+ feature. Who's on call is derived deterministically from the anchor, rotation length, and ordered member list — escalations route to the current member's own channels.

GET/v1/on-callany key

List on-call schedules with the member currently on call.

Returns { items: [{ id, name, rotationDays, anchorAt, memberIds, currentOnCallId }] }
POST/v1/on-callread-write key

Create an on-call schedule.

Body
  • namestring, 1–100 (required)
  • rotationDaysint 1–90, default 7
  • memberIdsordered uuid[] of org members, max 50
  • anchorAtISO datetime; defaults to now
Returns 201 OnCallSchedule
PATCH/v1/on-call/:idread-write key

Update a schedule (name, rotation, members, anchor).

Returns OnCallSchedule
DELETE/v1/on-call/:idread-write key

Delete an on-call schedule.

Returns { deleted: boolean }

Audit log

Team+ feature. An append-only record of who changed what in the organization.

GET/v1/auditany key

Read the org audit log, newest first (cursor-paginated). Team+ tier.

Query
  • limitint 1–200, default 50
  • cursorcreatedAt ISO of the previous page's last row
Returns { items: [{ id, actorEmail, actorKind, method, path, status, targetType, targetId, createdAt }], nextCursor }

Status pages

GET/v1/status-pagesany key

List the organization's status pages.

Returns { items: StatusPage[] }
GET/v1/status-pages/:idany key

Fetch one status page.

Returns { id, slug, title, branding: { logoUrl, accentColor, headerText }, isPublic, hasPassword, customDomain, monitorIds, createdAt }
GET/v1/status-pages/by-slug/:slug/previewany key

Team preview: the same payload as the public status endpoint, for members of the owning org. Works for hidden pages and bypasses the password gate. 403 when the page belongs to another organization.

Returns PublicStatusPage
POST/v1/status-pagesread-write key

Create a status page. Slugs are global — 409 if taken; tier_limit_exceeded at the plan's page limit.

Body
  • slug3–40 chars, lowercase letters/digits/hyphens (required)
  • titlestring, 1–100 chars (required)
Returns 201 StatusPage
PATCH/v1/status-pages/:idread-write key

Update settings, branding, monitors, visibility, custom domain, or password.

Body
  • titlestring, 1–100 chars
  • slugnew slug (409 if taken)
  • brandingpartial { logoUrl, accentColor, headerText }
  • isPublicboolean
  • customDomainhostname (max 253) or null to clear
  • monitorIdsuuid[], max 200 — all must be org monitors
  • passwordstring 4–200 to set, null to remove, omit to keep
Returns StatusPage
DELETE/v1/status-pages/:idread-write key

Delete a status page.

Returns { deleted: boolean }
GET/v1/status-pages/:id/domain-statusany key

Whether the page's custom domain is live, and the DNS records still to be created if it is not.

Returns { domain, state, message, records: DomainDnsRecord[] }

records is empty once the domain is active (or when none is configured).

API keys

GET/v1/api-keysany key

List your active (non-revoked) keys. The secret is never returned.

Returns { items: [{ id, name, access, prefix, lastUsedAt, createdAt }] }
POST/v1/api-keysdashboard only

Mint a key. Only available with a browser session — keys can't create keys. The secret is returned once; up to 25 active keys per user.

Body
  • namestring, 1–100 chars (required)
  • access"read_only" | "read_write", default "read_write"
Returns 201 ApiKey & { secret }
DELETE/v1/api-keys/:iddashboard only

Revoke a key. Only available with a browser session.

Returns { revoked: boolean }

Referrals

The affiliate program. Earnings accrue on the terms in force when the referred user signed up, so a later change to the program never alters an existing referrer's rate.

GET/v1/referrals/meany key

Your referral code, share link, and running totals. Money is grouped per charge currency and never summed across currencies — conversion happens at payout.

Returns { code, link, programEnabled, ratePercent, durationMonths, firstPaymentOnly, signups, paidConversions, totals: [{ currency, pendingEarningsCents, paidEarningsCents, lifetimeEarningsCents }] }

programEnabled is false when no active config exists; the other terms fields are then not meaningful.

GET/v1/referrals/me/earningsany key

Your individual earnings, newest first, capped at 200 rows. Reversed rows (refunded charges) are included and count toward nothing.

Returns { items: [{ id, organizationId, netAmountCents, earnedAmountCents, currency, rateBps, status, createdAt, paidAt }] }

status is "pending" | "approved" | "paid" | "reversed". Commission is calculated on the net amount actually captured, not the gross invoice total.

POST/v1/referrals/attributedashboard only

Attribute the caller's signup to a referral code. The web app calls this once after signup with the code from the hm_ref cookie.

Body
  • codea referral code (required)
Returns { attributed: boolean }

Always 200, even when the code is unknown, is your own, or you were already attributed — attributed simply reports whether it took effect. First touch wins.

Support & feature requests

GET/v1/supportany key

List the organization's support tickets and feature requests, newest first.

Returns { items: SupportRequest[], nextCursor }
POST/v1/supportread-write key

Open a support ticket or file a feature request. Limited to 5 open items per kind per organization; 409 once that quota is full.

Body
  • kind"support" | "feature" (required)
  • subjectstring, 3–150 chars (required)
  • bodystring, 10–5000 chars (required)
Returns 201 SupportRequest

Only "open" and "in_progress" count toward the quota; resolved, rejected and closed items free a slot.

Status page incidents

Manually authored incidents published on a status page, independent of monitor-detected downtime. Each carries a timeline of updates.

GET/v1/status-pages/:statusPageId/incidentsany key

List incidents on a status page, newest first, each with its full update timeline.

Returns { items: StatusIncident[] }
POST/v1/status-pages/:statusPageId/incidentsread-write key

Publish an incident. The body becomes the first entry in its timeline. Confirmed subscribers of the page are emailed.

Body
  • titlestring, 1–200 chars (required)
  • impact"none" | "minor" | "major" | "critical", default "minor"
  • state"investigating" | "identified" | "monitoring" | "resolved", default "investigating"
  • bodythe first update's message, 1–2000 chars (required)
Returns 201 StatusIncident
POST/v1/status-incidents/:id/updatesread-write key

Post a follow-up update and advance the incident's state. Setting the state to resolved closes the incident and stamps resolvedAt.

Body
  • state"investigating" | "identified" | "monitoring" | "resolved" (required)
  • bodystring, 1–2000 chars (required)
Returns StatusIncident (with the appended update)
DELETE/v1/status-incidents/:idread-write key

Delete an incident and its whole timeline. Subscribers are not notified.

Returns { deleted: boolean }
GET/v1/status-pages/:statusPageId/subscribers/countany key

How many confirmed subscribers would be emailed by the next incident on this page.

Returns { confirmed: number }

Billing

Dashboard-only. These back the in-app billing screens and are not part of the public API surface — use the dashboard rather than building against them, as they may change without notice.

GET/v1/billing/statusdashboard only

Whether billing is configured, the org's plan, and its purchased extra seats.

Returns { tier, billingEnabled, hasCustomer, hasSubscription, monitorCap, extraSeats }
POST/v1/billing/checkoutdashboard only

Create a Stripe Checkout session for a paid tier. 409 if billing isn't configured or the org already has a subscription.

Body
  • tiera paid tier (not free) (required)
  • cycle"monthly" | "annual", default "monthly"
  • currency"usd" | "eur" | "chf" — currency to charge, default "usd" (falls back to USD if the price doesn't support it)
Returns { url: string }
PUT/v1/billing/seatsdashboard only

Set the org's purchased extra seats (absolute count, in the tier's block size — 1 for Team, 5 for Enterprise). Requires an active subscription; 409 on tiers without extra seats.

Body
  • extraSeatstotal extra seats beyond the plan's included ones (0 removes the add-on) (required)
Returns { extraSeats }
POST/v1/billing/portaldashboard only

Open the Stripe customer portal. Pass an optional target to deep-link straight to a plan switch or seat change; omit it for the portal landing page. 409 if the org has no Stripe customer yet.

Returns { url: string }

Public endpoints

No Authorization header. Each is rate-limited to 120 requests per minute.

GET/v1/heartbeat/:tokenpublic

Heartbeat ping for cron-style monitors. Call it from your job on every run; the monitor goes down when pings stop for longer than the grace period.

Returns { ok: true }
POST/v1/heartbeat/:tokenpublic

Identical to the GET form, for clients that would rather POST. No body is read.

Returns { ok: true }
GET/v1/public/status/:slugpublic

The JSON behind a public status page, including daily uptime per monitor over the page's configured history window (7/30/90/365 days, default 90).

Query
  • passwordrequired only for password-protected pages
Returns { title, branding, overall, historyDays, monitors: [{ id, name, status, uptime, dailyUptime[historyDays] }], incidents, updatedAt } — or { passwordRequired: true, title }
GET/v1/public/resolve-domainpublic

Resolve a status-page custom domain to its slug.

Query
  • hosthostname, max 253 chars (required)
Returns { slug: string|null }
GET/v1/public/badge/:slug/:monitorIdpublic

An SVG status badge for one monitor on a public status page, for embedding in a README. Rate-limited to 240 requests per minute.

Query
  • variant"status" | "uptime", default "status" — current state, or the uptime percentage
Returns image/svg+xml
POST/v1/public/status/:slug/subscribepublic

Subscribe an email address to a status page's incident notifications. Double opt-in: a confirmation link is emailed and nothing is sent until it is followed.

Body
  • emailemail, max 255 chars (required)
Returns { ok: true }

Always { ok: true } — whether the address was already subscribed is deliberately not revealed.

GET/v1/public/subscribe/confirm/:tokenpublic

Confirm a status-page subscription. This is the target of the link in the confirmation email.

Returns { confirmed: boolean }
GET/v1/public/subscribe/unsubscribe/:tokenpublic

Unsubscribe from a status page. Linked at the foot of every incident email.

Returns { unsubscribed: boolean }
GET/healthzpublic

Liveness probe (note: no /v1 prefix).

Returns { status, service, uptimeSeconds }
GET/readyzpublic

Readiness probe — 503 until the database and scheduler are up (no /v1 prefix).

Returns { status, checks }

Errors

Every error is JSON with a stable code:

{ "error": { "code": "conflict", "message": "…", "details": … } }
CodeStatusMeaning
validation_error400Request body or query failed validation — details carries the issues
unauthorized401Missing, invalid, or revoked credentials
forbidden403Read-only key on a write, non-member org, missing owner role, or a dashboard-only route
not_found404Resource (or route) doesn't exist in your org
conflict409State conflict — slug taken, monitor not paused, channel disabled, billing unconfigured…
tier_limit_exceeded403A plan count limit was reached (monitors, channels, status pages, 25 API keys)
tier_interval_too_low403Check interval below your plan's minimum
tier_type_not_allowed403Monitor type not available on your plan
rate_limited429Too many requests (public endpoints: 120/min)
internal500Unexpected server error