Using the REST API
Everything the MCP server can do is a plain HTTP endpoint too, so a cron job, a deploy script, or a status board can drive your monitoring without speaking MCP. Same tokens, same rules, same answers: both surfaces run the same code underneath.
Base URL
https://undownable.com/api/v1
JSON in, JSON out, authenticated with an
Authorization: Bearer <token> header.
Versioned, so a script written today keeps working: a breaking change ships as
/api/v2.
1. Create a token
- Log in and open API tokens in the dashboard sidebar.
- Pick the abilities the script actually needs. A status board wants
monitors:readand nothing else. - If you belong to more than one team, pin the token to one of them so it can never touch the others.
- Copy the token immediately: it is shown exactly once.
curl -H "Authorization: Bearer $UNDOWNABLE_TOKEN" \
https://undownable.com/api/v1/monitors
2. Two rules worth knowing up front
Anything that writes must name its team
Pass team on every create, update, or delete.
Reads may omit it, in which case they span every team the token can see. We refuse to guess on a
write on purpose: the alternative is following whichever team you last clicked in the dashboard,
which would one day point a nightly script at the wrong systems without anything having changed
on its side. A token pinned to one team may omit it, because there is nothing to guess.
config replaces, it does not merge
Sending config on an update overwrites the stored
settings wholesale, so include every key the monitor still needs. Drop the
keyword from a keyword monitor and it quietly
becomes a plain reachability check that passes against an error page.
3. Endpoints
Every response is shaped {"data": ..., "meta": ...}.
Monitor and window IDs are the ULIDs the list endpoints return.
| Endpoint | Ability | What it does |
|---|---|---|
GET /teams | teams:read | Teams this token can reach, and the identifiers every other endpoint accepts as team |
GET /monitors | monitors:read | Every monitor with status, interval, and ID. Filter with ?tag= |
GET /monitors/{id} | monitors:read | One monitor in detail: config (secrets masked), 24h, 7d, and 30d uptime, recent incidents |
POST /monitors | monitors:write | Create an HTTP, keyword, JSON, SSL, TCP, DNS, ping, or heartbeat monitor |
PATCH /monitors/{id} | monitors:write | Edit in place, keeping check history. The type cannot change |
DELETE /monitors/{id} | monitors:write | Remove a monitor and stop collecting its history |
POST /monitors/{id}/pausePOST /monitors/{id}/resume | monitors:write | Stop and restart checking. A pause never expires: for planned work use a maintenance window instead |
GET /incidents | incidents:read | Recent outages, newest first. Filter with ?monitor_id=, ?ongoing_only=, ?limit= |
GET /status-pagesPOST /status-pages | status-pages:readstatus-pages:write | List your status pages, or publish one with chosen monitors in display order |
GET /maintenance-windowsPOST /maintenance-windows | maintenance-windows:readmaintenance-windows:write | List windows (filter with ?state=), or schedule downtime for chosen monitors, a tag, or the whole team |
POST /maintenance-windows/{id}/endDELETE /maintenance-windows/{id} | maintenance-windows:write | End an active window early, or cancel one entirely |
GET /fleet-status | fleet-status:read | One compact health rollup per team, built for dashboards |
4. A worked example
Open a maintenance window before you take something down, and close it when you are finished. This is the pattern worth copying into a deploy script: a window ends itself, so forgetting to close it leaves you monitored rather than blind.
WINDOW=$(curl -sS -X POST https://undownable.com/api/v1/maintenance-windows \
-H "Authorization: Bearer $UNDOWNABLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"team": "acme", "name": "Rack move", "tag": "homelab", "duration_minutes": 90}' \
| jq -r .data.id)
# ... do the work ...
curl -sS -X POST https://undownable.com/api/v1/maintenance-windows/$WINDOW/end \
-H "Authorization: Bearer $UNDOWNABLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"team": "acme"}'
Good to know
Errors say what to do
Failures return a message, and validation failures an errors object. 422 means fix the request, 403 means the token lacks the ability, 404 means the token cannot see it, 409 means the resource is in the wrong state.
Plan limits apply
Monitor count, fastest check interval, and status page count follow your plan, and the error names the limit you hit.
Rate limited
120 requests per minute per token. The MCP server has its own limit of 60 per minute.
Secrets stay masked
Request headers you store on a monitor, and anything else that looks like a credential, come back redacted. Lists return config key names only.
Put it in your pipeline
Free includes 10 monitors, every alert channel, and a public status page. No card required.
Start monitoring free