riferimento api
Le 137 operazioni della REST API dell'hub, le stesse che usa l'interfaccia web. Ogni hub serve questo documento su /api/openapi.json e una Swagger UI interattiva su /api/docs, dove puoi provare le richieste con la tua sessione o un token. Scaricalo qui come openapi.json (OpenAPI 3.1, ultima versione su main) per generare un client. Le descrizioni degli endpoint vengono dal documento OpenAPI e sono in inglese.
──autenticazione
Crea un token in impostazioni → token api e mandalo come bearer token. I body sono JSON, gli orari sono millisecondi Unix, gli errori sono {"error": "..."}.
curl -H "Authorization: Bearer ptk_..." https://status.example.com/api/monitorscurl -X POST -H "Authorization: Bearer ptk_..." -H "Content-Type: application/json" \-d '{"name":"Website","type":"http","target":"https://example.com"}' \https://status.example.com/api/monitors
Ogni endpoint indica chi può chiamarlo:
- pubblico: nessuna autenticazione
- token di lettura: qualsiasi utente o token API, basta un token di lettura
- token di scrittura · admin: un admin, con un token di scrittura o la sessione del browser; utenti in sola lettura e token di lettura ricevono 403
- sessione del browser: solo sessione del browser, i token API ricevono 403
- sessione admin: solo la sessione del browser di un admin
Gli utenti in sola lettura vedono le password nelle destinazioni dei monitor, negli header e nei canali di notifica come ********.
──monitors
Checks, their history and heartbeat (push) URLs.
/api/monitorsList monitorstoken di letturaEvery monitor with its last 24 hours. Secrets (passwords in targets, headers, bodies, push tokens) are hidden from read-only users.
| group | query · string | Group id, or none for monitors without a group. |
| tag | query · integer | Tag id. |
risposta 200 · Monitors.
[{"name": null,"type": null,"target": null,"interval_sec": null,"timeout_ms": null,"retries": null,"method": null,"expected_status": null,"keyword": null,"keyword_invert": null,"dns_record": null,"dns_server": null,"dns_expected": null,"ignore_tls": null,"ip_family": null,"headers": null,"body": null,"assertions": null,"grace_sec": null,"degraded_pct": null,"locations": null,"group_id": null,"tag_ids": null,"notifiers": null,"cert_expiry_alert": null,"domain_expiry_alert": null,"paused": null,"id": 1,"push_token": "string","status": "up","status_since": 1761868800000,"created_at": 1761868800000,"uptime_24h": 12.5,"avg_ms_24h": 12.5,"last": {"id": null,"monitor_id": null,"location": null,"ts": null,"ok": null,"latency_ms": null,"status_code": null,"message": null,"cert_expiry": null,"maint": null,"timing": null},"bars": [{"t": null,"total": null,"ok": null,"degraded": null,"maint": null,"avg_ms": null}],"expiry": [{"monitor_id": null,"kind": null,"subject": null,"expires_at": null,"issuer": null,"source": null,"error": null,"checked_at": null}],"maintenance": {"id": 1,"name": "string","end_at": 1761868800000}}]
/api/monitorsCreate a monitortoken di scrittura · admin| name* | string | |
| type* | string | uno tra: http keyword tcp ping dns push postgres mysql redis mongodb docker grpc smtp mcp |
| target | string | What to check, depends on the type: an http(s) URL (http, keyword, mcp), host:port (tcp, IPv6 in brackets: [2001:db8::1]:443), a hostname or IP (ping, dns), a connection URL (postgres://, mysql://, redis://, mongodb://), a container name or id (docker), grpc[s]://host:port[/service], smtp[s]://host[:port]. Empty for push. |
| interval_sec | integer | Seconds between checks, 10 to 86400 (push: expected period, up to 7 days). 0 = default (60, push 3600). |
| timeout_ms | integer | 500 to 60000. 0 = 10000. |
| retries | integer | Consecutive failures before a location counts as down, 1 to 10. |
| method | string | HTTP method for http/keyword (default GET). For mcp: the transport, auto, streamable or sse. |
| expected_status | string | Accepted HTTP status codes, e.g. "200-299" or "200,301". For mcp: the minimum number of tools. |
| keyword | string | keyword: text that must appear in the body. mcp: comma separated tool names that must be listed. |
| keyword_invert | boolean | keyword: fail when the text is found. |
| dns_record | string | dns: record type (A, AAAA, CNAME, MX, TXT, NS...). |
| dns_server | string | dns: resolver to query, host[:port]. Empty = system resolver. |
| dns_expected | string | dns: value that must be in the answer. |
| ignore_tls | boolean | Accept invalid TLS certificates. |
| ip_family | integer | 0 = instance default (GeneralSettings.ip_family), 1 = IPv4 or IPv6 (whichever connects first), 4 = IPv4 only, 6 = IPv6 only, 46 = IPv4 and IPv6 (both checked, the check fails when either fails). Locations without IPv6 check over IPv4 with 0 and 1; 6 and 46 are refused on them (see Location.ipv6). Ignored for push and docker.uno tra: 0 1 4 6 46 |
| headers | object | Request headers (http, keyword, mcp). |
| body | string | Request body (http, keyword). |
| assertions | Assertion[] | JSON response rules, http and keyword only, at most 20. |
| grace_sec | integer | push: extra time after the period before the monitor goes down, 0 to 86400. |
| degraded_pct | integer | Share of failing locations (percent) from which the monitor is degraded, 1 to 50. 0 = instance default. |
| locations | string[] | Where the check runs: "local" (the hub) and/or "agent:<id>". Empty = local. See GET /api/locations. |
| group_id | integer | Group, 0 = none. |
| tag_ids | integer[] | |
| notifiers | ChannelLink[] | Notification channels linked to this monitor one by one (notifier_id + events). Channels whose scope covers the monitor need no link. Left out on update: the links are kept; [] removes them. |
| cert_expiry_alert | boolean | Alert before the TLS certificate expires (https targets). |
| domain_expiry_alert | boolean | Alert before the domain registration expires. |
| paused | boolean | Create the monitor paused. Ignored on update: use POST /api/monitors/{id}/pause. |
{"name": "Website","type": "http","target": "https://example.com","interval_sec": 60,"timeout_ms": 10000,"retries": 2,"method": "GET","expected_status": "200-299","locations": ["local"],"tag_ids": [],"group_id": 0,"assertions": [{"path": "status","op": "eq","value": "ok"}],"cert_expiry_alert": true}
risposta 201 · Created.
{"name": "Website","type": "http","target": "https://example.com","interval_sec": 60,"timeout_ms": 10000,"retries": 2,"method": "GET","expected_status": "200-299","locations": ["local"],"tag_ids": [],"group_id": 0,"assertions": [{"path": "status","op": "eq","value": "ok"}],"cert_expiry_alert": true,"id": 7,"headers": {},"body": "","keyword": "","keyword_invert": false,"dns_record": "","dns_server": "","dns_expected": "","ignore_tls": false,"push_token": "","grace_sec": 0,"degraded_pct": 0,"ip_family": 0,"domain_expiry_alert": false,"paused": false,"status": "up","status_since": 1761868800000,"created_at": 1761868800000}
/api/monitors/{id}Get a monitortoken di letturaIncludes 7 and 30 day uptime and the last result of each location.
| id* | path · integer | Id of the monitor. |
risposta 200 · Monitor.
{"id": null,"push_token": null,"status": null,"status_since": null,"created_at": null,"uptime_24h": 12.5,"avg_ms_24h": 12.5,"last": null,"bars": [null],"expiry": [null],"maintenance": {"id": null,"name": null,"end_at": null},"uptime_7d": 12.5,"uptime_30d": 12.5,"location_status": [{"id": "string","name": "string","online": true,"last": null}],"push_url": "string"}
/api/monitors/{id}Update a monitortoken di scrittura · adminReplaces the monitor: send every field.
| id* | path · integer | Id of the monitor. |
| name* | string | |
| type* | string | uno tra: http keyword tcp ping dns push postgres mysql redis mongodb docker grpc smtp mcp |
| target | string | What to check, depends on the type: an http(s) URL (http, keyword, mcp), host:port (tcp, IPv6 in brackets: [2001:db8::1]:443), a hostname or IP (ping, dns), a connection URL (postgres://, mysql://, redis://, mongodb://), a container name or id (docker), grpc[s]://host:port[/service], smtp[s]://host[:port]. Empty for push. |
| interval_sec | integer | Seconds between checks, 10 to 86400 (push: expected period, up to 7 days). 0 = default (60, push 3600). |
| timeout_ms | integer | 500 to 60000. 0 = 10000. |
| retries | integer | Consecutive failures before a location counts as down, 1 to 10. |
| method | string | HTTP method for http/keyword (default GET). For mcp: the transport, auto, streamable or sse. |
| expected_status | string | Accepted HTTP status codes, e.g. "200-299" or "200,301". For mcp: the minimum number of tools. |
| keyword | string | keyword: text that must appear in the body. mcp: comma separated tool names that must be listed. |
| keyword_invert | boolean | keyword: fail when the text is found. |
| dns_record | string | dns: record type (A, AAAA, CNAME, MX, TXT, NS...). |
| dns_server | string | dns: resolver to query, host[:port]. Empty = system resolver. |
| dns_expected | string | dns: value that must be in the answer. |
| ignore_tls | boolean | Accept invalid TLS certificates. |
| ip_family | integer | 0 = instance default (GeneralSettings.ip_family), 1 = IPv4 or IPv6 (whichever connects first), 4 = IPv4 only, 6 = IPv6 only, 46 = IPv4 and IPv6 (both checked, the check fails when either fails). Locations without IPv6 check over IPv4 with 0 and 1; 6 and 46 are refused on them (see Location.ipv6). Ignored for push and docker.uno tra: 0 1 4 6 46 |
| headers | object | Request headers (http, keyword, mcp). |
| body | string | Request body (http, keyword). |
| assertions | Assertion[] | JSON response rules, http and keyword only, at most 20. |
| grace_sec | integer | push: extra time after the period before the monitor goes down, 0 to 86400. |
| degraded_pct | integer | Share of failing locations (percent) from which the monitor is degraded, 1 to 50. 0 = instance default. |
| locations | string[] | Where the check runs: "local" (the hub) and/or "agent:<id>". Empty = local. See GET /api/locations. |
| group_id | integer | Group, 0 = none. |
| tag_ids | integer[] | |
| notifiers | ChannelLink[] | Notification channels linked to this monitor one by one (notifier_id + events). Channels whose scope covers the monitor need no link. Left out on update: the links are kept; [] removes them. |
| cert_expiry_alert | boolean | Alert before the TLS certificate expires (https targets). |
| domain_expiry_alert | boolean | Alert before the domain registration expires. |
| paused | boolean | Create the monitor paused. Ignored on update: use POST /api/monitors/{id}/pause. |
{"name": "Website","type": "http","target": "https://example.com","interval_sec": 60,"timeout_ms": 10000,"retries": 2,"method": "GET","expected_status": "200-299","locations": ["local"],"tag_ids": [],"group_id": 0,"assertions": [{"path": "status","op": "eq","value": "ok"}],"cert_expiry_alert": true}
risposta 200 · Updated.
{"name": "Website","type": "http","target": "https://example.com","interval_sec": 60,"timeout_ms": 10000,"retries": 2,"method": "GET","expected_status": "200-299","locations": ["local"],"tag_ids": [],"group_id": 0,"assertions": [{"path": "status","op": "eq","value": "ok"}],"cert_expiry_alert": true,"id": 7,"headers": {},"body": "","keyword": "","keyword_invert": false,"dns_record": "","dns_server": "","dns_expected": "","ignore_tls": false,"push_token": "","grace_sec": 0,"degraded_pct": 0,"ip_family": 0,"domain_expiry_alert": false,"paused": false,"status": "up","status_since": 1761868800000,"created_at": 1761868800000}
/api/monitors/{id}Delete a monitortoken di scrittura · adminAlso deletes its history.
| id* | path · integer | Id of the monitor. |
risposta 200 · Done.
{"ok": true}
/api/monitors/{id}/expiryCheck expiry dates nowtoken di scrittura · adminLooks up the TLS certificate and domain expiry.
| id* | path · integer | Id of the monitor. |
risposta 200 · Expiry checks.
[{"monitor_id": 1,"kind": "cert","subject": "string","expires_at": 1761868800000,"issuer": "string","source": "tls","error": "string","checked_at": 1761868800000}]
/api/monitors/{id}/pausePause or resumetoken di scrittura · adminPausing resolves the open incident.
| id* | path · integer | Id of the monitor. |
| paused* | boolean |
{"paused": true}
risposta 200 · Monitor.
{"name": "string","type": "http","target": "https://example.com/health","interval_sec": 1,"timeout_ms": 1,"retries": 1,"method": "string","expected_status": "string","keyword": "string","keyword_invert": true,"dns_record": "string","dns_server": "string","dns_expected": "string","ignore_tls": true,"ip_family": 0,"headers": {"key": "string"},"body": "string","assertions": [{"path": null,"op": null,"value": null}],"grace_sec": 1,"degraded_pct": 1,"locations": ["local","agent:2"],"group_id": 1,"tag_ids": [1],"notifiers": [{"monitor_id": null,"notifier_id": null,"events": null}],"cert_expiry_alert": true,"domain_expiry_alert": true,"paused": true,"id": 1,"push_token": "string","status": "up","status_since": 1761868800000,"created_at": 1761868800000}
/api/monitors/{id}/push-tokenRotate the heartbeat URLtoken di scrittura · adminpush monitors only: the old URL stops working.
| id* | path · integer | Id of the monitor. |
risposta 200 · Monitor with the new push_token.
{"name": "string","type": "http","target": "https://example.com/health","interval_sec": 1,"timeout_ms": 1,"retries": 1,"method": "string","expected_status": "string","keyword": "string","keyword_invert": true,"dns_record": "string","dns_server": "string","dns_expected": "string","ignore_tls": true,"ip_family": 0,"headers": {"key": "string"},"body": "string","assertions": [{"path": null,"op": null,"value": null}],"grace_sec": 1,"degraded_pct": 1,"locations": ["local","agent:2"],"group_id": 1,"tag_ids": [1],"notifiers": [{"monitor_id": null,"notifier_id": null,"events": null}],"cert_expiry_alert": true,"domain_expiry_alert": true,"paused": true,"id": 1,"push_token": "string","status": "up","status_since": 1761868800000,"created_at": 1761868800000}
/api/monitors/{id}/resultsLatest check resultstoken di lettura| id* | path · integer | Id of the monitor. |
| limit | query · integer | Number of results. |
| before | query · integer | Only results older than this time (unix ms), to load the next page. |
| from | query · integer | Only results at or after this time (unix ms). |
| to | query · integer | Only results before this time (unix ms). |
| failed | query · boolean | Only failed checks. |
risposta 200 · Results, newest first.
{"results": [{"id": 1,"monitor_id": 1,"location": "string","ts": 1761868800000,"ok": true,"latency_ms": 12.5,"status_code": 1,"message": "string","cert_expiry": 1,"maint": true,"timing": {"dns_ms": 12.5,"connect_ms": 12.5,"tls_ms": 12.5,"ttfb_ms": 12.5,"total_ms": 12.5,"n": 1}}],"locations": {"key": "string"}}
/api/monitors/{id}/seriesResponse time seriestoken di letturaAbout 90 buckets per location over the range, or over a custom window with start and end. stats sums up the window and the one of the same length just before it.
| id* | path · integer | Id of the monitor. |
| range | query · string | Time range.uno tra: 1h 6h 24h 7d 30d |
| start | query · integer | Custom window start, unix ms. With end, replaces range; 5 minutes to 400 days long. |
| end | query · integer | Custom window end, unix ms (capped at now). |
risposta 200 · Series.
{"from": 1761868800000,"to": 1761868800000,"size": 1,"locations": [{"id": "string","name": "string"}],"buckets": [{"t": 1761868800000,"location": "string","total": 1,"ok": 1,"degraded": 1,"maint": 1,"avg_ms": 12.5,"min_ms": 12.5,"max_ms": 12.5,"phases": {"dns_ms": 12.5,"connect_ms": 12.5,"tls_ms": 12.5,"ttfb_ms": 12.5,"total_ms": 12.5,"n": 1}}],"stats": {"current": {"from": 1,"to": 1,"uptime": 12.5,"avg_ms": 12.5,"percentiles": {"n": 1,"from": 1,"p50": 12.5,"p95": 12.5,"p99": 12.5}},"previous": {"from": 1,"to": 1,"uptime": 12.5,"avg_ms": 12.5,"percentiles": {"n": 1,"from": 1,"p50": 12.5,"p95": 12.5,"p99": 12.5}}}}
/api/monitors/{id}/testCheck nowtoken di scrittura · adminRuns the saved monitor's check once at each of its locations and returns the results. They are not stored and do not change the status.
| id* | path · integer | Id of the monitor. |
risposta 200 · One result per location of the monitor. Nothing is stored and no alert is sent.
{"results": [{"location": "local","name": "hub","ok": true,"latency_ms": 84.2,"status_code": 200},{"location": "agent:2","name": "fra · Frankfurt","ok": false,"skipped": true,"latency_ms": 0,"message": "agent offline"}]}
/api/monitors/bulkChange several monitorstoken di scrittura · adminPauses, resumes, deletes or moves to a group several monitors at once. Pausing resolves their open incidents. With action group, group_id 0 removes the monitors from their group.
| ids* | integer[] | |
| action* | string | uno tra: pause resume delete group |
| group_id | integer | Action group only: the group, 0 for none. |
{"ids": [1,2,3],"action": "pause"}
risposta 200 · Number of monitors selected.
{"count": 3}
/api/monitors/testTest an unsaved monitortoken di scrittura · adminRuns the check once at each location of the monitor in the body and waits for the results (up to the timeout plus 5 seconds). Nothing is saved. Heartbeat (push) monitors cannot be tested.
| name* | string | |
| type* | string | uno tra: http keyword tcp ping dns push postgres mysql redis mongodb docker grpc smtp mcp |
| target | string | What to check, depends on the type: an http(s) URL (http, keyword, mcp), host:port (tcp, IPv6 in brackets: [2001:db8::1]:443), a hostname or IP (ping, dns), a connection URL (postgres://, mysql://, redis://, mongodb://), a container name or id (docker), grpc[s]://host:port[/service], smtp[s]://host[:port]. Empty for push. |
| interval_sec | integer | Seconds between checks, 10 to 86400 (push: expected period, up to 7 days). 0 = default (60, push 3600). |
| timeout_ms | integer | 500 to 60000. 0 = 10000. |
| retries | integer | Consecutive failures before a location counts as down, 1 to 10. |
| method | string | HTTP method for http/keyword (default GET). For mcp: the transport, auto, streamable or sse. |
| expected_status | string | Accepted HTTP status codes, e.g. "200-299" or "200,301". For mcp: the minimum number of tools. |
| keyword | string | keyword: text that must appear in the body. mcp: comma separated tool names that must be listed. |
| keyword_invert | boolean | keyword: fail when the text is found. |
| dns_record | string | dns: record type (A, AAAA, CNAME, MX, TXT, NS...). |
| dns_server | string | dns: resolver to query, host[:port]. Empty = system resolver. |
| dns_expected | string | dns: value that must be in the answer. |
| ignore_tls | boolean | Accept invalid TLS certificates. |
| ip_family | integer | 0 = instance default (GeneralSettings.ip_family), 1 = IPv4 or IPv6 (whichever connects first), 4 = IPv4 only, 6 = IPv6 only, 46 = IPv4 and IPv6 (both checked, the check fails when either fails). Locations without IPv6 check over IPv4 with 0 and 1; 6 and 46 are refused on them (see Location.ipv6). Ignored for push and docker.uno tra: 0 1 4 6 46 |
| headers | object | Request headers (http, keyword, mcp). |
| body | string | Request body (http, keyword). |
| assertions | Assertion[] | JSON response rules, http and keyword only, at most 20. |
| grace_sec | integer | push: extra time after the period before the monitor goes down, 0 to 86400. |
| degraded_pct | integer | Share of failing locations (percent) from which the monitor is degraded, 1 to 50. 0 = instance default. |
| locations | string[] | Where the check runs: "local" (the hub) and/or "agent:<id>". Empty = local. See GET /api/locations. |
| group_id | integer | Group, 0 = none. |
| tag_ids | integer[] | |
| notifiers | ChannelLink[] | Notification channels linked to this monitor one by one (notifier_id + events). Channels whose scope covers the monitor need no link. Left out on update: the links are kept; [] removes them. |
| cert_expiry_alert | boolean | Alert before the TLS certificate expires (https targets). |
| domain_expiry_alert | boolean | Alert before the domain registration expires. |
| paused | boolean | Create the monitor paused. Ignored on update: use POST /api/monitors/{id}/pause. |
{"type": "http","target": "https://example.com/health","timeout_ms": 10000,"locations": ["local"]}
risposta 200 · One result per location of the monitor. Nothing is stored and no alert is sent.
{"results": [{"location": "local","name": "hub","ok": true,"latency_ms": 84.2,"status_code": 200},{"location": "agent:2","name": "fra · Frankfurt","ok": false,"skipped": true,"latency_ms": 0,"message": "agent offline"}]}
/api/push/{token}Send a heartbeatpubblicoHeartbeat of a push monitor. Any HTTP method works (GET and POST shown here). The token in the URL is the authentication.
curl -fsS https://status.example.com/api/push/<token>?status=up&msg=ok&ping=12
| token* | path · string | |
| status | query · string | Result of the job.uno tra: up down ok fail 0 1 |
| msg | query · string | Message shown in the history, up to 200 characters. |
| ping | query · number | Duration or latency in ms. |
risposta 200 · Recorded.
{"ok": true}
/api/push/{token}Send a heartbeatpubblicoHeartbeat of a push monitor. Any HTTP method works (GET and POST shown here). The token in the URL is the authentication.
curl -fsS https://status.example.com/api/push/<token>?status=up&msg=ok&ping=12
| token* | path · string | |
| status | query · string | Result of the job.uno tra: up down ok fail 0 1 |
| msg | query · string | Message shown in the history, up to 200 characters. |
| ping | query · number | Duration or latency in ms. |
risposta 200 · Recorded.
{"ok": true}
──incidents
Down periods of monitors.
/api/incidentsList incidentstoken di letturaThe latest 100, open ones first.
| monitor | query · integer | Only this monitor. |
| state | query · string | Only open or only resolved incidents.uno tra: open resolved |
| since | query · integer | Only incidents open at some point after this time (unix ms). |
| limit | query · integer | Maximum number of incidents. |
risposta 200 · Incidents.
[{"id": 1,"monitor_id": 1,"monitor_name": "string","started_at": 1761868800000,"resolved_at": 1,"cause": "string"}]
──maintenance
Windows during which monitors don't alert.
/api/maintenancesList maintenance windowstoken di letturarisposta 200 · Windows.
[{"name": "string","description": "string","all_monitors": true,"monitor_ids": [1],"group_ids": [1],"tag_ids": [1],"kind": "once","start_at": 1,"end_at": 1,"start_time": "02:00","duration_min": 1,"weekdays": [1],"month_days": [1],"timezone": "Europe/Rome","enabled": true,"id": 1,"created_at": 1761868800000,"active": true,"active_end": 1761868800000,"next_start": 1,"next_end": 1761868800000}]
/api/maintenancesCreate a maintenance windowtoken di scrittura · adminCovered monitors don't alert and don't count as down.
| name* | string | |
| description | string | |
| all_monitors | boolean | Covers every monitor. |
| monitor_ids | integer[] | |
| group_ids | integer[] | Every monitor of these groups. |
| tag_ids | integer[] | Every monitor with one of these tags. |
| kind* | string | uno tra: once daily weekly monthly |
| start_at | integer | once: start. Recurring: first day the schedule is valid (0 = now). |
| end_at | integer | once: end. Recurring: last day the schedule is valid (0 = forever). |
| start_time | string | Recurring: local start time, HH:MM. |
| duration_min | integer | Recurring: length in minutes, up to 7 days. |
| weekdays | integer[] | weekly: 0 = Sunday … 6 = Saturday. |
| month_days | integer[] | monthly: 1 … 31. |
| timezone | string | IANA time zone, default UTC. |
| enabled | boolean |
{"name": "DB upgrade","description": "","all_monitors": false,"monitor_ids": [7],"group_ids": [],"tag_ids": [],"kind": "weekly","start_time": "02:00","duration_min": 60,"weekdays": [0],"month_days": [],"timezone": "Europe/Rome","enabled": true}
risposta 201 · Created.
{"name": "string","description": "string","all_monitors": true,"monitor_ids": [1],"group_ids": [1],"tag_ids": [1],"kind": "once","start_at": 1,"end_at": 1,"start_time": "02:00","duration_min": 1,"weekdays": [1],"month_days": [1],"timezone": "Europe/Rome","enabled": true,"id": 1,"created_at": 1761868800000,"active": true,"active_end": 1761868800000,"next_start": 1,"next_end": 1761868800000}
/api/maintenances/{id}Update a maintenance windowtoken di scrittura · admin| id* | path · integer | Id of the maintenance window. |
| name* | string | |
| description | string | |
| all_monitors | boolean | Covers every monitor. |
| monitor_ids | integer[] | |
| group_ids | integer[] | Every monitor of these groups. |
| tag_ids | integer[] | Every monitor with one of these tags. |
| kind* | string | uno tra: once daily weekly monthly |
| start_at | integer | once: start. Recurring: first day the schedule is valid (0 = now). |
| end_at | integer | once: end. Recurring: last day the schedule is valid (0 = forever). |
| start_time | string | Recurring: local start time, HH:MM. |
| duration_min | integer | Recurring: length in minutes, up to 7 days. |
| weekdays | integer[] | weekly: 0 = Sunday … 6 = Saturday. |
| month_days | integer[] | monthly: 1 … 31. |
| timezone | string | IANA time zone, default UTC. |
| enabled | boolean |
{"name": "DB upgrade","description": "","all_monitors": false,"monitor_ids": [7],"group_ids": [],"tag_ids": [],"kind": "weekly","start_time": "02:00","duration_min": 60,"weekdays": [0],"month_days": [],"timezone": "Europe/Rome","enabled": true}
risposta 200 · Updated.
{"name": "string","description": "string","all_monitors": true,"monitor_ids": [1],"group_ids": [1],"tag_ids": [1],"kind": "once","start_at": 1,"end_at": 1,"start_time": "02:00","duration_min": 1,"weekdays": [1],"month_days": [1],"timezone": "Europe/Rome","enabled": true,"id": 1,"created_at": 1761868800000,"active": true,"active_end": 1761868800000,"next_start": 1,"next_end": 1761868800000}
/api/maintenances/{id}Delete a maintenance windowtoken di scrittura · admin| id* | path · integer | Id of the maintenance window. |
risposta 200 · Done.
{"ok": true}
──groups and tags
Organize monitors; status pages and maintenance windows can select them by group or tag.
/api/groupsList groupstoken di letturarisposta 200 · Groups.
[{"id": 1,"name": "string","description": "string","monitors": 1,"created_at": 1761868800000}]
/api/groupsCreate a grouptoken di scrittura · admin| id | integer | |
| name* | string | Unique, up to 64 characters. |
| description | string | Up to 500 characters. |
| monitors | integer | Number of monitors in the group (read only). |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
{"name": "Production","description": "Customer facing"}
risposta 201 · Created.
{"id": 1,"name": "string","description": "string","monitors": 1,"created_at": 1761868800000}
/api/groups/{id}Update a grouptoken di scrittura · admin| id* | path · integer | Id of the group. |
| id | integer | |
| name* | string | Unique, up to 64 characters. |
| description | string | Up to 500 characters. |
| monitors | integer | Number of monitors in the group (read only). |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
{"id": 1,"name": "string","description": "string","monitors": 1,"created_at": 1761868800000}
risposta 200 · Updated.
{"id": 1,"name": "string","description": "string","monitors": 1,"created_at": 1761868800000}
/api/groups/{id}Delete a grouptoken di scrittura · adminIts monitors are kept, without a group.
| id* | path · integer | Id of the group. |
risposta 200 · Done.
{"ok": true}
/api/groups/{id}/monitorsSet the monitors of a grouptoken di scrittura · adminMonitors listed here move to this group; monitors of the group not listed leave it.
| id* | path · integer | Id of the group. |
| monitor_ids* | integer[] |
{"monitor_ids": [7,8]}
risposta 200 · Group.
{"id": 1,"name": "string","description": "string","monitors": 1,"created_at": 1761868800000}
/api/tagsList tagstoken di letturarisposta 200 · Tags.
[{"id": 1,"name": "string","color": "red","monitors": 1,"created_at": 1761868800000}]
/api/tagsCreate a tagtoken di scrittura · admin| id | integer | |
| name* | string | Unique, up to 32 characters, no commas. |
| color | string | uno tra: "" red orange yellow green cyan blue purple pink gray |
| monitors | integer | Number of tagged monitors (read only). |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
{"name": "critical","color": "red"}
risposta 201 · Created.
{"id": 1,"name": "string","color": "red","monitors": 1,"created_at": 1761868800000}
/api/tags/{id}Update a tagtoken di scrittura · admin| id* | path · integer | Id of the tag. |
| id | integer | |
| name* | string | Unique, up to 32 characters, no commas. |
| color | string | uno tra: "" red orange yellow green cyan blue purple pink gray |
| monitors | integer | Number of tagged monitors (read only). |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
{"id": 1,"name": "string","color": "red","monitors": 1,"created_at": 1761868800000}
risposta 200 · Updated.
{"id": 1,"name": "string","color": "red","monitors": 1,"created_at": 1761868800000}
/api/tags/{id}Delete a tagtoken di scrittura · admin| id* | path · integer | Id of the tag. |
risposta 200 · Done.
{"ok": true}
──agents
Remote check locations connected to the hub.
/api/agentsList agentstoken di letturarisposta 200 · Agents.
{"agents": [{"id": 1,"name": "string","location": "string","version": "string","hostname": "string","platform": "string","remote_addr": "string","last_seen": 1761868800000,"created_at": 1761868800000,"ipv6": 0,"location_id": "agent:2","online": true,"monitors": 1,"update_available": true,"self_update": true,"no_update_reason": "string","update_state": "updating","update_error": "string"}],"hub_url": "string","hub_version": "string","auto_update": true}
/api/agentsCreate an agenttoken di scrittura · adminThe agent token is returned once.
| name* | string | |
| location | string |
{"name": "fra-1","location": "Frankfurt"}
risposta 201 · Created.
{"agent": {"id": 1,"name": "string","location": "string","version": "string","hostname": "string","platform": "string","remote_addr": "string","last_seen": 1761868800000,"created_at": 1761868800000,"ipv6": 0,"location_id": "agent:2","online": true,"monitors": 1,"update_available": true,"self_update": true,"no_update_reason": "string","update_state": "updating","update_error": "string"},"token": "string","hub_url": "string"}
/api/agents/{id}Rename an agenttoken di scrittura · admin| id* | path · integer | Id of the agent. |
| name* | string | |
| location | string |
{"name": "string","location": "string"}
risposta 200 · Done.
{"ok": true}
/api/agents/{id}Delete an agenttoken di scrittura · adminDisconnects it and removes it from monitor locations.
| id* | path · integer | Id of the agent. |
risposta 200 · Done.
{"ok": true}
/api/agents/{id}/monitorsPreview compatible monitorstoken di letturaMonitors this agent can be added to, and the ones skipped with the reason. Heartbeat monitors have no locations and are left out.
| id* | path · integer | Id of the agent. |
risposta 200 · Compatible and skipped monitors.
{"compatible": [{"id": 1,"name": "string","type": "string","target": "string","reason": "no_ipv6"}],"skipped": [{"id": 1,"name": "string","type": "string","target": "string","reason": "no_ipv6"}],"already": 1}
/api/agents/{id}/monitorsAdd the agent to monitorstoken di scrittura · adminAdds the agent's location to the given monitors. Monitors that are not compatible are skipped and returned with the reason.
| id* | path · integer | Id of the agent. |
| monitor_ids* | integer[] |
{"monitor_ids": [1,4,7]}
risposta 200 · Done.
{"added": 1,"skipped": [{"id": 1,"name": "string","type": "string","target": "string","reason": "no_ipv6"}]}
/api/agents/{id}/tokenRotate the agent tokentoken di scrittura · adminThe agent is disconnected until it uses the new token.
| id* | path · integer | Id of the agent. |
risposta 200 · New token.
{"token": "string","hub_url": "string"}
/api/agents/{id}/updateUpdate an agent nowtoken di scrittura · adminAsks a connected agent to install the hub's version.
| id* | path · integer | Id of the agent. |
risposta 200 · Done.
{"ok": true}
/api/locationsList check locationstoken di letturaThe hub (when it runs checks) and every agent.
risposta 200 · Locations.
[{"id": "local","name": "hub","online": true,"kind": "local","ipv6": 2},{"id": "agent:2","name": "fra-1 · Frankfurt","online": true,"kind": "agent","ipv6": 1}]
/api/settings/agent-updatesAgent auto-update settingtoken di scrittura · admin| auto_update* | boolean |
{"auto_update": true}
risposta 200 · Saved.
{"auto_update": true}
──notifications
Alert channels: webhook, Discord, Slack, Telegram, ntfy, email.
/api/mobile/configPush config for the mobile apptoken di letturaWhere the app enrolls its phone: it sends hub_id and its FCM token to {relay_url}/v1/devices and gives the handle it gets to POST /api/mobile/devices. live_activity is true when the hub also pushes Live Activity updates (PUT /api/mobile/live-activity/...). enabled is false (and the other fields empty) when push is off.
risposta 200 · Config.
{"enabled": true,"relay_url": "https://push.pingtxt.com","hub_id": "PrBlwRm8tbClvg8f5PEy4w","live_activity": true}
/api/mobile/devicesList my phonestoken di letturarisposta 200 · Phones of the current user.
[{"id": 1,"name": "string","created_at": 1761868800000,"last_seen_at": 1761868800000}]
/api/mobile/devicesRegister my phonetoken di letturaStores the device handle (prd_...) from the push relay for the app's sign-in. Only OAuth access tokens (pma_...) can call it; each sign-in keeps one handle, a new one replaces it. Revoking the sign-in removes the phone.
| device* | string | |
| name | string |
{"device": "prd_AeYWsf5y...","name": "Federico's iPhone"}
risposta 200 · Saved.
{"id": 3}
/api/mobile/devices/{id}Remove my phonetoken di letturaThe phone stops getting notifications from this hub.
| id* | path · integer | Id of the phone. |
risposta 200 · Done.
{"ok": true}
/api/mobile/live-activity/start-tokenSet my Live Activity push-to-start tokentoken di letturaStores the ActivityKit push-to-start token of the app (iOS 17.2+, Activity<OutageActivityAttributes>.pushToStartTokenUpdates) and the hub URL the app signed in with. When a monitor goes down and this phone has no activity running, the hub starts one through the push relay with attributes {"hub": hub_url}. Send it again whenever iOS gives a new one.
| token* | string | |
| hub_url* | string |
{"token": "80f4c3...","hub_url": "https://status.example.com"}
risposta 200 · Saved.
{"ok": true}
/api/mobile/live-activity/start-tokenRemove my Live Activity push-to-start tokentoken di letturaThe hub no longer starts the activity on this phone (for example, Live Activities turned off in the app).
risposta 200 · Saved.
{"ok": true}
/api/mobile/live-activity/tokenSet the token of my running Live Activitytoken di letturaStores the push token of the activity running on this phone (activity.pushTokenUpdates). The hub sends it update and end pushes when incidents open or close, and refreshes it every 15 minutes while incidents stay open. Send it again when it rotates.
| token* | string |
{"token": "6a0d1e..."}
risposta 200 · Saved.
{"ok": true}
/api/mobile/live-activity/tokenRemove the token of my Live Activitytoken di letturaCall it when the activity ends on the phone (ended by the app or dismissed by the user).
risposta 200 · Saved.
{"ok": true}
/api/notifiersList notification channelstoken di letturarisposta 200 · Channels.
[{"id": 1,"name": "string","type": "webhook","config": {"key": "string"},"enabled": true,"delay_min": 1,"repeat_min": 1,"all_monitors": true,"group_ids": [1],"tag_ids": [1],"events": ["down"],"monitors": [{"monitor_id": 1,"notifier_id": 1,"events": [null]}],"created_at": 1761868800000}]
/api/notifiersCreate a notification channeltoken di scrittura · admin| id | integer | |
| name* | string | |
| type* | string | uno tra: webhook discord slack telegram ntfy email app |
| config* | object | webhook, discord, slack, ntfy: {url}. telegram: {bot_token, chat_id}. email: {host, port, security (starttls|tls|none), username, password, from, to}. app: {users}, emails of the users whose phones get the alerts, comma separated (empty = every user). The email password is write-only: responses carry password_set="true" instead, and an empty password on update keeps the stored one. Read-only users see secrets as ********. |
| enabled | boolean | |
| delay_min | integer | Minutes a monitor must stay down before this channel gets its first alert (0 = right away). Use it for an escalation contact: shorter outages never reach it, and it gets the recovery message only if it was alerted. |
| repeat_min | integer | Repeat the alert every this many minutes while the monitor stays down (0 = once, otherwise at least 5). Reminders carry reminder=1, 2, ... and down_since in the webhook payload. Nothing is sent during maintenance windows. |
| all_monitors | boolean | The channel covers every monitor, also the ones added later. Default true when left out on create. |
| group_ids | integer[] | Without all_monitors: the channel covers the monitors of these groups. |
| tag_ids | integer[] | Without all_monitors: the channel covers the monitors with one of these tags. |
| events | "down" | "up" | "degraded" | "cert_expiry" | "domain_expiry"[] | What the channel gets for the monitors it covers. Default on create: down, up, cert_expiry, domain_expiry. down = the monitor went down (and the reminders of repeat_min), up = it is back up (only to channels that got the down alert), degraded = some locations fail and when that ends, cert_expiry / domain_expiry = the TLS certificate or the domain registration expires soon, expired or was renewed. |
| monitors | ChannelLink[] | Monitors linked one by one (monitor_id + events), the same links as the monitors' notifiers. Left out on update: kept; [] removes them. |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
{"name": "ops","type": "slack","config": {"url": "https://hooks.slack.com/services/T000/B000/XXXX"},"enabled": true}
risposta 201 · Created.
{"id": 1,"name": "string","type": "webhook","config": {"key": "string"},"enabled": true,"delay_min": 1,"repeat_min": 1,"all_monitors": true,"group_ids": [1],"tag_ids": [1],"events": ["down"],"monitors": [{"monitor_id": 1,"notifier_id": 1,"events": ["down"]}],"created_at": 1761868800000}
/api/notifiers/{id}Update a notification channeltoken di scrittura · admin| id* | path · integer | Id of the channel. |
| id | integer | |
| name* | string | |
| type* | string | uno tra: webhook discord slack telegram ntfy email app |
| config* | object | webhook, discord, slack, ntfy: {url}. telegram: {bot_token, chat_id}. email: {host, port, security (starttls|tls|none), username, password, from, to}. app: {users}, emails of the users whose phones get the alerts, comma separated (empty = every user). The email password is write-only: responses carry password_set="true" instead, and an empty password on update keeps the stored one. Read-only users see secrets as ********. |
| enabled | boolean | |
| delay_min | integer | Minutes a monitor must stay down before this channel gets its first alert (0 = right away). Use it for an escalation contact: shorter outages never reach it, and it gets the recovery message only if it was alerted. |
| repeat_min | integer | Repeat the alert every this many minutes while the monitor stays down (0 = once, otherwise at least 5). Reminders carry reminder=1, 2, ... and down_since in the webhook payload. Nothing is sent during maintenance windows. |
| all_monitors | boolean | The channel covers every monitor, also the ones added later. Default true when left out on create. |
| group_ids | integer[] | Without all_monitors: the channel covers the monitors of these groups. |
| tag_ids | integer[] | Without all_monitors: the channel covers the monitors with one of these tags. |
| events | "down" | "up" | "degraded" | "cert_expiry" | "domain_expiry"[] | What the channel gets for the monitors it covers. Default on create: down, up, cert_expiry, domain_expiry. down = the monitor went down (and the reminders of repeat_min), up = it is back up (only to channels that got the down alert), degraded = some locations fail and when that ends, cert_expiry / domain_expiry = the TLS certificate or the domain registration expires soon, expired or was renewed. |
| monitors | ChannelLink[] | Monitors linked one by one (monitor_id + events), the same links as the monitors' notifiers. Left out on update: kept; [] removes them. |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
{"name": "ops","type": "slack","config": {"url": "https://hooks.slack.com/services/T000/B000/XXXX"},"enabled": true}
risposta 200 · Updated.
{"id": 1,"name": "string","type": "webhook","config": {"key": "string"},"enabled": true,"delay_min": 1,"repeat_min": 1,"all_monitors": true,"group_ids": [1],"tag_ids": [1],"events": ["down"],"monitors": [{"monitor_id": 1,"notifier_id": 1,"events": ["down"]}],"created_at": 1761868800000}
/api/notifiers/{id}Delete a notification channeltoken di scrittura · admin| id* | path · integer | Id of the channel. |
risposta 200 · Done.
{"ok": true}
/api/notifiers/{id}/testTest a channeltoken di scrittura · admin| id* | path · integer | Id of the channel. |
risposta 200 · Done.
{"ok": true}
/api/notifiers/testTest an unsaved channeltoken di scrittura · adminSends a test alert with this configuration. With an id, empty secrets are taken from that saved channel.
| id | integer | |
| name* | string | |
| type* | string | uno tra: webhook discord slack telegram ntfy email app |
| config* | object | webhook, discord, slack, ntfy: {url}. telegram: {bot_token, chat_id}. email: {host, port, security (starttls|tls|none), username, password, from, to}. app: {users}, emails of the users whose phones get the alerts, comma separated (empty = every user). The email password is write-only: responses carry password_set="true" instead, and an empty password on update keeps the stored one. Read-only users see secrets as ********. |
| enabled | boolean | |
| delay_min | integer | Minutes a monitor must stay down before this channel gets its first alert (0 = right away). Use it for an escalation contact: shorter outages never reach it, and it gets the recovery message only if it was alerted. |
| repeat_min | integer | Repeat the alert every this many minutes while the monitor stays down (0 = once, otherwise at least 5). Reminders carry reminder=1, 2, ... and down_since in the webhook payload. Nothing is sent during maintenance windows. |
| all_monitors | boolean | The channel covers every monitor, also the ones added later. Default true when left out on create. |
| group_ids | integer[] | Without all_monitors: the channel covers the monitors of these groups. |
| tag_ids | integer[] | Without all_monitors: the channel covers the monitors with one of these tags. |
| events | "down" | "up" | "degraded" | "cert_expiry" | "domain_expiry"[] | What the channel gets for the monitors it covers. Default on create: down, up, cert_expiry, domain_expiry. down = the monitor went down (and the reminders of repeat_min), up = it is back up (only to channels that got the down alert), degraded = some locations fail and when that ends, cert_expiry / domain_expiry = the TLS certificate or the domain registration expires soon, expired or was renewed. |
| monitors | ChannelLink[] | Monitors linked one by one (monitor_id + events), the same links as the monitors' notifiers. Left out on update: kept; [] removes them. |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
{"name": "ops","type": "slack","config": {"url": "https://hooks.slack.com/services/T000/B000/XXXX"},"enabled": true}
risposta 200 · Done.
{"ok": true}
──reports
Periodic email reports: uptime and incidents of the last week or month, per monitor group.
/api/reportsGet report settingstoken di letturaReport settings, when the next reports go out and the last ones sent.
risposta 200 · Settings.
{"weekly": true,"monthly": true,"hour": 1,"timezone": "Europe/Rome","notifier_ids": [1],"group_ids": [1],"next": {"key": 1},"last": {"key": {"from": 1,"sent_at": 1}}}
/api/reportsSave report settingstoken di scrittura · adminTurns the weekly and monthly reports on or off and picks the email channels, the groups, the hour and the time zone. A report that is turned on starts with the next period.
| weekly | boolean | Send a report every Monday for the week before. |
| monthly | boolean | Send a report on the 1st for the month before. |
| hour | integer | Hour of the day the reports are sent, in timezone. |
| timezone | string | IANA time zone of the periods and of the times in the report. |
| notifier_ids | integer[] | Email channels that receive the reports. Disabled channels are skipped. |
| group_ids | integer[] | Only these monitor groups. Empty: every monitor. |
{"weekly": true,"monthly": true,"hour": 8,"timezone": "Europe/Rome","notifier_ids": [3],"group_ids": []}
risposta 200 · Saved.
{"weekly": true,"monthly": true,"hour": 1,"timezone": "Europe/Rome","notifier_ids": [1],"group_ids": [1],"next": {"key": 1},"last": {"key": {"from": 1,"sent_at": 1}}}
/api/reports/previewPreview a reporttoken di letturaThe report of the last complete week (Monday to Sunday) or month, with the saved settings, as it would be sent now. text is the plain text body of the email.
| kind | query · string | Which report.uno tra: weekly monthly |
risposta 200 · Report.
{"kind": "weekly","from": 1,"to": 1,"timezone": "string","uptime": 12.5,"prev_uptime": 12.5,"incidents": 1,"downtime_ms": 1,"monitors": 1,"groups": [{"id": 1,"name": "string","uptime": 12.5,"prev_uptime": 12.5,"incidents": 1,"downtime_ms": 1,"monitors": [null],"incident_list": [null]}],"subject": "string","text": "string","url": "string"}
/api/reports/sendSend a report nowtoken di scrittura · adminEmails the report of the last complete period to the saved channels now. The schedule does not change.
| kind | string | uno tra: weekly monthly |
{"kind": "monthly"}
risposta 200 · Sent to at least one channel.
{"ok": true,"sent": 1}
──status pages
Public pages and their data.
/api/badge/{slug}/{kind}Status page badgepubblicoSVG badge with the overall state of a status page (operational, degraded, partial outage, major outage), to embed in a README:
``markdown

| slug* | path · string | Slug of a published status page. |
| kind* | path · string | status, optionally with .svg.uno tra: status status.svg |
| label | query · string | Replaces the left text (up to 60 characters). |
| style | query · string | flat (default) or flat-square.uno tra: flat flat-square |
/api/badge/{slug}/{monitor}/{kind}Monitor badgepubblicoSVG badge of one monitor shown on a published status page: its state (status), its uptime (uptime) or its average response time (response) over period. The label defaults to the monitor name, uptime 30d or response 24h.
``markdown

| slug* | path · string | Slug of a published status page. |
| monitor* | path · integer | Monitor id. |
| kind* | path · string | status, uptime or response, optionally with .svg.uno tra: status uptime response status.svg uptime.svg response.svg |
| period | query · string | Window for uptime (default 30d) and response (default 24h).uno tra: 24h 7d 30d 90d |
| label | query · string | Replaces the left text (up to 60 characters). |
| style | query · string | flat (default) or flat-square.uno tra: flat flat-square |
/api/public/status/{slug}Public status page datapubblicoWhat /status/<slug> shows. Drafts answer 404 unless a user is logged in. Cached for 15 seconds.
Published pages without a password answer with Access-Control-Allow-Origin: *, so any website can read them from the browser (for example to show a status dot).
| slug* | path · string |
risposta 200 · Page.
{"slug": "string","title": "string","description": "string","header_url": "string","logo": {"svg": true,"width": 1,"height": 1,"v": 1},"hide_title": true,"published": true,"status": "operational","days": 1,"monitors": [{"id": 1,"name": "string","group": "string","status": "string","uptime": 12.5,"uptime_24h": 12.5,"bars": [{"t": null,"total": null,"ok": null,"degraded": null,"maint": null,"avg_ms": null}]}],"show_incidents": true,"incidents": [{"id": 1,"monitor_name": "string","started_at": 1761868800000,"resolved_at": 1761868800000}],"updated_at": 1761868800000,"reports": [{"id": 1,"page_id": 1,"title": "string","impact": "none","status": "investigating","created_at": 1761868800000,"updated_at": 1761868800000,"resolved_at": 1,"updates": [null]}],"subscribe": true}
/api/public/status/{slug}/logo/{file}Status page logopubblicoOne format of the page logo: logo.svg, or logo.avif, logo.webp and logo.png. Same access rules as the page (drafts for logged-in users, password protected pages need the unlock cookie). With the current ?v= the file is cached for a year. On a custom domain the path is /api/public/status/logo/{file}.
| slug* | path · string | |
| file* | path · string | uno tra: logo.svg logo.avif logo.webp logo.png |
| v | query · integer | Logo version from the page JSON. |
/api/public/status/{slug}/subscribeSubscribe to a status page by emailpubblicoSends a confirmation link to the address; nothing else is sent until it is confirmed. The answer is the same for new, waiting and already confirmed addresses. On a custom domain the path is /api/public/status/subscribe. Limited to 10 requests per client address and 3 per email address every 15 minutes.
| slug* | path · string |
| email* | string |
{"email": "jane@example.com"}
risposta 202 · Confirmation email queued (or already subscribed).
{"ok": true}
/api/public/status/{slug}/unlockUnlock a password protected status pagepubblicoChecks the page password and sets a cookie that opens the page, its badges and its subscription form for 30 days. Changing or removing the password closes the page again for every visitor. On a custom domain the path is /api/public/status/unlock. Limited to 10 wrong passwords per client address and 100 per page every 15 minutes.
| slug* | path · string |
| password* | string |
{"password": "string"}
risposta 200 · Unlocked (or the page has no password); the cookie is set.
{"ok": true}
/api/public/subscriptions/{token}/confirmConfirm an email subscriptionpubblicoCalled by the status page when a visitor opens the link of the confirmation email. Links expire after 7 days.
| token* | path · string | Token of the links sent by email. |
risposta 200 · Confirmed.
{"ok": true,"email": "string","page": "string"}
/api/public/subscriptions/{token}/unsubscribeUnsubscribe from a status pagepubblicoRemoves the address. Also the target of the List-Unsubscribe header of update emails (RFC 8058 one-click: the body may be a form, not JSON).
| token* | path · string | Token of the links sent by email. |
risposta 200 · Unsubscribed.
{"ok": true,"email": "string","page": "string"}
/api/status-pagesList status pagestoken di letturarisposta 200 · Pages.
{"pages": [{"id": 1,"slug": "string","title": "string","description": "string","domain": "string","header_url": "string","hide_title": true,"logo": {"svg": true,"width": 1,"height": 1,"v": 1},"published": true,"show_incidents": true,"monitor_ids": [1],"group_ids": [1],"created_at": 1761868800000,"subscribe_notifier_id": 1,"protected": true,"password": "string"}],"hub_url": "string"}
/api/status-pagesCreate a status pagetoken di scrittura · admin| id | integer | |
| slug* | string | URL path: /status/<slug>. 1-50 lowercase letters, digits or dashes. |
| title* | string | |
| description | string | |
| domain | string | Optional custom domain serving the page, e.g. status.example.com. |
| header_url | string | Optional http(s) address the page name links to, e.g. the company website. Empty = the name is not a link. |
| hide_title | boolean | Show only the logo in the page header, without the page name (it stays the browser tab title). Ignored when the page has no logo. |
| logo | PageLogo | The page logo, only in the answer of GET /api/status-pages/{id}. Change it with the logo routes. |
| published | boolean | Drafts are visible only to logged-in users. |
| show_incidents | boolean | |
| monitor_ids | integer[] | |
| group_ids | integer[] | Every monitor of these groups, current and future. |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
| subscribe_notifier_id | integer | Email notification channel that sends incident updates to the page's subscribers (its SMTP server and sender are used, its recipients are not). 0 = visitors cannot subscribe. |
| protected | boolean | Visitors need a password to open the page, its badges and its subscription form. Logged-in users open it without one. On save, false removes the password and true keeps the current one. |
| password | string | New password for visitors (6-72 characters), stored hashed and never returned. Needed when protected is set on a page without one. |
{"slug": "acme","title": "Acme status","description": "","domain": "","published": true,"show_incidents": true,"monitor_ids": [7],"group_ids": []}
risposta 201 · Created.
{"id": 1,"slug": "string","title": "string","description": "string","domain": "string","header_url": "string","hide_title": true,"logo": {"svg": true,"width": 1,"height": 1,"v": 1},"published": true,"show_incidents": true,"monitor_ids": [1],"group_ids": [1],"created_at": 1761868800000,"subscribe_notifier_id": 1,"protected": true,"password": "string"}
/api/status-pages/{id}Get a status pagetoken di lettura| id* | path · integer | Id of the status page. |
risposta 200 · Page.
{"id": 1,"slug": "string","title": "string","description": "string","domain": "string","header_url": "string","hide_title": true,"logo": {"svg": true,"width": 1,"height": 1,"v": 1},"published": true,"show_incidents": true,"monitor_ids": [1],"group_ids": [1],"created_at": 1761868800000,"subscribe_notifier_id": 1,"protected": true,"password": "string"}
/api/status-pages/{id}Update a status pagetoken di scrittura · admin| id* | path · integer | Id of the status page. |
| id | integer | |
| slug* | string | URL path: /status/<slug>. 1-50 lowercase letters, digits or dashes. |
| title* | string | |
| description | string | |
| domain | string | Optional custom domain serving the page, e.g. status.example.com. |
| header_url | string | Optional http(s) address the page name links to, e.g. the company website. Empty = the name is not a link. |
| hide_title | boolean | Show only the logo in the page header, without the page name (it stays the browser tab title). Ignored when the page has no logo. |
| logo | PageLogo | The page logo, only in the answer of GET /api/status-pages/{id}. Change it with the logo routes. |
| published | boolean | Drafts are visible only to logged-in users. |
| show_incidents | boolean | |
| monitor_ids | integer[] | |
| group_ids | integer[] | Every monitor of these groups, current and future. |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
| subscribe_notifier_id | integer | Email notification channel that sends incident updates to the page's subscribers (its SMTP server and sender are used, its recipients are not). 0 = visitors cannot subscribe. |
| protected | boolean | Visitors need a password to open the page, its badges and its subscription form. Logged-in users open it without one. On save, false removes the password and true keeps the current one. |
| password | string | New password for visitors (6-72 characters), stored hashed and never returned. Needed when protected is set on a page without one. |
{"slug": "acme","title": "Acme status","description": "","domain": "","published": true,"show_incidents": true,"monitor_ids": [7],"group_ids": []}
risposta 200 · Updated.
{"id": 1,"slug": "string","title": "string","description": "string","domain": "string","header_url": "string","hide_title": true,"logo": {"svg": true,"width": 1,"height": 1,"v": 1},"published": true,"show_incidents": true,"monitor_ids": [1],"group_ids": [1],"created_at": 1761868800000,"subscribe_notifier_id": 1,"protected": true,"password": "string"}
/api/status-pages/{id}Delete a status pagetoken di scrittura · admin| id* | path · integer | Id of the status page. |
risposta 200 · Done.
{"ok": true}
/api/status-pages/{id}/incidentsList the incidents of a status pagetoken di letturaIncidents written on the page, open ones first, each with its updates (newest first).
| id* | path · integer | Id of the status page. |
risposta 200 · Incidents.
{"incidents": [{"id": 1,"page_id": 1,"title": "string","impact": "none","status": "investigating","created_at": 1761868800000,"updated_at": 1761868800000,"resolved_at": 1,"updates": [{"id": null,"incident_id": null,"status": null,"message": null,"created_at": null}]}]}
/api/status-pages/{id}/incidentsCreate an incident on a status pagetoken di scrittura · adminOpens an incident with its first update. With notify (default true) the update is emailed to the confirmed subscribers of the page, in the background.
| id* | path · integer | Id of the status page. |
| title | string | One line, at most 200 characters. Required to create and to edit. |
| impact | string | Default minor.uno tra: none minor major critical |
| status | string | Status of the update. Default investigating when creating.uno tra: investigating identified monitoring resolved |
| message | string | Text of the update, at most 5000 characters. Required to create and for updates. |
| notify | boolean | Email the update to the confirmed subscribers of the page (default true). Ignored when editing the title or impact. |
{"title": "Slow API responses","impact": "minor","status": "investigating","message": "We are looking into slow responses of the API.","notify": true}
risposta 201 · Created.
{"incident": {"id": 1,"page_id": 1,"title": "string","impact": "none","status": "investigating","created_at": 1761868800000,"updated_at": 1761868800000,"resolved_at": 1,"updates": [{"id": 1,"incident_id": 1,"status": "investigating","message": "string","created_at": 1761868800000}]},"notified": 1}
/api/status-pages/{id}/incidents/{incident}Edit an incidenttoken di scrittura · adminChanges the title and impact. Updates are added with POST …/updates.
| id* | path · integer | Id of the status page. |
| incident* | path · integer | Id of the incident. |
| title | string | One line, at most 200 characters. Required to create and to edit. |
| impact | string | Default minor.uno tra: none minor major critical |
| status | string | Status of the update. Default investigating when creating.uno tra: investigating identified monitoring resolved |
| message | string | Text of the update, at most 5000 characters. Required to create and for updates. |
| notify | boolean | Email the update to the confirmed subscribers of the page (default true). Ignored when editing the title or impact. |
{"title": "Slow API responses in EU","impact": "major"}
risposta 200 · Incident.
{"id": 1,"page_id": 1,"title": "string","impact": "none","status": "investigating","created_at": 1761868800000,"updated_at": 1761868800000,"resolved_at": 1,"updates": [{"id": 1,"incident_id": 1,"status": "investigating","message": "string","created_at": 1761868800000}]}
/api/status-pages/{id}/incidents/{incident}Delete an incidenttoken di scrittura · adminRemoves the incident and all its updates from the page.
| id* | path · integer | Id of the status page. |
| incident* | path · integer | Id of the incident. |
risposta 200 · Done.
{"ok": true}
/api/status-pages/{id}/incidents/{incident}/updatesPost an incident updatetoken di scrittura · adminAdds an update to the timeline; the incident takes its status (resolved closes it, any other status reopens it). With notify (default true) it is emailed to the confirmed subscribers.
| id* | path · integer | Id of the status page. |
| incident* | path · integer | Id of the incident. |
| title | string | One line, at most 200 characters. Required to create and to edit. |
| impact | string | Default minor.uno tra: none minor major critical |
| status | string | Status of the update. Default investigating when creating.uno tra: investigating identified monitoring resolved |
| message | string | Text of the update, at most 5000 characters. Required to create and for updates. |
| notify | boolean | Email the update to the confirmed subscribers of the page (default true). Ignored when editing the title or impact. |
{"status": "resolved","message": "Response times are back to normal.","notify": true}
risposta 201 · Added.
{"incident": {"id": 1,"page_id": 1,"title": "string","impact": "none","status": "investigating","created_at": 1761868800000,"updated_at": 1761868800000,"resolved_at": 1,"updates": [{"id": 1,"incident_id": 1,"status": "investigating","message": "string","created_at": 1761868800000}]},"notified": 1}
/api/status-pages/{id}/incidents/{incident}/updates/{update}Delete an incident updatetoken di scrittura · adminRemoves one update; the incident takes the status of the latest one left. The last update cannot be deleted (409): delete the incident instead.
| id* | path · integer | Id of the status page. |
| incident* | path · integer | Id of the incident. |
| update* | path · integer | Id of the update. |
risposta 200 · Incident.
{"id": 1,"page_id": 1,"title": "string","impact": "none","status": "investigating","created_at": 1761868800000,"updated_at": 1761868800000,"resolved_at": 1,"updates": [{"id": 1,"incident_id": 1,"status": "investigating","message": "string","created_at": 1761868800000}]}
/api/status-pages/{id}/logoUpload a status page logotoken di scrittura · adminRaw image file as the body (Content-Type: application/octet-stream), up to 4 MB: SVG, PNG, JPEG, GIF, WebP or AVIF. An SVG is kept as a vector, without scripts, event handlers or external references. Other images are trimmed of transparent borders, scaled down to fit 960×192 pixels and stored as AVIF, WebP and PNG. Replaces the current logo.
| id* | path · integer | Id of the status page. |
risposta 200 · Stored.
{"logo": {"svg": true,"width": 1,"height": 1,"v": 1}}
/api/status-pages/{id}/logoRemove a status page logotoken di scrittura · admin| id* | path · integer | Id of the status page. |
risposta 200 · Done.
{"ok": true}
/api/status-pages/{id}/logo/urlImport a status page logo from a URLtoken di scrittura · adminThe hub downloads the image (public http or https addresses only: private and local networks are refused) and stores it like an upload.
| id* | path · integer | Id of the status page. |
| url* | string |
{"url": "string"}
risposta 200 · Stored.
{"logo": {"svg": true,"width": 1,"height": 1,"v": 1}}
/api/status-pages/{id}/subscribersList the email subscribers of a status pagesessione adminAddresses subscribed to incident updates, confirmed or waiting for confirmation (unconfirmed ones are dropped after 7 days). enabled is false when the page has no working email channel.
| id* | path · integer | Id of the status page. |
risposta 200 · Subscribers.
{"subscribers": [{"id": 1,"page_id": 1,"email": "string","confirmed_at": 1,"created_at": 1761868800000}],"enabled": true}
/api/status-pages/{id}/subscribers/{subscriber}Remove an email subscribersessione adminThe address gets no more emails from the page.
| id* | path · integer | Id of the status page. |
| subscriber* | path · integer | Id of the subscriber. |
risposta 200 · Done.
{"ok": true}
/api/status-pages/previewPreview a status pagetoken di scrittura · adminWhat visitors would see with these settings, without saving them. With the id of a saved page, the incidents written on it are included.
| id | integer | |
| slug* | string | URL path: /status/<slug>. 1-50 lowercase letters, digits or dashes. |
| title* | string | |
| description | string | |
| domain | string | Optional custom domain serving the page, e.g. status.example.com. |
| header_url | string | Optional http(s) address the page name links to, e.g. the company website. Empty = the name is not a link. |
| hide_title | boolean | Show only the logo in the page header, without the page name (it stays the browser tab title). Ignored when the page has no logo. |
| logo | PageLogo | The page logo, only in the answer of GET /api/status-pages/{id}. Change it with the logo routes. |
| published | boolean | Drafts are visible only to logged-in users. |
| show_incidents | boolean | |
| monitor_ids | integer[] | |
| group_ids | integer[] | Every monitor of these groups, current and future. |
| created_at | integer | Unix time in milliseconds (0 = never / unknown). |
| subscribe_notifier_id | integer | Email notification channel that sends incident updates to the page's subscribers (its SMTP server and sender are used, its recipients are not). 0 = visitors cannot subscribe. |
| protected | boolean | Visitors need a password to open the page, its badges and its subscription form. Logged-in users open it without one. On save, false removes the password and true keeps the current one. |
| password | string | New password for visitors (6-72 characters), stored hashed and never returned. Needed when protected is set on a page without one. |
{"slug": "acme","title": "Acme status","description": "","domain": "","published": true,"show_incidents": true,"monitor_ids": [7],"group_ids": []}
risposta 200 · Page.
{"slug": "string","title": "string","description": "string","header_url": "string","logo": {"svg": true,"width": 1,"height": 1,"v": 1},"hide_title": true,"published": true,"status": "operational","days": 1,"monitors": [{"id": 1,"name": "string","group": "string","status": "string","uptime": 12.5,"uptime_24h": 12.5,"bars": [{"t": null,"total": null,"ok": null,"degraded": null,"maint": null,"avg_ms": null}]}],"show_incidents": true,"incidents": [{"id": 1,"monitor_name": "string","started_at": 1761868800000,"resolved_at": 1761868800000}],"updated_at": 1761868800000,"reports": [{"id": 1,"page_id": 1,"title": "string","impact": "none","status": "investigating","created_at": 1761868800000,"updated_at": 1761868800000,"resolved_at": 1,"updates": [null]}],"subscribe": true}
──system
Health, counters, metrics and this document.
/api/docsInteractive API docspubblicoRedirects to the Swagger UI page of the web app.
/api/healthHealth checkpubblicoLiveness probe, also returns the hub version.
risposta 200 · Hub is running.
{"ok": true,"version": "v0.1.0"}
/api/openapi.jsonOpenAPI documentpubblicoThis document, for the running version.
risposta 200 · OpenAPI 3.1 document.
{}
/api/overviewDashboard counterstoken di letturarisposta 200 · Counters.
{"monitors": 12,"status": {"up": 10,"down": 1,"paused": 1},"agents": 2,"agents_online": 2,"open_incidents": 1,"version": "v0.1.0"}
/api/telemetryAnonymous usage statssessione adminrisposta 200 · Usage stats status.
{"enabled": true,"asked": true,"forced_off": true,"report": null,"sent_at": 1,"error": "string"}
/api/telemetryTurn anonymous usage stats on or offsessione admin| enabled* | boolean |
{"enabled": true}
risposta 200 · Usage stats status.
{"enabled": true,"asked": true,"forced_off": true,"report": null,"sent_at": 1,"error": "string"}
/api/versionVersion and update checktoken di letturarisposta 200 · Version status.
{"current": "string","check_enabled": true,"check_disabled": true,"latest": null,"update_available": true,"checked_at": 1,"error": "string"}
/api/version/checkCheck for a new version nowsessione adminrisposta 200 · Version status.
{"current": "string","check_enabled": true,"check_disabled": true,"latest": null,"update_available": true,"checked_at": 1,"error": "string"}
/metricsPrometheus metricstoken di letturaMetrics in the Prometheus text format. Public when *settings → general → metrics* is set to public.
risposta 200 · Metrics.
pingtxt_monitor_up{monitor="Website",type="http"} 1
──auth
Setup, login, password, single sign-on and two-factor. Browser oriented.
/api/auth/2fa/disableDisable two-factorsessione del browser| code* | string | TOTP or recovery code. |
{"code": "string"}
risposta 200 · Done.
{"ok": true}
/api/auth/2fa/enableEnable two-factorsessione del browser| code* | string |
{"code": "123456"}
risposta 200 · Enabled. Recovery codes are shown once.
{"recovery_codes": ["string"]}
/api/auth/2fa/recovery-codesNew recovery codessessione del browserReplaces the old codes.
| code* | string |
{"code": "string"}
risposta 200 · New codes.
{"recovery_codes": ["string"]}
/api/auth/2fa/setupStart two-factor setupsessione del browserReturns a new TOTP secret; confirm it with /api/auth/2fa/enable.
risposta 200 · Secret and otpauth:// URI for the QR code.
{"secret": "string","uri": "string"}
/api/auth/identities/{provider}Unlink a single sign-on providersessione del browser| provider* | path · string | uno tra: github google oidc |
risposta 200 · Done.
{"ok": true}
/api/auth/loginLog in with passwordpubblicoSets the pingtxt_session cookie. When the user has two-factor authentication, the response is {"two_factor": true, "challenge": "..."} instead: send the challenge with a code to /api/auth/login/2fa.
| email* | string | |
| password* | string |
{"email": "admin@example.com","password": "correct horse"}
risposta 200 · Logged in, or a two-factor challenge.
{"id": 1,"email": "string","role": "admin","created_at": 1761868800000,"has_password": true,"two_factor": true,"identities": [{"provider": "string","email": "string","created_at": 1761868800000}]}
/api/auth/login/2faComplete a two-factor loginpubblico| challenge* | string | |
| code* | string | 6 digit TOTP code or a recovery code. |
{"challenge": "string","code": "string"}
risposta 200 · Logged in, session cookie set.
{"id": 1,"email": "string","role": "admin","created_at": 1761868800000,"has_password": true,"two_factor": true,"identities": [{"provider": "string","email": "string","created_at": 1761868800000}]}
/api/auth/logoutLog outpubblicoEnds the browser session.
risposta 200 · Done.
{"ok": true}
/api/auth/meCurrent usertoken di letturarisposta 200 · The user behind the session or token.
{"user": {"id": 1,"email": "admin@example.com","role": "admin","created_at": 1761868800000,"has_password": true,"two_factor": false,"identities": []},"version": "v0.1.0","recovery_codes_left": 10}
/api/auth/oauth/{provider}/callbackSingle sign-on callbackpubblicoRegister <public url>/api/auth/oauth/<provider>/callback at the provider. Not meant to be called directly.
| provider* | path · string | uno tra: github google oidc |
/api/auth/oauth/{provider}/startStart single sign-onpubblicoBrowser redirect to the provider. ?next=/path returns there after login, ?link=1 links the provider to the logged-in account.
| provider* | path · string | uno tra: github google oidc |
| next | query · string | Local path to open after login. |
| link | query · string | 1 to link to the current account. |
/api/auth/optionsLogin optionspubblicoWhat the login page shows: password form and single sign-on buttons.
risposta 200 · Options.
{"password_login": true,"providers": [{"id": "github","label": "GitHub"}]}
/api/auth/passwordChange passwordsessione del browserOther sessions of the user are logged out. Accounts created with single sign-on can set a first password with an empty current.
| current | string | |
| new* | string | At least 8 characters. |
{"current": "string","new": "string"}
risposta 200 · Done.
{"ok": true}
/api/setupSetup statepubblicoWhether the instance still needs its first admin account.
risposta 200 · State.
{"needs_setup": false,"version": "v0.1.0"}
/api/setupCreate the first adminpubblicoWorks only while no user exists. Starts a session.
| email* | string | |
| password* | string | At least 8 characters. |
{"email": "admin@example.com","password": "correct horse"}
risposta 201 · Admin created, session cookie set.
{"id": 1,"email": "admin@example.com","role": "admin","created_at": 1761868800000,"has_password": true,"two_factor": false,"identities": []}
──api tokens
Personal tokens and connected OAuth apps. Browser session only.
/api/oauth/grantsList connected appssessione del browserApps connected through OAuth (the iOS app, MCP clients): one entry per sign-in.
risposta 200 · Grants.
[{"id": 1,"client_id": "string","client_name": "string","scope": "string","expires_at": 1761868800000,"created_at": 1761868800000,"last_used_at": 1761868800000,"push_device": "string"}]
/api/oauth/grants/{id}Disconnect an appsessione del browser| id* | path · integer | Id of the grant. |
risposta 200 · Done.
{"ok": true}
/api/tokensList your API tokenssessione del browserrisposta 200 · Tokens of the current user.
[{"id": 1,"name": "string","hint": "string","scope": "read","created_at": 1761868800000,"last_used_at": 1761868800000,"expires_at": 1}]
/api/tokensCreate an API tokensessione del browserThe secret is returned once. Read-only users can only create read tokens. At most 50 tokens per user.
| name* | string | Up to 100 characters. |
| scope* | string | uno tra: read write |
| expires_in_days | integer | 0 = never, up to 3650. |
{"name": "grafana","scope": "read","expires_in_days": 365}
risposta 201 · Created.
{"token": {"id": 3,"name": "grafana","hint": "x9Qa","scope": "read","created_at": 1761868800000,"last_used_at": 0,"expires_at": 1793404800000},"secret": "ptk_5uG...x9Qa"}
/api/tokens/{id}Revoke an API tokensessione del browser| id* | path · integer | Id of the token. |
risposta 200 · Done.
{"ok": true}
──users
Accounts and roles. Admin browser session only.
/api/usersList userssessione adminrisposta 200 · Users.
[{"id": 1,"email": "string","role": "admin","created_at": 1761868800000,"has_password": true,"two_factor": true,"identities": [{"provider": "string","email": "string","created_at": 1761868800000}]}]
/api/usersCreate a usersessione adminWithout a password the user logs in with single sign-on (matched by verified email).
| email* | string | |
| password | string | Optional, at least 8 characters. |
| role* | string | uno tra: admin viewer |
{"email": "dev@example.com","role": "viewer"}
risposta 201 · Created.
{"id": 1,"email": "string","role": "admin","created_at": 1761868800000,"has_password": true,"two_factor": true,"identities": [{"provider": "string","email": "string","created_at": 1761868800000}]}
/api/users/{id}Change a user's rolesessione adminYou cannot change your own role.
| id* | path · integer | Id of the user. |
| role* | string | uno tra: admin viewer |
{"role": "admin"}
risposta 200 · Updated.
{"id": 1,"email": "string","role": "admin","created_at": 1761868800000,"has_password": true,"two_factor": true,"identities": [{"provider": "string","email": "string","created_at": 1761868800000}]}
/api/users/{id}Delete a usersessione admin| id* | path · integer | Id of the user. |
risposta 200 · Done.
{"ok": true}
/api/users/{id}/2faReset a user's two-factorsessione admin| id* | path · integer | Id of the user. |
risposta 200 · Done.
{"ok": true}
──settings
Instance settings. Admin browser session only.
/api/settingsGet instance settingssessione adminrisposta 200 · Settings.
{"general": {"public_url": "string","local_name": "string","local_enabled": true,"retention_days": 1,"raw_retention_days": 1,"monitor_defaults": {"interval_sec": 1,"timeout_ms": 1,"retries": 1},"expiry": {"cert_days": [1],"domain_days": [1]},"metrics": {"public": true},"degraded_pct": 1,"ip_family": 1,"update_check": {"disabled": true}},"auth": {"password_login": true,"allow_signup": true,"allowed_domains": ["string"],"default_role": "string","providers": [{"id": "string","label": "string","enabled": true,"client_id": "string","client_secret": "string","client_secret_set": true,"callback_url": "string","name": "string","issuer": "string","scopes": "string"}]},"detected_url": "string"}
/api/settings/authSave login settingssessione admin| password_login | boolean | |
| allow_signup | boolean | Create accounts on first single sign-on login. |
| allowed_domains | string[] | Email domains allowed to sign up, empty = any. |
| default_role | string | uno tra: admin viewer |
| providers | object | Keyed by github, google, oidc. |
{"password_login": true,"allow_signup": true,"allowed_domains": ["string"],"default_role": "admin","providers": {"key": {"enabled": true,"client_id": "string","client_secret": "string","name": "string","issuer": "string","scopes": "string"}}}
risposta 200 · Settings.
{"general": {"public_url": "string","local_name": "string","local_enabled": true,"retention_days": 1,"raw_retention_days": 1,"monitor_defaults": {"interval_sec": 1,"timeout_ms": 1,"retries": 1},"expiry": {"cert_days": [1],"domain_days": [1]},"metrics": {"public": true},"degraded_pct": 1,"ip_family": 1,"update_check": {"disabled": true}},"auth": {"password_login": true,"allow_signup": true,"allowed_domains": ["string"],"default_role": "string","providers": [{"id": "string","label": "string","enabled": true,"client_id": "string","client_secret": "string","client_secret_set": true,"callback_url": "string","name": "string","issuer": "string","scopes": "string"}]},"detected_url": "string"}
/api/settings/generalSave general settingssessione admin| public_url | string | Public URL of the hub, used in links, agent setup and OAuth callbacks. |
| local_name | string | Name of the hub's own location. |
| local_enabled | boolean | The hub runs checks itself. |
| retention_days | integer | Hourly history, up to 3650. |
| raw_retention_days | integer | Every single check. |
| monitor_defaults | object | |
| expiry | object | |
| metrics | object | |
| degraded_pct | integer | Default degraded threshold, 1 to 50. |
| ip_family | integer | Default IP family of monitors with ip_family 0: 1 IPv4 or IPv6 (default), 4 IPv4 only, 6 IPv6 only, 46 IPv4 and IPv6. Locations without IPv6 check those monitors over IPv4.uno tra: 1 4 6 46 |
| update_check | object |
{"public_url": "string","local_name": "string","local_enabled": true,"retention_days": 1,"raw_retention_days": 1,"monitor_defaults": {"interval_sec": 1,"timeout_ms": 1,"retries": 1},"expiry": {"cert_days": [1],"domain_days": [1]},"metrics": {"public": true},"degraded_pct": 1,"ip_family": 1,"update_check": {"disabled": true}}
risposta 200 · Settings.
{"general": {"public_url": "string","local_name": "string","local_enabled": true,"retention_days": 1,"raw_retention_days": 1,"monitor_defaults": {"interval_sec": 1,"timeout_ms": 1,"retries": 1},"expiry": {"cert_days": [1],"domain_days": [1]},"metrics": {"public": true},"degraded_pct": 1,"ip_family": 1,"update_check": {"disabled": true}},"auth": {"password_login": true,"allow_signup": true,"allowed_domains": ["string"],"default_role": "string","providers": [{"id": "string","label": "string","enabled": true,"client_id": "string","client_secret": "string","client_secret_set": true,"callback_url": "string","name": "string","issuer": "string","scopes": "string"}]},"detected_url": "string"}
/api/settings/pushGet mobile app push settingssessione adminrisposta 200 · Push settings.
{"enabled": true,"relay_url": "string","hub_id": "string","registered": true,"devices": 1}
/api/settings/pushSave mobile app push settingssessione adminTurning push on registers the hub with the relay the first time, and again when the relay no longer accepts the hub token (its secret changed): the phones then enroll again. A different relay URL drops the registration and every phone (they enroll again).
| enabled | boolean | |
| relay_url | string |
{"enabled": true,"relay_url": "https://push.pingtxt.com"}
risposta 200 · Saved.
{"enabled": true,"relay_url": "string","hub_id": "string","registered": true,"devices": 1}
/api/settings/push/testTest the push relaysessione adminChecks the hub token at the relay and that the relay can reach Firebase.
risposta 200 · The relay accepts this hub.
{"ok": true,"hub_id": "string"}
──backups
Database backups and configuration export/import.
/api/backupsList backupssessione adminrisposta 200 · Backups and settings.
{"settings": {"enabled": true,"interval_hours": 1,"keep": 1},"backups": [{"name": "string","kind": "auto","size": 1,"created_at": 1761868800000}],"dir": "string","pending_restore": {"name": "string","staged_at": 1761868800000},"driver": "string","file_backups": true}
/api/backupsBack up nowsessione adminrisposta 201 · Backup file.
{"name": "string","kind": "auto","size": 1,"created_at": 1761868800000}
/api/backups/{name}Download a backupsessione admin| name* | path · string | Backup file name. |
/api/backups/{name}Delete a backupsessione admin| name* | path · string | Backup file name. |
risposta 200 · Done.
{"ok": true}
/api/backups/{name}/restoreStage a restoresessione adminThe backup replaces the database at the next start of the hub.
| name* | path · string | Backup file name. |
risposta 200 · Staged.
{"pending_restore": {"name": "string","staged_at": 1761868800000},"info": {"schema_version": 1,"monitors": 1,"users": 1}}
/api/backups/restoreCancel a staged restoresessione adminrisposta 200 · Done.
{"ok": true}
/api/backups/uploadUpload a backupsessione adminRaw SQLite file as the body (Content-Type: application/octet-stream), up to 4 GB. SQLite only.
risposta 201 · Stored.
{"backup": {"name": "string","kind": "auto","size": 1,"created_at": 1761868800000},"info": {"schema_version": 1,"monitors": 1,"users": 1}}
/api/config/exportExport the configurationtoken di letturaAdmins only. Without secrets by default; ?secrets=1 needs a browser session (API tokens get 403).
| secrets | query · string | 1 to include passwords and tokens. |
risposta 200 · JSON download.
{"format": "string","version": 1,"hub_version": "string","exported_at": "string","secrets": true,"settings": {},"agents": [{}],"groups": [{}],"tags": [{}],"notifiers": [{}],"monitors": [{}],"maintenances": [{}],"status_pages": [{}]}
/api/config/importImport a configurationsessione adminA backup is taken first. Body up to 64 MB.
| mode | query · string | replace deletes what is not in the document.uno tra: merge replace |
| settings | query · string | 0 to leave instance settings alone. |
| format | string | Always "pingtxt-config". |
| version | integer | |
| hub_version | string | |
| exported_at | string | |
| secrets | boolean | Passwords and tokens are included. |
| settings | object | |
| agents | object[] | |
| groups | object[] | |
| tags | object[] | |
| notifiers | object[] | |
| monitors | object[] | |
| maintenances | object[] | |
| status_pages | object[] |
{"format": "string","version": 1,"hub_version": "string","exported_at": "string","secrets": true,"settings": {},"agents": [{}],"groups": [{}],"tags": [{}],"notifiers": [{}],"monitors": [{}],"maintenances": [{}],"status_pages": [{}]}
risposta 200 · Result.
{"mode": "merge","backup": "string","created": {"key": 1},"updated": {"key": 1},"deleted": {"key": 1},"skipped": {"key": 1},"settings": true,"warnings": ["string"]}
/api/import/uptime-kumaImport from Uptime Kumasessione adminCreates monitors from an Uptime Kuma 1.x backup file (Settings → Backup → Export in Uptime Kuma). Kuma groups become groups, tags become tags, paused monitors stay paused. Monitors whose name already exists are left alone; unsupported types are skipped with a reason. Notification channels are not imported. With dry_run=1 nothing is created and the answer is the preview.
| dry_run | query · boolean | Preview only. |
| version | string | |
| monitorList* | object[] |
{"version": "string","monitorList": [{}]}
risposta 200 · What was (or would be) done with each monitor.
{"version": "string","monitors": [{"name": "string","kuma_type": "string","type": "string","target": "string","group": "string","paused": true,"action": "create","reason": "string","note": "string"}],"groups": ["string"],"tags": ["string"],"warnings": ["string"],"created": 1}
/api/settings/backupsSave backup schedulesessione admin| enabled | boolean | |
| interval_hours | integer | |
| keep | integer | Scheduled backups to keep. |
{"enabled": true,"interval_hours": 24,"keep": 7}
risposta 200 · Saved.
{"enabled": true,"interval_hours": 1,"keep": 1}
──oauth
OAuth 2.1 authorization server for native apps and MCP clients. Form bodies, OAuth error format.
/.well-known/oauth-authorization-serverAuthorization server metadatapubblicoRFC 8414 metadata: endpoints, scopes, PKCE methods.
risposta 200 · Metadata.
{}
/oauth/authorizeStart a sign-inpubblicoOpen in a browser (ASWebAuthenticationSession on iOS). The user logs in on the hub if needed and allows the app on a consent page; the browser then goes to redirect_uri with code, state and iss, or with error.
| response_type* | query · string | code. |
| client_id* | query · string | pingtxt-ios for the iOS app. |
| redirect_uri* | query · string | pingtxt://oauth for the iOS app. |
| code_challenge* | query · string | BASE64URL(SHA256(code_verifier)), no padding. |
| code_challenge_method* | query · string | S256. |
| state* | query · string | Random value, checked on return. |
| scope | query · string | read, write or read write: write is preselected on the consent page for admins. |
| resource | query · string | Optional (RFC 8707): <hub>/api or <hub>/mcp. |
/oauth/revokeSign outpubblicoEnds the sign-in of the token (access or refresh token, RFC 7009). Always answers 200.
| token* | string | Refresh or access token. |
| client_id* | string | pingtxt-ios for the iOS app. |
/oauth/tokenGet or refresh tokenspubblicogrant_type=authorization_code exchanges the code (single use, 5 minutes) for tokens. grant_type=refresh_token returns a new access token and a new refresh token: the old one stops working, and using it again revokes the whole sign-in, so refresh one request at a time and store the new refresh token before using it.
| grant_type* | string | authorization_code or refresh_token. |
| client_id* | string | pingtxt-ios for the iOS app. |
| code | string | authorization_code: the code from the redirect. |
| redirect_uri | string | authorization_code: same as in the authorization request. |
| code_verifier | string | authorization_code: the PKCE verifier. |
| refresh_token | string | refresh_token: the latest refresh token. |
risposta 200 · Tokens.
{"access_token": "string","token_type": "Bearer","expires_in": 1,"refresh_token": "string","scope": "read"}