Uptime monitoring
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:
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.
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
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/<address>. 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, notprod-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.
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 |