# Uptime monitoring > https://docs.errora.org/en/products/uptime Uptime monitors poll a URL from Errora on a fixed interval, assert something about the response, and open an issue when the assertion fails often enough. Unlike every other product, this one needs **no SDK** — it is configured entirely in Errora. ## Create a monitor **Uptime → New monitor**, or over the API: ```bash curl -X POST "$ERRORA/api/v1/projects/$PROJECT_ID/uptime-monitors" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Checkout API", "url": "https://api.example.com/healthz", "method": "GET", "interval_seconds": 300, "timeout_ms": 10000, "environment": "production", "headers": [["Authorization", "Bearer …"]], "body": "", "expected_status_codes": [200, 204], "body_match": "\"status\":\"ok\"", "downtime_threshold": 3, "recovery_threshold": 1, "trace_sampling": false }' ``` ### Fields | Field | Meaning | | --- | --- | | `url` | What to poll. Redirects are followed one SSRF-validated hop at a time. | | `method` | `GET`, `POST`, `HEAD`, … | | `headers` | List of `[name, value]` pairs — auth tokens, custom host headers | | `body` | Request body for `POST`/`PUT` checks | | `interval_seconds` | `60`, `300`, `600`, `1200`, `1800` or `3600` (1 minute → 1 hour) | | `timeout_ms` | Default `10000`, max `60000` | | `expected_status_codes` | Empty means "any 2xx". Otherwise an explicit allow-list. | | `body_match` | Substring that must appear in the response body | | `downtime_threshold` | Consecutive failures before the monitor is *down* (default `3`) | | `recovery_threshold` | Consecutive successes before it is *up* again (default `1`) | | `environment` | Tags the resulting issue and alerts | | `trace_sampling` | Attach a trace to each check (useful when the target is your own instrumented service) | | `is_muted` | Keep checking, stop alerting | ## What a failure means A check fails on a connection error, a timeout, an unexpected status code, or a missing `body_match`. The failure reason is recorded per check, so the history distinguishes "connection refused" from "returned 500" from "body assertion failed". After `downtime_threshold` consecutive failures the monitor goes **down**: Errora opens a downtime **issue** for the project and fires the `uptime.down` alert event. After `recovery_threshold` successes it goes **up**, closes the issue and fires `uptime.up`. ## In the UI **Uptime** lists monitors with current status, uptime percentage and recent checks. Open one for the check history (status code, response time, failure reason), statistics, and a **Check now** button that runs a probe immediately instead of waiting for the schedule. ## Alerts Route `uptime.down` / `uptime.up` to Telegram, Bale, Mattermost, email, SMS or a webhook in **Settings → Webhooks** — see [Alerts](/en/products/alerts). ## What Errora can reach Checks run from Errora's own servers, not from inside your network, so the URL must be reachable from the public internet. Private/internal addresses are refused, including when a redirect points at one — put a public health endpoint in front of an internal service if you need to watch it. ## API ```text GET /api/v1/projects/{project_id}/uptime-monitors POST /api/v1/projects/{project_id}/uptime-monitors GET /api/v1/projects/{project_id}/uptime-monitors/overview GET /api/v1/projects/{project_id}/uptime-monitors/{id} PATCH /api/v1/projects/{project_id}/uptime-monitors/{id} DELETE /api/v1/projects/{project_id}/uptime-monitors/{id} GET /api/v1/projects/{project_id}/uptime-monitors/{id}/checks GET /api/v1/projects/{project_id}/uptime-monitors/{id}/stats POST /api/v1/projects/{project_id}/uptime-monitors/{id}/check-now ``` ## Public status page A status page turns a set of uptime monitors into a page your customers can read: `/status/
`. It needs no account, so it keeps working when the rest of the product does not. Create one under **Uptime → Status pages**. A page has four parts: - **Settings** — the title, headline and intro, the address it lives at, an optional logo and support link, and a **notice banner** pinned to the top until you clear it. A page is a draft until you publish it; an unpublished page answers `404`. - **Components** — one per uptime monitor, each shown under a name customers recognise (`Checkout API`, not `prod-eu-checkout-lb`) and optionally grouped under a heading. The component reads its state from the monitor's probes; there is nothing to keep in sync by hand. - **Incidents** — the write-up around an outage, with a timeline. Posting an update *is* how an incident's status moves: there is no way to change the status without saying why. An unresolved incident shows the page as degraded even when every probe is green. - **Maintenance** — a planned window with the text customers should read. While a window is in progress its components report "under maintenance" rather than down, so scheduled work never looks like an outage. Each component shows one bar per day over the page's history window (up to 90 days), computed from the same hourly rollups as the monitor detail page. ```text GET /api/v1/status/{slug} (public, no auth) GET /api/v1/organizations/{org_id}/status-pages POST /api/v1/organizations/{org_id}/status-pages GET /api/v1/organizations/{org_id}/status-pages/{id} PATCH /api/v1/organizations/{org_id}/status-pages/{id} DELETE /api/v1/organizations/{org_id}/status-pages/{id} GET /api/v1/organizations/{org_id}/status-pages/{id}/components POST /api/v1/organizations/{org_id}/status-pages/{id}/components GET /api/v1/organizations/{org_id}/status-pages/{id}/incidents POST /api/v1/organizations/{org_id}/status-pages/{id}/incidents POST /api/v1/organizations/{org_id}/status-pages/{id}/incidents/{incident_id}/updates GET /api/v1/organizations/{org_id}/status-pages/{id}/maintenances POST /api/v1/organizations/{org_id}/status-pages/{id}/maintenances ``` Editing a status page needs the organization-manage capability; reading the public page needs nothing at all. ## Troubleshooting | Symptom | Cause / fix | | --- | --- | | "Private address" error | The URL resolves to a private/internal address, which checks may not reach | | Flapping up/down | Raise `downtime_threshold`, or increase `timeout_ms` for a slow endpoint | | `403`/`401` on every check | The target needs auth — add an `Authorization` header | | Passes in a browser, fails here | The endpoint requires cookies, a specific `Host`, or blocks the checker's IP | | Body assertion always fails | `body_match` is a plain substring, not a regex — and the body may be gzip-encoded HTML, not the JSON you expect |