API

The Norcube Monitor API the dashboard and the CLI use. Authentication and scopes, the endpoints, the RFC 9457 error shape with its codes and reasons, rate limits and pagination.

Verified November 5, 2026

Everything the dashboard does goes through this API, and the CLI calls it too. Endpoints live under https://monitor.api.norcube.com/app/v1. The authoritative reference is the live Swagger UI at /app/v1/swagger/ on that host; this page documents the conventions.

Authentication

Authorization: Bearer <JWT>

Access tokens come from Norcube Auth with monitor as the audience; see Platform → Tokens. For automation, exchange a CI token for an access token. A CI token is scoped: monitor.read, monitor.check, monitor.write, monitor.channels or monitor.statuspages (the CLI page has the table). A request outside the token's scopes is 403 FORBIDDEN with reason INSUFFICIENT_SCOPE; the detail names the scope that would allow it. Every request is also checked against the caller's IAM permissions.

Endpoints

MethodPathWhat it does
GET/monitorsList. Filters type, status (overall status, paused included), q; sort (status, name, lastChecked, uptime, created); include=history adds the last 60 results per row.
POST/monitorsCreate (http, ssl or domain). Every check setting is required, no hidden defaults. The first check runs at once.
POST/monitors/bulk{action: pause | resume | delete, monitorIds: [...]}, 1 to 100 ids. One result per id; not atomic.
GET, PATCH, DELETE/monitors/{id}Settings and status (secret header values are never returned); partial update, a settings object replaces settings as a whole; delete with history.
POST/monitors/{id}/pause, /resumeIdempotent. Pausing closes the open incident quietly; resume checks right away unless the last check is less than an interval old.
POST/monitors/{id}/checkOn-demand check, stored and billed like a scheduled one.
GET/monitors/{id}/resultsRaw checks, from/to (RFC 3339, default last 24 h), cursor paging.
GET/monitors/{id}/statsrange=24h|7d|30d|90d: uptime, average and maximum latency, hourly or daily series.
GET/monitors/{id}/status-changesEvery status change, newest first, with duration, condition, HTTP status, message and the events in its span. Kept 1 year.
GET/monitors/{id}/availabilitydays (1 to 90) UTC days ending today: counts, uptime, estimated downtime, noData.
GET/monitors/{id}/certificateThe newest certificate seen (SSL and HTTPS monitors), with thresholds and the next threshold for SSL monitors. 404 when none was seen.
GET, PUT/monitors/{id}/alertsConfirmations, reminder interval and routes. useDefaults: true returns to the organization defaults; routes: [] mutes.
GET/incidentsOpen first (critical, then warning), then resolved. Filters status, monitorId, severity, aspect, acknowledged, q, from, to.
GET/incidents/summaryOpen counts by severity and unacknowledged, resolved in the last 7 and 30 days, mean time to resolve over 30 days.
GET/incidents/{id}Timeline and every alert sent, with status, attempts, last error and next attempt.
POST/incidents/{id}/acknowledgeStops reminders. Idempotent.
GET, POST/alert-channelsList and create. A webhook's signing secret is returned on create only.
GET, PATCH, DELETE/alert-channels/{id}Credentials are never returned; rotateSecret: true returns a new webhook secret; a new address or phone number needs verifying again.
POST/alert-channels/{id}/verify, /verify/confirmSend a 6-digit code, confirm it.
GET/alert-channels/{id}/deliveriesThe channel's delivery log, newest first, filter status=pending|sent|dead. Kept 90 days.
POST/alert-channels/{id}/testSend a test now and return the provider's answer.
GET, POST/status-pagesList, newest first (monitorId lists the pages showing that monitor instead), and create. A new page starts unpublished; every setting is required.
GET, PATCH, DELETE/status-pages/{id}A published page's new slug leaves the old one redirecting for 30 days; delete takes the page offline.
POST/status-pages/{id}/publish, /unpublishIdempotent. Publishing needs a component.
GET, PUT/status-pages/{id}/layoutPUT replaces the layout as a whole, keeping the ids you send.
GET, PUT, DELETE/status-pages/{id}/logoThe logo. PUT replaces it as a whole.
GET, PUT, DELETE/status-pages/{id}/domainThe custom domain and the DNS records it needs. PUT sets hostname.
GET/status-pages/{id}/previewThe public snapshot and history, published or not.
POST/status-pages/{id}/preview-linkA signed link to the rendered page, valid for 15 minutes.
GET, POST/status-pages/{id}/incidentsPosted incidents, filter status=open|resolved, cursor paging; post one.
GET, PATCH/status-pages/{id}/incidents/{incidentId}PATCH corrects the title, impact, start, components and links. The status moves by posting an update.
POST/status-pages/{id}/incidents/{incidentId}/updatesIts status becomes the incident's; resolved ends it.

Status pages have their own guide. The public pages themselves are served separately; see Public data.

Conventions

JSON

Request and response fields are camelCase. Bodies are decoded strictly: an unknown member, a member named twice, or a value of the wrong type is a validation error, not ignored. Secrets (header values, Slack and Discord URLs, webhook secrets) are write-only; a Slack or Discord URL is echoed back only as a short hint.

Pagination

List endpoints use cursor pagination: limit and cursor in, a next cursor out, absent at the end. limit is at most 100 (200 for check results). A monitor list cursor carries its sort; a cursor used with another sort is 400 INVALID_PAYLOAD.

Errors

Every error is an RFC 9457 problem (application/problem+json):

{
  "type": "https://docs.norcube.com/errors/limit-reached",
  "title": "Limit reached",
  "status": 403,
  "detail": "Your organization can have at most 10 monitors.",
  "instance": "urn:uuid:3f2c8a8e-6c1d-4a59-9a43-2b1f0f6f9d10",
  "code": "LIMIT_REACHED",
  "reason": "MONITOR_LIMIT",
  "data": { "limit": "10" },
  "timestamp": "2026-11-04T12:00:00Z"
}

Switch on code (and reason where you act on the finer cause), never on detail, which is an English sentence for people. instance is the request id, also returned as the X-Request-ID header.

StatuscodeMeaning
400INVALID_PAYLOADThe body isn't JSON, or a query parameter is malformed.
400VALIDATION_FAILEDA field is wrong; errors lists each one (below). Reason CHANNEL_TYPE_UNAVAILABLE: the channel kind isn't offered.
401UNAUTHORIZEDNo valid token.
402ORG_BLOCKEDThe organization is blocked for unpaid invoices. On-demand checks, new monitors, test sends, codes, creating or publishing status pages, and setting a custom domain are refused.
403FORBIDDENIAM refused, or reason INSUFFICIENT_SCOPE for a CI token.
403LIMIT_REACHEDA limit, with the reason MONITOR_LIMIT, ALERT_CHANNEL_LIMIT, UNVERIFIED_CHANNEL_LIMIT, STATUS_PAGE_LIMIT or STATUS_PAGE_DOMAIN_LIMIT.
404NOT_FOUNDNo such resource, another organization's resource, a malformed id, or no such route. Reason DOMAIN_NOT_SET: the status page has no custom domain.
405METHOD_NOT_ALLOWED
409ALREADY_EXISTS
409CONFLICTThe resource's state refuses it, with reasons such as CODE_EXPIRED, CODE_RECENTLY_SENT, ALREADY_VERIFIED, SLUG_TAKEN, DOMAIN_TAKEN, DOMAINS_DISABLED, INCIDENT_RESOLVED.
409MANAGED_RESOURCEThe monitor belongs to another Norcube service's resource (a DomainRadar saved domain) and can't be deleted or re-targeted here.
413PAYLOAD_TOO_LARGE
429TOO_MANY_REQUESTSA rate limit; see below.
500INTERNAL_ERROR
503UNAVAILABLEA dependency is unreachable. Retry.

New codes and reasons are added over time; treat an unknown one by its HTTP status. Error codes explains each code and what to do about it.

Field errors

A VALIDATION_FAILED problem lists every problem in errors:

{
  "pointer": "/http/timeoutMs",
  "rule": "range",
  "params": { "min": 1000, "max": 30000 },
  "detail": "must be between 1000 and 30000"
}

pointer is the field's JSON Pointer (/http/headers/2/value; query parameters are top level, /from), rule one of required, range, maxLength, oneOf, format, public, unknown, duplicate, type, unsupported and a few more, and params the rule's bounds or values. Show detail, or build your own message from rule and params; treat a rule you don't know as a generic invalid value.

Rate limits

Endpoints that cost a real request are limited per user and per organization, per hour. A refused request is 429 TOO_MANY_REQUESTS with Retry-After, and every limited response carries the IETF RateLimit-Policy and RateLimit headers.

EndpointsPer user / hPer organization / h
POST /monitors, /monitors/{id}/resume, /monitors/{id}/check, /monitors/bulk200600
POST /alert-channels/{id}/verify, /alert-channels/{id}/test2060
POST /alert-channels/{id}/verify/confirm2060

Limits allow a burst of the whole budget, then one request every 3600/n seconds. The user limit is checked first, so a user who is over their limit never spends the organization's budget.

Limits

LimitValue
Monitors per organization10 by default (MONITOR_LIMIT)
Shortest check interval60 s by default; 30 s absolute
Longest check interval86,400 s
Domain monitor intervalat least 3,600 s
Alert channels per organization100 (ALERT_CHANNEL_LIMIT)
Channels awaiting verification3 (UNVERIFIED_CHANNEL_LIMIT)
Routes per monitor20
Status pages per organization1 by default (STATUS_PAGE_LIMIT)
Custom domains per organization10, a safety cap (STATUS_PAGE_DOMAIN_LIMIT)
SMS per organization and day10 by default
Name, description120, 1,000 characters
Raw check resultskept about 30 days
Status changes, monitor eventskept 1 year
Alert events and deliverieskept 90 days
Resolved incidentskept 1 year

The kind-specific bounds (URL length, headers, status rules, thresholds) are on the HTTP, SSL and domain pages.

On this page