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.
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.
https://api.hypermonitors.com/v1/mcp{
"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_…"
list_monitorsget_monitorget_monitor_uptimeget_monitor_checkslist_incidentslist_alert_channelsget_usagecreate_monitorupdate_monitorpause_monitorresume_monitorset_monitor_channelsDeleting monitors is intentionally not exposed over MCP.
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-idheader. - Conventions. List responses are shaped
{ items: [...] }; timestamps are ISO 8601 UTC; ids are UUIDs. Only/v1/notificationspaginates (viacursor/nextCursor). - OpenAPI. A machine-readable spec is served at https://api.hypermonitors.com/docs/json.
Monitors
/v1/monitorsany keyList every monitor in the organization, newest first.
{ items: Monitor[], nextCursor: null }/v1/monitorsread-write keyCreate a monitor. The body is discriminated on type — see the field reference below.
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 30000confirmationThresholdconsecutive failures before an incident opens, int 1–10, default 2regionsregion ids to check from; capped to your plan. Omit to use the plan defaulthomeRegionAutobool, default false — check from every region your plan allows and order them by response timetagsstring[], max 20 — free-form labels for grouping/filtering…plus the type-specific fields listed in the monitor type reference
201 MonitorPlan 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.
/v1/monitors/:idany keyFetch one monitor.
Monitor/v1/monitors/:idread-write keyPartially update a monitor. Accepts any create field for the monitor's type except type itself (immutable). The merged result is re-validated in full.
Monitor/v1/monitors/:idread-write keyDelete a monitor and its history.
{ deleted: boolean }/v1/monitors/:id/pauseread-write keyPause checking. Status becomes paused.
Monitor/v1/monitors/:id/resumeread-write keyResume a paused monitor. 409 conflict if it isn't paused.
Monitor/v1/monitors/:id/check-nowread-write keyRun an on-demand check now in every selected region. Rate-limited; not available for heartbeat monitors.
{ enqueued: number }/v1/monitors/:id/checksany keyRaw check results, newest first.
hoursint 1–17520, default 24limitint 1–1000, default 500
{ items: [{ id, monitorId, checkedAt, ok, responseTimeMs, statusCode, error }] }/v1/monitors/:id/checks/seriesany keyChecks aggregated into time buckets — ideal for charts and uptime bars.
hoursint 1–17520, default 24bucketMinutesint 1–1440, default 5
{ items: [{ bucketStart, avgResponseTimeMs, upCount, downCount }] }/v1/monitors/:id/checks/region-statusany keyCurrent up/down and latest latency per region — for multi-region monitors.
{ items: [{ region, status: "up"|"down"|"pending", latestResponseMs, lastCheckAt }] }/v1/monitors/:id/checks.csvany keyExport raw checks as CSV (attachment).
hoursint 1–17520, default 720
text/csv/v1/monitors/:id/uptimeany keyUptime percentage over fixed windows. Always returns all four windows.
{ windows: [{ window: "1d"|"7d"|"30d"|"365d", uptimePct: number|null, totalChecks }] }/v1/monitors/:id/incidentsany keyIncidents for this monitor, newest first (up to 200).
{ items: [{ id, monitorId, kind, startedAt, resolvedAt, cause }] }/v1/monitors/:id/channelsany keyThe alert channels currently assigned to this monitor.
{ channelIds: uuid[] }/v1/monitors/:id/channelsread-write keyReplace the monitor's alert-channel assignment wholesale.
channelIdsuuid[], max 100 — every id must be a channel in your org
{ channelIds: uuid[] }Fields accepted by POST /v1/monitors in addition to the common ones, per type. Types marked “paid plans” aren't available on Free.
httpurlhttp(s) URL (required)httpMethodGET | HEAD | POST | PUT, default GEThttpBodyrequest body sent with POST/PUT (max 10000 chars)expectedStatusCodesint[] (100–599, max 50) — default: any 2xx/3xx is upkeywordType"exists" | "absent" — pair with keywordValuekeywordValuestring 1–500 — pair with keywordTypefollowRedirectsboolean, default truecustomHeadersobject of header name → value (max 20) sent with every check, e.g. { "Authorization": "Bearer …" } — request-framing headers (host, content-length, connection, …) can't be setsslCheckEnabledboolean, default true — capture HTTPS cert stats + expiry reminderssslAlertDaysint 1–90, default 14
porthosthostname or IPv4 (required)portint 1–65535 (required)
pinghosthostname or IPv4 (required)
heartbeatpaid plansheartbeatGraceSecondsint 0–86400, default 300 — how long a ping may be overdue before the monitor goes down
dnspaid planshosthostname (required)dnsRecordTypeA | AAAA | CNAME | MX | TXT | NS (required)dnsExpectedValuestring 1–255 — resolved records must include it
sslpaid planshosthostname (required)portint 1–65535, default 443sslAlertDaysint 1–90, default 14sslWatchChangesboolean, default false — alert when the certificate fingerprint changes
domainpaid planshostregistrable domain, e.g. example.com (required) — expiry resolved via RDAPdomainAlertDaysint 1–90, default 30 — remind this many days before the registration expires
Incidents
/v1/incidentsany keyIncidents across all monitors in the organization, newest first.
status"open" | "resolved" — open means resolvedAt is null (optional)limitint 1–200, default 50
{ items: [{ id, monitorId, monitorName, kind: "down"|"ssl_expiring"|"ssl_changed", startedAt, resolvedAt, cause }] }/v1/incidents/acknowledgeread-write keyAcknowledge an open incident to stop repeat/escalation notifications.
incidentIduuid of an open incident
Incident/v1/incidents.csvany keyExport incidents as CSV (attachment).
text/csvAlert channels
/v1/alert-channelsany keyList the organization's alert channels.
{ items: [{ id, type, name, enabled, verified, config, createdAt, … }] }/v1/alert-channelsread-write keyCreate an alert channel. It is automatically assigned to every existing monitor (opt-out). tier_limit_exceeded when the plan's channel limit is reached.
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
201 AlertChannel/v1/alert-channels/:idread-write keyRename or enable/disable a channel.
namestring, 1–100 chars (optional)enabledboolean (optional)
AlertChannel/v1/alert-channels/:idread-write keyDelete a channel.
{ deleted: boolean }/v1/alert-channels/:id/testread-write keySend a test alert through the channel. 409 if the channel is disabled or Telegram isn't linked yet.
{ delivered: boolean, message: string|null }/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.
{ sent: boolean }Email and SMS channels are double opt-in: they receive no alerts until verified.
/v1/alert-channels/:id/verifyread-write keyVerify 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.
codethe 6-digit code, digits only (required)
{ verified: boolean }/v1/alert-channels/telegram/link-coderead-write keyMint 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.
channelIduuid of a telegram channel (required)
{ code, deepLink, expiresAt }Notifications
/v1/notificationsany keyYour in-app notifications (per user, not per org). The only cursor-paginated endpoint.
unreadOnlyboolean, default falselimitint 1–200, default 50cursorid from a previous page's nextCursor (optional)
{ items: [{ id, incidentId, title, body, readAt, createdAt }], nextCursor: string|null }/v1/notifications/readread-write keyMark notifications as read.
idsnumber[], 1–500 entries (required)
{ updated: number }Account & usage
/v1/meany keyThe authenticated user and their active organization's tier.
{ id, email, tier, telegramLinked, createdAt }/v1/medashboard onlyUpdate 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.
mcpEnabledboolean (optional)activeOrganizationIduuid of an organization you belong to (optional) — switches the active org
MeResponse (tier reflects the new active organization)/v1/medashboard onlyPermanently 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.
{ 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.
/v1/usageany keyCurrent usage against plan limits.
{ tier, monitors: {used, limit}, alertChannels: {used, limit}, minIntervalSeconds, logRetentionDays, allowedMonitorTypes }/v1/analyticsany keyOrg-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.
days7 or 30, default 7
{ windowDays, since, until, uptimePct, prevUptimePct, incidents, prevIncidents, downtimeMs, avgIncidentMs, longestIncidentMs, mtbfMs, avgResponseMs, prevAvgResponseMs, minResponseMs, maxResponseMs, peaksAbove1s, monitors: [{ id, name, uptimePct, incidents, avgResponseMs }] }/v1/analytics/sla.csvany keyExport the per-monitor SLA/uptime breakdown as CSV (attachment).
days7 or 30, default 7
text/csvMCP 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.
/v1/mcpany keyMCP 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.
JSON-RPC 2.0 responsesOrganization & team
/v1/organizationsany keyEvery organization the authenticated user belongs to (drives the org switcher).
{ items: [{ id, name, tier, role: "owner"|"member" }] }/v1/organizationsread-write keyCreate a new organization (the caller becomes its owner and it becomes active).
namestring, 1–100 chars (required)
201 Organization/v1/organizationany keyThe active organization.
{ id, name, tier, timezone, createdAt }/v1/organizationowner · read-writeUpdate the organization's name and/or timezone.
namestring, 1–100 chars (optional)timezoneIANA timezone, e.g. Europe/Vienna (optional)
Organization/v1/organization/transferowner · read-writeTransfer ownership to an existing member (the caller becomes a member).
userIduuid of a current member (required)
{ transferred: boolean }/v1/organization/leaveread-write keyLeave the active organization. Owners must transfer ownership (or delete the org) first.
{ organizationId: uuid } — the org you're switched to after leaving/v1/organization/membersany keyList members.
{ items: [{ userId, email, role: "owner"|"member", joinedAt }] }/v1/organization/members/:idowner · read-writeRemove a member. 409 when trying to remove yourself.
{ removed: boolean }/v1/organization/invitesany keyList pending invites.
{ items: [{ id, email, role, expiresAt, createdAt }] }/v1/organization/invitesowner · read-writeInvite a teammate. Invites expire after 7 days. 409 if they're already a member.
emailemail, max 255 (required)role"owner" | "member", default "member"
201 { id, token, email, role, acceptUrl, expiresAt }/v1/organization/invites/:idowner · read-writeRevoke a pending invite.
{ revoked: boolean }/v1/organization/invites/acceptread-write keyAccept an invite as the calling user. 404 if the token is invalid or expired.
tokeninvite token, min 8 chars (required)
{ organizationId: uuid }/v1/organization/seed-home-regiondashboard onlyReport 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.
homeRegiona region id, e.g. eu1 (required)
{ homeRegion }/v1/organization/ssoowner · read-writeThe organization's SAML SSO configuration, plus the SP endpoints to hand to your identity provider. Enterprise only — 403 on other tiers.
{ configured, providerId, metadataUrl, domains, updatedAt, acsUrl, metadataSpUrl }/v1/organization/ssoowner · read-writeCreate 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.
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)
SsoConfig/v1/organization/ssoowner · read-writeRemove SAML SSO. Existing members keep their accounts; new signups on those domains stop being routed to the IdP. Enterprise only — 403 on other tiers.
{ 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.
/v1/maintenance-windowsany keyList the org's maintenance windows.
{ items: [{ id, name, monitorIds, startsAt, endsAt, recurrence, note }] }/v1/maintenance-windowsread-write keyCreate a maintenance window.
namestring, 1–100 (required)monitorIdsuuid[], max 500 — empty = whole orgstartsAtISO datetime (required)endsAtISO datetime, after startsAt (required)recurrencenull (one-off) or weekly recurrence objectnotestring, max 500 (optional)
201 MaintenanceWindow/v1/maintenance-windows/:idread-write keyUpdate a maintenance window.
MaintenanceWindow/v1/maintenance-windows/:idread-write keyDelete a maintenance window.
{ 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.
/v1/on-callany keyList on-call schedules with the member currently on call.
{ items: [{ id, name, rotationDays, anchorAt, memberIds, currentOnCallId }] }/v1/on-callread-write keyCreate an on-call schedule.
namestring, 1–100 (required)rotationDaysint 1–90, default 7memberIdsordered uuid[] of org members, max 50anchorAtISO datetime; defaults to now
201 OnCallSchedule/v1/on-call/:idread-write keyUpdate a schedule (name, rotation, members, anchor).
OnCallSchedule/v1/on-call/:idread-write keyDelete an on-call schedule.
{ deleted: boolean }Audit log
Team+ feature. An append-only record of who changed what in the organization.
/v1/auditany keyRead the org audit log, newest first (cursor-paginated). Team+ tier.
limitint 1–200, default 50cursorcreatedAt ISO of the previous page's last row
{ items: [{ id, actorEmail, actorKind, method, path, status, targetType, targetId, createdAt }], nextCursor }Status pages
/v1/status-pagesany keyList the organization's status pages.
{ items: StatusPage[] }/v1/status-pages/:idany keyFetch one status page.
{ id, slug, title, branding: { logoUrl, accentColor, headerText }, isPublic, hasPassword, customDomain, monitorIds, createdAt }/v1/status-pages/by-slug/:slug/previewany keyTeam 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.
PublicStatusPage/v1/status-pagesread-write keyCreate a status page. Slugs are global — 409 if taken; tier_limit_exceeded at the plan's page limit.
slug3–40 chars, lowercase letters/digits/hyphens (required)titlestring, 1–100 chars (required)
201 StatusPage/v1/status-pages/:idread-write keyUpdate settings, branding, monitors, visibility, custom domain, or password.
titlestring, 1–100 charsslugnew slug (409 if taken)brandingpartial { logoUrl, accentColor, headerText }isPublicbooleancustomDomainhostname (max 253) or null to clearmonitorIdsuuid[], max 200 — all must be org monitorspasswordstring 4–200 to set, null to remove, omit to keep
StatusPage/v1/status-pages/:idread-write keyDelete a status page.
{ deleted: boolean }/v1/status-pages/:id/domain-statusany keyWhether the page's custom domain is live, and the DNS records still to be created if it is not.
{ domain, state, message, records: DomainDnsRecord[] }records is empty once the domain is active (or when none is configured).
API keys
/v1/api-keysany keyList your active (non-revoked) keys. The secret is never returned.
{ items: [{ id, name, access, prefix, lastUsedAt, createdAt }] }/v1/api-keysdashboard onlyMint 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.
namestring, 1–100 chars (required)access"read_only" | "read_write", default "read_write"
201 ApiKey & { secret }/v1/api-keys/:iddashboard onlyRevoke a key. Only available with a browser session.
{ 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.
/v1/referrals/meany keyYour referral code, share link, and running totals. Money is grouped per charge currency and never summed across currencies — conversion happens at payout.
{ 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.
/v1/referrals/me/earningsany keyYour individual earnings, newest first, capped at 200 rows. Reversed rows (refunded charges) are included and count toward nothing.
{ 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.
/v1/referrals/attributedashboard onlyAttribute the caller's signup to a referral code. The web app calls this once after signup with the code from the hm_ref cookie.
codea referral code (required)
{ 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
/v1/supportany keyList the organization's support tickets and feature requests, newest first.
{ items: SupportRequest[], nextCursor }/v1/supportread-write keyOpen a support ticket or file a feature request. Limited to 5 open items per kind per organization; 409 once that quota is full.
kind"support" | "feature" (required)subjectstring, 3–150 chars (required)bodystring, 10–5000 chars (required)
201 SupportRequestOnly "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.
/v1/status-pages/:statusPageId/incidentsany keyList incidents on a status page, newest first, each with its full update timeline.
{ items: StatusIncident[] }/v1/status-pages/:statusPageId/incidentsread-write keyPublish an incident. The body becomes the first entry in its timeline. Confirmed subscribers of the page are emailed.
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)
201 StatusIncident/v1/status-incidents/:id/updatesread-write keyPost a follow-up update and advance the incident's state. Setting the state to resolved closes the incident and stamps resolvedAt.
state"investigating" | "identified" | "monitoring" | "resolved" (required)bodystring, 1–2000 chars (required)
StatusIncident (with the appended update)/v1/status-incidents/:idread-write keyDelete an incident and its whole timeline. Subscribers are not notified.
{ deleted: boolean }/v1/status-pages/:statusPageId/subscribers/countany keyHow many confirmed subscribers would be emailed by the next incident on this page.
{ 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.
/v1/billing/statusdashboard onlyWhether billing is configured, the org's plan, and its purchased extra seats.
{ tier, billingEnabled, hasCustomer, hasSubscription, monitorCap, extraSeats }/v1/billing/checkoutdashboard onlyCreate a Stripe Checkout session for a paid tier. 409 if billing isn't configured or the org already has a subscription.
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)
{ url: string }/v1/billing/seatsdashboard onlySet 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.
extraSeatstotal extra seats beyond the plan's included ones (0 removes the add-on) (required)
{ extraSeats }/v1/billing/portaldashboard onlyOpen 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.
{ url: string }Public endpoints
No Authorization header. Each is rate-limited to 120 requests per minute.
/v1/heartbeat/:tokenpublicHeartbeat 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.
{ ok: true }/v1/heartbeat/:tokenpublicIdentical to the GET form, for clients that would rather POST. No body is read.
{ ok: true }/v1/public/status/:slugpublicThe 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).
passwordrequired only for password-protected pages
{ title, branding, overall, historyDays, monitors: [{ id, name, status, uptime, dailyUptime[historyDays] }], incidents, updatedAt } — or { passwordRequired: true, title }/v1/public/resolve-domainpublicResolve a status-page custom domain to its slug.
hosthostname, max 253 chars (required)
{ slug: string|null }/v1/public/badge/:slug/:monitorIdpublicAn SVG status badge for one monitor on a public status page, for embedding in a README. Rate-limited to 240 requests per minute.
variant"status" | "uptime", default "status" — current state, or the uptime percentage
image/svg+xml/v1/public/status/:slug/subscribepublicSubscribe 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.
emailemail, max 255 chars (required)
{ ok: true }Always { ok: true } — whether the address was already subscribed is deliberately not revealed.
/v1/public/subscribe/confirm/:tokenpublicConfirm a status-page subscription. This is the target of the link in the confirmation email.
{ confirmed: boolean }/v1/public/subscribe/unsubscribe/:tokenpublicUnsubscribe from a status page. Linked at the foot of every incident email.
{ unsubscribed: boolean }/healthzpublicLiveness probe (note: no /v1 prefix).
{ status, service, uptimeSeconds }/readyzpublicReadiness probe — 503 until the database and scheduler are up (no /v1 prefix).
{ status, checks }Errors
Every error is JSON with a stable code:
{ "error": { "code": "conflict", "message": "…", "details": … } }| Code | Status | Meaning |
|---|---|---|
validation_error | 400 | Request body or query failed validation — details carries the issues |
unauthorized | 401 | Missing, invalid, or revoked credentials |
forbidden | 403 | Read-only key on a write, non-member org, missing owner role, or a dashboard-only route |
not_found | 404 | Resource (or route) doesn't exist in your org |
conflict | 409 | State conflict — slug taken, monitor not paused, channel disabled, billing unconfigured… |
tier_limit_exceeded | 403 | A plan count limit was reached (monitors, channels, status pages, 25 API keys) |
tier_interval_too_low | 403 | Check interval below your plan's minimum |
tier_type_not_allowed | 403 | Monitor type not available on your plan |
rate_limited | 429 | Too many requests (public endpoints: 120/min) |
internal | 500 | Unexpected server error |