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://monitor.internetgroup.com/v1/mcp{
"mcpServers": {
"hypermonitor": {
"type": "http",
"url": "https://monitor.internetgroup.com/v1/mcp",
"headers": {
"Authorization": "Bearer hm_live_YOUR_KEY"
}
}
}
}Or with Claude Code: claude mcp add --transport http hypermonitor https://monitor.internetgroup.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://monitor.internetgroup.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://monitor.internetgroup.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 (required)namestring, 1–100 chars (required)intervalSecondsint 30–86400, must be ≥ your plan's minimum (required)timeoutMsint 1000–60000, default 30000confirmationThresholdconsecutive failures before an incident opens, int 1–10, default 2…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/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/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)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
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 }] }Alert 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/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.
mcpEnabledboolean (optional)activeOrganizationIduuid of an organization you belong to (optional) — switches the active org
MeResponse (tier reflects the new active organization)/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 }] }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.
/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/organizationany keyThe active organization.
{ id, name, tier, createdAt }/v1/organizationowner · read-writeRename the organization.
namestring, 1–100 chars (required)
Organization/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 }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 }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 }Billing
/v1/billing/statusany keyWhether billing is configured and the org has a Stripe customer.
{ tier, billingEnabled, hasCustomer }/v1/billing/checkoutowner · read-writeCreate a Stripe Checkout session for a paid tier. 409 if billing isn't configured.
tiera paid tier (not free) (required)cycle"monthly" | "annual", default "monthly"
{ url: string }/v1/billing/portalowner · read-writeOpen the Stripe customer portal. 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 — also accepts POST. Call it from your job on every run; the monitor goes down when pings stop for longer than the grace period.
{ 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, uptime90d, 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 }/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 |