Skip to content

Status pages API

10 operations. Every call needs a session cookie or an Authorization: Bearer nct_… token unless marked Public. Conventions, errors and scopes

GET/status-pages

List status pages

The workspace's public status pages with their components. Component service, environment and project names are empty for tokens restricted to other projects.

Response 200 · OK · application/json
PageListOutputBody
  • StatusPageView[] | nullrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/status-pages

Create a status page

Creates an unpublished page. Add components with PUT /status-pages/{pageId}/components (or start from a project's production services with fromProjectId), point the hostname's DNS at the control-plane server (GET /status-pages/{pageId}/dns; with a connected Cloudflare zone NebulaCtrl manages the records itself, see GET /status-pages/{pageId}/cloudflare), then publish with PATCH. 409 hostname_reserved for the dashboard's own host, hostname_conflict for a hostname another page or a service domain uses, slug_conflict for a slug in use. Requires the admin scope.

ParameterInTypeDescription
Idempotency-KeyheaderstringClient-chosen key (<=255 chars). A retry with the same key replays the original response for 24 hours.
Request body · application/json · required
CreatePageInputBody
  • descriptionstring
  • footerstring
    Plain text shown under the public page; line breaks kept
  • fromProjectIdstring
    Start from a project: the page gets that project's production-environment services that have at least one public domain as components (display name = service name, no group, in name order). Editable afterwards. 422 unknown_project when the project is not in the workspace
  • hostnamestring
    The public DNS name the page is served on, e.g. status.example.com. Lowercased; unique; not the dashboard's host or a service domain
  • showUptimeboolean
    Show availability percentages (default true)
  • slugstring
    Unique in the workspace: lowercase letters, digits and hyphens. Default: derived from the title
  • themeenum: auto | light | dark
    Default auto: the visitor's preference
  • titlestringrequired
    Shown as the page's heading
Response 201 · Created · application/json
StatusPageView
  • StatusPageComponentView[] | nullrequired
    In page order
  • createdAtstring (date-time)required
  • descriptionstringrequired
  • footerstringrequired
    Plain text shown under the page
  • hostnamestring | nullrequired
    The public hostname; null until one is set
  • idstringrequired
  • publishedbooleanrequired
    Served on the hostname (needs a hostname and at least one component)
  • showUptimebooleanrequired
    Show availability percentages next to the day bars
  • slugstringrequired
  • themeenum: auto | light | darkrequired
  • titlestringrequired
  • updatedAtstring (date-time)required
Errors
  • 401Unauthorized
  • 403Forbidden
  • 409Conflicthostname_reserved
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

GET/status-pages/{pageId}

Get a status page

ParameterInTypeDescription
pageIdrequiredpathstring
Response 200 · OK · application/json
StatusPageView
  • StatusPageComponentView[] | nullrequired
    In page order
  • createdAtstring (date-time)required
  • descriptionstringrequired
  • footerstringrequired
    Plain text shown under the page
  • hostnamestring | nullrequired
    The public hostname; null until one is set
  • idstringrequired
  • publishedbooleanrequired
    Served on the hostname (needs a hostname and at least one component)
  • showUptimebooleanrequired
    Show availability percentages next to the day bars
  • slugstringrequired
  • themeenum: auto | light | darkrequired
  • titlestringrequired
  • updatedAtstring (date-time)required
Errors
  • 401Unauthorized
  • 403Forbidden
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

PATCH/status-pages/{pageId}

Update a status page

Omitted fields stay as they are. published: true needs a hostname and at least one component (422 publish_needs_hostname, publish_needs_components). Changing the hostname of a published page, or publishing or unpublishing it, re-sends the control-plane server's routes: the page is served (or withdrawn) within moments. Requires the admin scope.

ParameterInTypeDescription
pageIdrequiredpathstring
Request body · application/json · required
UpdatePageInputBody
  • descriptionstring
  • footerstring
    Plain text shown under the public page; line breaks kept
  • hostnamestring
    Empty removes the hostname (the page must not be published)
  • publishedboolean
    true serves the page on its hostname; needs a hostname and at least one component
  • showUptimeboolean
  • slugstring
  • themeenum: auto | light | dark
  • titlestring
Response 200 · OK · application/json
StatusPageView
  • StatusPageComponentView[] | nullrequired
    In page order
  • createdAtstring (date-time)required
  • descriptionstringrequired
  • footerstringrequired
    Plain text shown under the page
  • hostnamestring | nullrequired
    The public hostname; null until one is set
  • idstringrequired
  • publishedbooleanrequired
    Served on the hostname (needs a hostname and at least one component)
  • showUptimebooleanrequired
    Show availability percentages next to the day bars
  • slugstringrequired
  • themeenum: auto | light | darkrequired
  • titlestringrequired
  • updatedAtstring (date-time)required
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 409Conflict
  • 422Unprocessable Entitypublish_needs_hostname
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

DELETE/status-pages/{pageId}

Delete a status page

Deletes the page and withdraws its route. Incidents and maintenance windows posted to it stay. Requires the admin scope.

ParameterInTypeDescription
pageIdrequiredpathstring
Response 204 · No Content

No body.

Errors
  • 401Unauthorized
  • 403Forbidden
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

GET/status-pages/{pageId}/cloudflare

Live Cloudflare DNS status of a status page's hostname

Reads the records of the hostname from Cloudflare and classifies them like GET /domains/{domainId}/dns does: managed, present, wrong_target, missing or not_in_zone. The wanted records are the control-plane server's public addresses (desiredRecords). originLock says whether the page is locked to Cloudflare. Changes nothing. 422 no_hostname while the page has no hostname.

ParameterInTypeDescription
pageIdrequiredpathstring
Response 200 · OK · application/json
DNSStatus
  • checkedAtstring (date-time) | nullrequired
  • configuredbooleanrequired
    The team has a Cloudflare connection
  • conflictstringrequired
    Why adopting is impossible (for example a CNAME next to an A record)
  • RecordViewrequired
  • RecordView[] | nullrequired
  • effectiveProxiedbooleanrequired
  • errorstringrequired
    The last failed sync, empty when the last sync worked
  • managebooleanrequired
    NebulaCtrl keeps this hostname's records in sync
  • messagestringrequired
  • needsAdoptbooleanrequired
  • OriginLockViewrequired
  • proxiedbooleanrequired
    The proxy setting of the hostname (orange cloud)
  • RecordView[] | nullrequired
    A, AAAA and CNAME records of the hostname
  • sslModeenum: | off | flexible | full | strictrequired
    The zone's SSL/TLS mode; empty when unknown
  • statusenum: | managed | present | wrong_target | missing | not_in_zonerequired
  • tunnelbooleanrequired
    The server is behind a Cloudflare Tunnel: the record is a proxied CNAME to it (never for a status page)
  • warningsstring[] | nullrequired
  • zoneIdstringrequired
  • zoneNamestringrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entityno_hostname
  • 500Internal Server Error
  • 502Bad Gateway

Errors are RFC 9457 application/problem+json with a stable code field.

PUT/status-pages/{pageId}/cloudflare

Change the Cloudflare settings of a status page's hostname

Sets the proxy toggle, whether NebulaCtrl manages the page's records and the origin lock opt-in, then syncs. A proxied, published page is locked to Cloudflare. A failed sync is reported as error in the status and retried in the background. Audited as status_page.dns.update. Requires the admin scope.

ParameterInTypeDescription
pageIdrequiredpathstring
Request body · application/json · required
PageDNSSettingsInputBody
  • manageboolean
    Let NebulaCtrl keep the page's DNS records in sync
  • originLockboolean
    Lock the control-plane server's route of the page to Cloudflare while it is proxied (default true). False opts out: anyone who finds the server's IP can bypass Cloudflare. Audited.
  • proxiedboolean
    Proxy through Cloudflare (orange cloud). A proxied page is locked to Cloudflare while it is published.
Response 200 · OK · application/json
DNSStatus
  • checkedAtstring (date-time) | nullrequired
  • configuredbooleanrequired
    The team has a Cloudflare connection
  • conflictstringrequired
    Why adopting is impossible (for example a CNAME next to an A record)
  • RecordViewrequired
  • RecordView[] | nullrequired
  • effectiveProxiedbooleanrequired
  • errorstringrequired
    The last failed sync, empty when the last sync worked
  • managebooleanrequired
    NebulaCtrl keeps this hostname's records in sync
  • messagestringrequired
  • needsAdoptbooleanrequired
  • OriginLockViewrequired
  • proxiedbooleanrequired
    The proxy setting of the hostname (orange cloud)
  • RecordView[] | nullrequired
    A, AAAA and CNAME records of the hostname
  • sslModeenum: | off | flexible | full | strictrequired
    The zone's SSL/TLS mode; empty when unknown
  • statusenum: | managed | present | wrong_target | missing | not_in_zonerequired
  • tunnelbooleanrequired
    The server is behind a Cloudflare Tunnel: the record is a proxied CNAME to it (never for a status page)
  • warningsstring[] | nullrequired
  • zoneIdstringrequired
  • zoneNamestringrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/status-pages/{pageId}/cloudflare/fix

Fix a status page's DNS records

Creates or corrects the records so the hostname points at the control-plane server. A record NebulaCtrl did not create is never modified unless adopt is true (409 adoption_required; 409 dns_conflict when adopting is impossible). 422 not_configured / not_in_zone / no_hostname; 502 when Cloudflare fails. Requires the admin scope.

ParameterInTypeDescription
pageIdrequiredpathstring
Request body · application/json · required
PageDNSFixInputBody
  • adoptboolean
    Take over the existing record NebulaCtrl did not create: it is marked as managed and pointed at the control-plane server
Response 200 · OK · application/json
DNSStatus
  • checkedAtstring (date-time) | nullrequired
  • configuredbooleanrequired
    The team has a Cloudflare connection
  • conflictstringrequired
    Why adopting is impossible (for example a CNAME next to an A record)
  • RecordViewrequired
  • RecordView[] | nullrequired
  • effectiveProxiedbooleanrequired
  • errorstringrequired
    The last failed sync, empty when the last sync worked
  • managebooleanrequired
    NebulaCtrl keeps this hostname's records in sync
  • messagestringrequired
  • needsAdoptbooleanrequired
  • OriginLockViewrequired
  • proxiedbooleanrequired
    The proxy setting of the hostname (orange cloud)
  • RecordView[] | nullrequired
    A, AAAA and CNAME records of the hostname
  • sslModeenum: | off | flexible | full | strictrequired
    The zone's SSL/TLS mode; empty when unknown
  • statusenum: | managed | present | wrong_target | missing | not_in_zonerequired
  • tunnelbooleanrequired
    The server is behind a Cloudflare Tunnel: the record is a proxied CNAME to it (never for a status page)
  • warningsstring[] | nullrequired
  • zoneIdstringrequired
  • zoneNamestringrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 409Conflictadoption_requireddns_conflict
  • 422Unprocessable Entitynot_configured
  • 500Internal Server Error
  • 502Bad Gateway

Errors are RFC 9457 application/problem+json with a stable code field.

PUT/status-pages/{pageId}/components

Set the components of a status page

Replaces the page's components with the ordered list: the services it shows, each under a public display name (default: the service's name, at most 60 characters) and an optional group. Every service must belong to the workspace (422 unknown_service). A published page needs at least one component. Requires the admin scope.

ParameterInTypeDescription
pageIdrequiredpathstring
Request body · application/json · required
SetComponentsInputBody
  • ComponentBody[] | nullrequired
    The full ordered list: it replaces the page's components
Response 200 · OK · application/json
StatusPageView
  • StatusPageComponentView[] | nullrequired
    In page order
  • createdAtstring (date-time)required
  • descriptionstringrequired
  • footerstringrequired
    Plain text shown under the page
  • hostnamestring | nullrequired
    The public hostname; null until one is set
  • idstringrequired
  • publishedbooleanrequired
    Served on the hostname (needs a hostname and at least one component)
  • showUptimebooleanrequired
    Show availability percentages next to the day bars
  • slugstringrequired
  • themeenum: auto | light | darkrequired
  • titlestringrequired
  • updatedAtstring (date-time)required
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 409Conflict
  • 422Unprocessable Entityunknown_service
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

GET/status-pages/{pageId}/dns

Check the DNS of a status page's hostname

The A and AAAA records the hostname needs (the control-plane server's public addresses) and whether the hostname resolves to them right now. A page whose Cloudflare record is proxied is ok when the hostname resolves to Cloudflare's addresses (proxied: true), not mismatch. 422 no_hostname while the page has no hostname.

ParameterInTypeDescription
pageIdrequiredpathstring
Response 200 · OK · application/json
StatusPageDNSCheck
  • hostnamestringrequired
  • messagestringrequired
  • proxiedbooleanrequired
    The page's Cloudflare record is proxied and the hostname resolves to Cloudflare's addresses: status is ok then
  • StatusPageDNSRecord[] | nullrequired
    The records to create: the control-plane server's public addresses
  • resolvedstring[] | nullrequired
    What the hostname resolves to right now
  • statusenum: ok | mismatch | missing | unknownrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 422Unprocessable Entityno_hostname
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.