---
title: "Websites — API v1 and MCP | Nodea Documentation"
description: "Full reference for the Websites module: REST API v1 endpoints (CRUD, run/pause/resume/mute/clone, runs, uptime, maintenance windows), monitoring:* scopes, MCP tools and curl / JSON-RPC examples."
url: "https://nodea.io/en/api-docs/websites"
lang: "en"
updated: "2026-07-16"
---

# Websites — API v1 and MCP | Nodea Documentation

> Websites module reference — API v1 endpoints (CRUD, run/pause/resume/mute/clone actions, runs, uptime, maintenance windows), monitoring:read/write/delete scopes, MCP tools and examples.

The **Websites** module covers full management of monitored websites: listing, create, update, delete and operational actions (run now, pause, resume, mute, clone), plus test-run history, uptime statistics and maintenance windows. This document describes the REST API v1 layer (/api/v1) and the module's MCP tools.

## Authentication and scopes

API v1 authenticates with a Passport personal access token via the Authorization: Bearer \<token\> header. Every route is guarded by a scope matching the operation tier:

- monitoring:read — listing and detail (index, show, runs, uptime, maintenance-windows).
- monitoring:write — create, update and non-destructive actions (run, pause, resume, mute, clone, maintenance-window create/delete).
- monitoring:delete — permanently destroying the website.

The whole module is additionally gated by the Pennant flag features:websites — disabling it hides the module from both the web UI and the API. Rate limit: throttle:api-v1 = 240/min per token user.

## Ownership and visibility

Row-level access is enforced by WebsitePolicy: **view** = owner or accepted FOREIGN share, **manage** = the owner. **Shared admin pool:** a Nodea administrator with confirmed 2FA (when admin 2FA enforcement is on) gets owner-level **manage** and **delete** on the websites of other such administrators; an administrator without 2FA, a former administrator and a customer with a FOREIGN share do not. The permission is computed live from the role and 2FA, not from the share row alone. An id that exists but is not visible to the token yields 403 (policy); an unknown id yields 404 (route model binding). The run action is the exception: allowed on view (owner AND accepted share may trigger a re-check); the other state actions (pause/resume/mute/clone) require manage. Test parameters are masked — basic-auth passwords, credential-bearing headers and raw POST bodies never come back in cleartext (\*\*\*\*+last-4 scheme), so even an accepted share never sees the owner's credentials.

## API v1 — endpoints

MethodPathScopeDescriptionGET/api/v1/websitesmonitoring:readList of websites (owned + accepted shares), paginated. GET/api/v1/websites/{website}monitoring:readWebsite detail with tests; meta.can\_manage. GET/api/v1/websites/{website}/runsmonitoring:readTest-run history (TestRun), newest first. GET/api/v1/websites/{website}/uptimemonitoring:readMonthly uptime aggregates from website\_uptimes. GET/api/v1/websites/{website}/maintenance-windowsmonitoring:readList of the website's maintenance windows. POST/api/v1/websitesmonitoring:writeCreate a website with its test set. Returns 201. PUT/PATCH/api/v1/websites/{website}monitoring:writeUpdate name/URL, upsert tests, sync notification groups and excluded run servers. POST/api/v1/websites/{website}/runmonitoring:writeManually dispatch probes (HEALTH\_CHECK/SSL/WHOIS). 202/422/503. POST/api/v1/websites/{website}/pausemonitoring:writeIdempotent pause (sets paused\_at). POST/api/v1/websites/{website}/resumemonitoring:writeIdempotent resume (clears paused\_at). POST/api/v1/websites/{website}/mutemonitoring:writeToggle paused\_at null↔now (silences notifications). POST/api/v1/websites/{website}/clonemonitoring:writeClone the website for the token user. Returns 201. POST/api/v1/websites/{website}/maintenance-windowsmonitoring:writeCreate a maintenance window. Returns 201. DELETE/api/v1/websites/{website}/maintenance-windows/{maintenanceWindow}monitoring:writeDelete a maintenance window (scoped binding). Returns 204. DELETE/api/v1/websites/{website}monitoring:deletePermanently delete the website (cascade). Returns 204.

### GET /websites — query parameters

- search (string, optional, max 255) — matched against name and URL.
- status (enum: paused|healthy|unhealthy|skipped, optional).
- sort (enum: name|url|created\_at, default created\_at).
- direction (asc|desc).
- per\_page (int 1–100, default 20), page (int ≥1).

Response: a WebsiteResource collection — fields: id, name, url, status (paused→unhealthy→skipped→healthy), paused\_at, ownership (owner|shared), tests\[\], created\_at, updated\_at. Each test: id, type (enum backing value), type\_name, interval, state, state\_name, parameters (masked), last\_run.

### POST/PATCH — body parameters

- url (string, required, SafePublicUrl rule).
- name (string, required, max 255).
- intervals (object keyed by TestType backing value; value: seconds, 0–259200).
- parameters (per-type object; optional — omitting a type keeps its stored config).
- notificationGroupsId\[\] — must belong to the token user (otherwise 422).
- excludedRunServersId\[\] — existing run\_servers.id.

Test types (backing value): 1=HEALTH\_CHECK, 2=SSL, 4=WHOIS, 16=TCP, 32=DNS, 64=PING, 128=SMTP, 256=POP3, 512=IMAP, 1024=UDP.

### GET /websites/{website}/runs — response fields

Each item is a TestRunResource: id, test\_id, state (TestRunState backing value), state\_name (stable machine label), reason, reason\_code, skip\_reason, failure\_status\_code, details, run\_server (when included) and created\_at.

reason\_code describes a probe that **ran and failed** (failure taxonomy: timeout, dns\_failure, connection\_refused, …). skip\_reason describes a probe that was **deliberately not run**, and takes the values seeder\_host, ssrf\_blocked or maintenance\_window. The two fields are mutually exclusive.

Note for integrators: a seeder skip and an SSRF block share state = 7 and state\_name = "Skipped (seeder)" — kept for backwards compatibility. Read skip\_reason to tell them apart, for example to detect that monitoring is not running because the host resolves to a private address.

### Error codes

- 401 — missing/invalid token.
- 403 — missing required scope or policy (existing, foreign resource without access).
- 404 — unknown id.
- 422 — validation (e.g. no runnable test on run, code no\_runnable\_test).
- 503 — no healthy run server available to execute probes (code no\_run\_server\_available).

## Examples — API

### List websites filtered by status

curl -s https://app.nodea.io/api/v1/websites?status=unhealthy&per\_page=10 \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Accept: application/json"

### Create a website with a 60s health check

curl -s -X POST https://app.nodea.io/api/v1/websites \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Content-Type: application/json" \\ -d '{ "name": "Shop", "url": "https://shop.example.com", "intervals": { "1": 60 }, "parameters": { "1": { "timeout": 10, "status\_codes": "200" } } }'

### Manually dispatch probes

curl -s -X POST https://app.nodea.io/api/v1/websites/{id}/run \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Content-Type: application/json" \\ -d '{ "test\_id": 123 }'

## MCP — tools

MCP tools share the auth and scope model with API v1 (Bearer + scope checked in ensureScope) and the features:websites flag (a tool on an inactive flag disappears from tools/list). Resource ids are UUIDs passed as strings. A missing and a foreign id raise the same error (no cross-tenant enumeration).

ToolScopeTypeDescriptionwebsites-listmonitoring:readread-onlyList websites; filters search, status (paused/healthy/unhealthy), page, per\_page (max 50). website-getmonitoring:readread-onlyWebsite detail with tests. Param: id. website-createmonitoring:writewriteCreate a website; name, url, tests\[\] (type/interval/parameters, min 1). website-updatemonitoring:writeidempotentUpdate name/url + upsert tests\[\]. Owner only. website-deletemonitoring:deletedestructivePermanent delete; requires confirm: true. website-run-nowmonitoring:writewriteDispatch probes; optional test\_type. Returns dispatched/skipped. website-set-pausedmonitoring:writeidempotentPause/resume via paused: true|false (not a toggle). website-runsmonitoring:readread-onlyRun history; test\_type, page, per\_page (max 50). website-uptimemonitoring:readread-onlyMonthly uptime; months (default 12, max 60). website-maintenance-windowsmonitoring:readread-onlyList maintenance windows with an active\_now flag. website-maintenance-window-createmonitoring:writewriteCreate a window; starts\_at/ends\_at (wall-clock in timezone), recurrence\_type, weekdays. website-maintenance-window-deletemonitoring:writedestructive (annotation)Delete a window by id. Scope stays write.

### Example — MCP (JSON-RPC)

{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "website-run-now", "arguments": { "id": "9b1f...uuid", "test\_type": 1 } } } { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "website-set-paused", "arguments": { "id": "9b1f...uuid", "paused": true } } }

## Related

- [Heartbeats — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/heartbeats)
- [Reports (SSL / WHOIS) — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/reports)
- [Status Pages — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/status-pages)

---

Languages: [pl](https://nodea.pl/pl/dokumentacja-api/websites.md)  
Site index: [llms.txt](https://nodea.io/en/llms.txt)
