API reference

Everything the dashboard can do with monitors, incidents and channels is available over HTTPS at https://uptimeroom.com/api/v1. Create keys under API & Developers; probe egress addresses are listed on Probe IPs.

Authentication

Send the key as a Bearer token. Keys look like urm_live_…, are shown once at creation and are stored hashed. A request carrying an Authorization: Bearer header is authenticated by that key alone - an unknown or revoked key returns 401 even from a signed-in browser.

curl https://uptimeroom.com/api/v1/monitors \ -H "Authorization: Bearer urm_live_..."

Scopes

ScopeGrants
monitor:readlist/read monitors, incidents, alert jobs, series, alert routes, channels
monitor:writecreate/edit/delete monitors, run checks, edit alert routes, create/test channels

Envelope

Successful responses are { "data": … }; list endpoints return an array in data. Errors are { "error": { "code", "message", "details"? } } with the HTTP status carrying the class of failure. Ids are UUIDs; timestamps are ISO-8601 UTC.

Rate limits, idempotency, caching

  • Rate limits: there is no per-key rate limit on /api/v1 yet. Sign-in, password-reset and verification endpoints are rate-limited; a 429 carries the reason in error.message. Be a good neighbour: poll monitors no more often than their interval.
  • Idempotency: only POST /posts/bulk honours an Idempotency-Key header (scoped to your org; a replay answers with idempotent-replay: true). Every other write is not idempotent - retry only after a network failure with no response.
  • Caching: authenticated responses are Cache-Control: private, no-store.

Error codes

StatusCodeWhen
400bad_requestBody is not valid JSON, or the multipart/JSON shape is wrong
401unauthorizedMissing, unknown or revoked Bearer key (a Bearer header never falls back to a browser session)
403forbiddenThe key lacks the required scope, or the dashboard role is too low
403channel_gatedThe channel type is not included in the org's plan
404not_foundUnknown id, or an id that is not a UUID
409quota_exceededMonitor limit for the plan reached
409slug_conflictA post with that slug already exists
409pausedRun-check on a paused monitor
409channel_disabledAlert route references a channel disabled on the plan
413payload_too_largeMedia upload over 10 MB
415unsupported_media_typeMedia upload that is not a raster image
422validation_errorField validation failed; `details` carries zod's { fieldErrors, formErrors }
422invalid_regionsUnknown region code(s) for this deployment
422unsafe_targetTarget resolves to a private/loopback/link-local address (SSRF guard)
422ssrf_rejectedA channel config URL fails the SSRF guard

Monitors

Monitors are the checks the probes run. Types: http, api, keyword, tcp, dns, ping, udp, ssl, domain, heartbeat.

EndpointScopeDescription
GET /api/v1/monitorsmonitor:readList the org's monitors with their latest state.
No parameters.
POST /api/v1/monitorsmonitor:writeCreate a monitor.
JSON: name (1–120), type, target, intervalSec (30–86400; plan floor applies), regions (1–5 region codes from GET /api/regions), optional port, method, expectedStatus[], assertions[], keyword, keywordMatch, dnsRecordType, dnsExpected, timeoutMs (1000–120000), responseTimeThresholdMs (50–120000), alertAfterCycles (1–10), headers, body, followRedirects, certWarnDays, maintenanceWindows[], paused.
Errors: 400 bad_request / no_plan · 409 quota_exceeded · 422 validation_error / invalid_regions / unsafe_target
Region count, interval floor and slow-response thresholds are gated by the plan. Not idempotent - two identical POSTs create two monitors.
GET /api/v1/monitors/{id}monitor:readRead one monitor.
Errors: 404 not_found
PATCH /api/v1/monitors/{id}monitor:writeUpdate any subset of the monitor fields.
JSON: the same fields as POST, all optional; nullable fields accept null to clear.
Errors: 404 not_found · 422 validation_error / invalid_regions / unsafe_target
DELETE /api/v1/monitors/{id}monitor:writeDelete a monitor and its history.
Errors: 404 not_found
POST /api/v1/monitors/{id}/checkmonitor:writeRequest an immediate re-check in every configured region.
Errors: 404 not_found · 409 paused
GET /api/v1/monitors/{id}/seriesmonitor:readBucketed p50/p95/p99 + availability for charts.
Query: window=1h|24h|7d|30d (default 24h) and region=<code>|all, or from=<iso>&to=<iso> (≤ 31 days).
Errors: 404 not_found · 422 validation_error
GET /api/v1/monitors/{id}/routesmonitor:readThe monitor's alert routes (channels, re-alert cadence, recovery/degraded toggles).
Errors: 404 not_found
PUT /api/v1/monitors/{id}/routesmonitor:writeReplace the full set of alert routes.
JSON: { routes: [{ channelId, reAlertEveryMin (0–1440; 0 = alert once), notifyOnRecovery, notifyOnDegraded }] } (max 50).
Errors: 404 not_found · 409 channel_disabled · 422 validation_error

Incidents & alert delivery

Incidents open when quorum confirms a monitor down/degraded and close on recovery.

EndpointScopeDescription
GET /api/v1/incidentsmonitor:readNewest incidents first.
Query: limit (default 50, clamped).
Bearer key only - dashboard sessions read incidents through the app pages.
GET /api/v1/incidents/{id}monitor:readOne incident with its alert delivery jobs.
Errors: 404 not_found
PATCH /api/v1/incidents/{id}monitor:writeAcknowledge an incident (idempotent; records who acknowledged).
JSON: { acknowledgedAt?: iso }.
Errors: 404 not_found · 422 validation_error
GET /api/v1/incidents/exportmonitor:readCSV of up to 5,000 incidents, newest first.
Query: state=all|ongoing|resolved, monitor=<uuid>, kind=down|degraded.
Errors: 422 validation_error
Responds with text/csv, not the JSON envelope.
GET /api/v1/alert-jobsmonitor:readAlert delivery queue with per-status counts (dead-lettered jobs carry last_error).
Query: status=pending|processing|delivered|failed|dead, limit (default 50).
Errors: 422 validation_error

Channels

Alert channels (email, Slack, webhook, PagerDuty, web push, SMS, voice, ...). Third-party channels are plan-gated.

EndpointScopeDescription
GET /api/v1/channelsmonitor:readList channels (secrets redacted).
POST /api/v1/channelsmonitor:writeCreate a channel.
JSON: { type, name?, config } - required config keys depend on the type.
Errors: 400 bad_request / no_plan · 403 channel_gated · 422 validation_error
GET /api/v1/channels/{id}monitor:readRead one channel.
Errors: 404 not_found
PATCH /api/v1/channels/{id}monitor:writeUpdate the channel config (clears verified until re-tested).
JSON: { config }.
Errors: 400 bad_request · 404 not_found · 422 validation_error
DELETE /api/v1/channels/{id}monitor:writeDelete a channel (dashboard sessions need the admin role).
Errors: 403 forbidden · 404 not_found
POST /api/v1/channels/{id}/testmonitor:writeDeliver a representative alert through the channel and mark it verified on success.
Errors: 404 not_found
SMS/voice tests are real sends and debit credits.
POST /api/v1/channels/test-configmonitor:writeTest an unsaved channel config before creating it.
JSON: { type, config }.
Errors: 400 bad_request · 422 validation_error / ssrf_rejected

API keys

Keys are org-level credentials: minting, listing and revoking them is a dashboard (admin+) action, so no key can mint another key.

EndpointScopeDescription
GET /api/v1/keysdashboard sessionList live keys (prefix, scopes, last used) - never the secret.
POST /api/v1/keysdashboard sessionCreate a key; the plaintext secret is returned exactly once.
JSON: { name (1–120), scopes: [...] }.
Errors: 422 validation_error · 403 forbidden (content:* on a non-staff account)
DELETE /api/v1/keys/{id}dashboard sessionRevoke a key; calls using it fail with 401 immediately.
Errors: 404 not_found

Public, unauthenticated endpoints

  • GET /api/regions - probe locations and the egress allow-list (also on /docs/probe-ips).
  • GET /api/credits/rates - SMS/voice credit rates.
  • POST /api/heartbeat/<token> - heartbeat pings (token-authenticated, no key needed).