Read your monitors and quiet alerts from a script.
The API reads a workspace's monitors, incidents and maintenance windows, creates, edits and deletes monitors and heartbeats, and lets a deploy job pause a monitor, acknowledge or resolve an incident, post an update and plan, end or call off maintenance. JSON over HTTPS, one key per script.
Before you start
A key belongs to one workspace, and only an owner of that workspace makes one, in Account, Security, under API keys. It is shown once; we keep only a hash of it, so a lost key is rotated for a new one rather than shown again. An owner sees every key of the workspace in Settings, Team, and can revoke any of them. When the person who made a key stops being an owner, their keys are revoked at once.
A key is Read only or Read and write. Read only reads; Read and write also makes the writes below. Every plan has the API:
- Free3 active keys
- Solo20 active keys
- Team20 active keys
A plan change never revokes a key; over the new count, no new key can be made until one goes.
Making a request
Every address starts with https://api.upglory.io/v1, over HTTPS only. The key goes in the Authorization header as a bearer key, never in the address: a request with a key in its address is refused even when the header is right, because addresses end up in logs.
curl https://api.upglory.io/v1/monitors \ -H "Authorization: Bearer $UPGLORY_API_KEY"
Answers are JSON. Every time is ISO 8601 in UTC, such as 2026-09-29T10:04:00Z. A body is a JSON object; a field an endpoint does not know is refused, so a typo never passes as a default.
Endpoints
List monitors
GET /v1/monitors
Every monitor and heartbeat of the key's workspace, oldest first, 100 to a page. A draft is not listed. Needs any key.
Query parameters
- page Which page, from 1. Left out, the first.
200 with monitors, page, per_page, total and next_page, which is null on the last page. A page that is not a whole number from 1 is 400 bad_request.
curl https://api.upglory.io/v1/monitors?page=1 \ -H "Authorization: Bearer $UPGLORY_API_KEY"
One monitor
GET /v1/monitors/{id}
One monitor or heartbeat by its id. Needs any key.
200 with the monitor. An id that is not a monitor of this workspace is 404 not_found, the same as one that does not exist.
curl https://api.upglory.io/v1/monitors/{id} \
-H "Authorization: Bearer $UPGLORY_API_KEY"List incidents
GET /v1/incidents
Incidents 100 to a page, the open ones first, then newest first. How far back the list reaches is your plan's history, as in the app. Needs any key.
Query parameters
- state
openfor the open ones,allfor every one. Left out,all. - since An ISO 8601 time such as
2026-09-29T10:00:00Z; incidents opened before it are left out. - cursor The
next_cursorof the page before, for the page after it. Send the samestateandsincewith it. Left out, the first page.
200 with incidents, total (every incident that matches), next_cursor, which is null on the last page, and has_more, true when there is a next page. A page starts after the last row of the one before, so an incident that opens or closes between two pages never makes another one appear twice. A state, a time or a cursor it cannot read is 400 bad_request.
curl https://api.upglory.io/v1/incidents?state=open \ -H "Authorization: Bearer $UPGLORY_API_KEY"
One incident
GET /v1/incidents/{id}
One incident by its id. Needs any key.
200 with the incident, or 404 not_found.
curl https://api.upglory.io/v1/incidents/{id} \
-H "Authorization: Bearer $UPGLORY_API_KEY"List maintenance
GET /v1/maintenance
The workspace's maintenance windows, the latest start first, up to 200, with what each covers and its state. Needs any key.
200 with windows.
curl https://api.upglory.io/v1/maintenance \ -H "Authorization: Bearer $UPGLORY_API_KEY"
One maintenance window
GET /v1/maintenance/{id}
One window by its id. Needs any key.
200 with the window. An id that is not a window of this workspace is 404 not_found, the same as one that does not exist.
curl https://api.upglory.io/v1/maintenance/{id} \
-H "Authorization: Bearer $UPGLORY_API_KEY"Create a monitor
POST /v1/monitors
Create an http monitor or a heartbeat, under the same rules as the app: your plan's count, its fastest interval and its number of locations, the wait only on Team, channels of this workspace only, and no address inside a private network. Its history names the key. Send an Idempotency-Key header to make a retry safe, as below. Needs a read and write key.
Body
- kind Required,
httporheartbeat. - name Required, at most 80 characters.
- url An
httpmonitor's address,httporhttps, on the public internet. - interval_seconds
30,60or300, no faster than your plan. Left out, your plan's fastest. - regions Location codes, as in an answer. Left out, as many as your plan allows.
- expected_status, keyword, timeout_seconds What a good answer is: its status code (left out,
200), words it contains, and the seconds before a check gives up (left out,10, at most30). - degraded_ms An answer slower than this many milliseconds is degraded. Left out or
0, off. - carries_secrets True when the address holds a secret, so the app masks it. A monitor that sends a header or a body carries secrets whatever this says.
- method The method each check sends:
GET,HEAD,POST,PUT,PATCH,DELETEorOPTIONS. Left out,GET. - headers Up to 10 request headers, as the whole list:
{"name": "Authorization", "value": "Bearer abc123"}for a new value, or{"name": "Authorization", "set": true}to keep the value already stored. A kept value stays on the host it was saved for: change the address and type the value again. An edit that sends an empty list removes them; one that leaves them out keeps them. A value is never read back. - body, body_type The body each check sends, under 4 KB, on
POST,PUT,PATCHorDELETEonly, and its type:application/json(left out),application/x-www-form-urlencoded,text/plainorapplication/xml. - description, period_seconds, grace_seconds A heartbeat's line about the job, how often it pings and how late a ping may be. The two durations are required on a create, in seconds, each a whole number of minutes.
- wait_seconds On Team, how long the first alert is held back. A new heartbeat waits as the workspace does; give it a wait of its own with an edit.
- mode
followthe workspace's default channels,ownfor thechannelssent, or on a createinboxfor nobody outside the app. Left out,follow. - channels Alert channel ids of this workspace, for
own. - tags Up to 10 tags of up to 24 characters, on either kind.
- on_page True to show it on the workspace's first status page.
- confirm_silent True when a monitor that alerts nobody is meant. Without it, one that would alert nobody is refused.
201 with the monitor, which for a heartbeat includes its ping_url. A field it cannot take is 400 bad_request with fields; one that would alert nobody without confirm_silent is refused in fields too. Over your plan's count it is 409 plan_limit with the limit.
curl -X POST https://api.upglory.io/v1/monitors \
-H "Authorization: Bearer $UPGLORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"kind": "heartbeat", "name": "Nightly backup", "period_seconds": 86400, "grace_seconds": 3600}'Edit a monitor
PATCH /v1/monitors/{id}
Change a monitor or a heartbeat. Only the fields sent change; the rest stay as they are. The fields are the create's, without kind, and a monitor keeps its kind. A changed address is checked again. Needs a read and write key.
Body
- revision Optional, the
revisiona read answered. If the monitor changed since, nothing is written and the answer is409 changed; read it again and send the new one. - confirm_silent True when a change to
modeorchannelsleaves nobody alerted, and that is meant.
200 with the monitor as it is now. A field it cannot take is 400 bad_request with fields.
curl -X PATCH https://api.upglory.io/v1/monitors/{id} \
-H "Authorization: Bearer $UPGLORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"interval_seconds": 60}'Delete a monitor
DELETE /v1/monitors/{id}
Delete a monitor or a heartbeat for good, with its history, as Delete does in the app. Its open incident is resolved first, and the incident's timeline names the key. Needs a read and write key.
204 with no body. A second delete is 404 not_found.
curl -X DELETE https://api.upglory.io/v1/monitors/{id} \
-H "Authorization: Bearer $UPGLORY_API_KEY"Pause a monitor
POST /v1/monitors/{id}/pause
Pause a monitor or a heartbeat, the way Pause does in the app. A monitor's history names the key, as key deploy-bot, where it would name a person. Pausing one that is already paused changes nothing. Needs a read and write key.
200 with the monitor as it is now. No body is needed.
curl -X POST https://api.upglory.io/v1/monitors/{id}/pause \
-H "Authorization: Bearer $UPGLORY_API_KEY"Resume a monitor
POST /v1/monitors/{id}/resume
Resume a paused monitor or heartbeat. Resuming counts against your plan, as it does in the app. Needs a read and write key.
200 with the monitor as it is now. Over the plan's count, or over the plan's monitors at 30 seconds when this one checks that often, it is 409 plan_limit and the monitor stays paused.
curl -X POST https://api.upglory.io/v1/monitors/{id}/resume \
-H "Authorization: Bearer $UPGLORY_API_KEY"Acknowledge an incident
POST /v1/incidents/{id}/acknowledge
Acknowledge an open incident, which stops its repeat alerts. The timeline names the key. A second acknowledge answers the first one's time. Needs a read and write key.
200 with the incident. An incident that was resolved without being acknowledged is 409 resolved: nobody is being alerted about it.
curl -X POST https://api.upglory.io/v1/incidents/{id}/acknowledge \
-H "Authorization: Bearer $UPGLORY_API_KEY"Declare an incident
POST /v1/incidents
Declare an incident by hand on one monitor or heartbeat, for a problem the checks cannot see, as Declare incident does in the app. Its alert channels hear it at once, named as declared by the key, and the status pages that show it carry it as they carry any incident. The checks never close it: resolve it when it is over. Needs a read and write key.
Body
- monitor_id Required, a monitor or heartbeat of this workspace.
- title Required, at most 120 characters. What is wrong, in the words the alerts and the incident lead with.
- update Optional, at most 1000 characters. The status page's first update; only a monitor a published status page shows takes one.
- stage Optional, one of
investigating,identified,monitoring, the update's stage. Left out,investigating.
201 with the incident. A monitor with an open incident is 409 already_open with that incident's incident_id; post an update there instead. An update on a monitor no published status page shows is 409 no_status_page, and nothing is declared.
curl -X POST https://api.upglory.io/v1/incidents \
-H "Authorization: Bearer $UPGLORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"monitor_id": "{id}", "title": "Checkout is slow for some customers", "update": "We are looking into slow payments.", "stage": "investigating"}'Resolve an incident
POST /v1/incidents/{id}/resolve
Resolve an open incident by hand, as Resolve does in the app, for a recovery the checks cannot see. The timeline names the key, and the alert channels, the status page's subscribers and the inbox hear it is back. If the next check still fails, a new incident opens. It takes no body. Needs a read and write key.
200 with the incident. One already resolved is answered as it is, and nothing is recorded.
curl -X POST https://api.upglory.io/v1/incidents/{id}/resolve \
-H "Authorization: Bearer $UPGLORY_API_KEY"Post an update
POST /v1/incidents/{id}/updates
Post an update on an incident. A status page that shows the incident shows the update, and its subscribers hear it, as they do an update posted in the app; its author is the key. Needs a read and write key.
Body
- body Required, at most 1000 characters. The words the status page shows.
- stage Optional, one of
investigating,identified,monitoring,resolved. Postingresolvedlabels the update; the incident closes when the checks pass.
201 with id, incident_id, body, stage and posted_at. An incident no published status page shows is 409 no_status_page, and one hidden from its pages is 409 hidden_from_status_page: nobody would read the update, so nothing is posted.
curl -X POST https://api.upglory.io/v1/incidents/{id}/updates \
-H "Authorization: Bearer $UPGLORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"body": "Rolling back the release.", "stage": "identified"}'Plan maintenance
POST /v1/maintenance
Plan a maintenance window around a deploy. While it runs, alerts for what it covers are held, and the status pages that show those monitors say so. It ends by itself; nothing has to call back. Needs a read and write key.
Body
- title Required, at most 120 characters.
- detail Optional, at most 400 characters, shown on the status page.
- starts_at Optional, an ISO 8601 time. Left out, now.
- ends_at Required, an ISO 8601 time after the start and at most a week from it.
- monitor_ids The monitors and heartbeats it covers, by id. Each must be of this workspace.
- page_ids Status pages it covers, by id: everything each page shows when the window runs, as Everything on this page does in the app.
- tags Tags it covers: every monitor and heartbeat carrying one when the window runs. Name at least one id, page or tag.
- suppress_alerts Optional, true when left out: alerts are held while it runs.
201 with the window and its state: scheduled, announced (within three days of its start), running or over.
curl -X POST https://api.upglory.io/v1/maintenance \
-H "Authorization: Bearer $UPGLORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Deploy", "ends_at": "2026-09-29T10:30:00Z", "tags": ["checkout"]}'Edit maintenance
PATCH /v1/maintenance/{id}
Change a window that has not started. Only the fields sent change; the fields are the plan's. Once it runs, what visitors and subscribers were told has happened, so it is ended or deleted instead. Needs a read and write key.
200 with the window as it is now. A window that has started is 409 window_changed with its state, and nothing changes.
curl -X PATCH https://api.upglory.io/v1/maintenance/{id} \
-H "Authorization: Bearer $UPGLORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"starts_at": "2026-09-30T02:00:00Z", "ends_at": "2026-09-30T03:00:00Z"}'End maintenance
POST /v1/maintenance/{id}/end
End a running window now, as End early does in the app. Alerts are no longer held, and the status page's subscribers hear the work is over. It takes no body. Needs a read and write key.
200 with the window, over, its ended_at set. A window that is not running is 409 window_changed with its state.
curl -X POST https://api.upglory.io/v1/maintenance/{id}/end \
-H "Authorization: Bearer $UPGLORY_API_KEY"Delete maintenance
DELETE /v1/maintenance/{id}
Delete a window that is not over. Subscribers told it was coming hear it is cancelled, and if it is running they hear it ended. A window that is over stays as the record of the work. Needs a read and write key.
204 with no body. A window that is over is 409 window_changed with its state.
curl -X DELETE https://api.upglory.io/v1/maintenance/{id} \
-H "Authorization: Bearer $UPGLORY_API_KEY" Sending a create twice
A create whose answer was lost on the way back can be sent again safely with an Idempotency-Key header: any text of yours, up to 255 printable characters, such as an id your script already has for the job. Within a day, the same key with the same body makes nothing and answers the first answer again, with Idempotent-Replayed: true. The same key with another body is 409 idempotency_conflict, and while the first request still runs, 409 in_progress.
A refused create keeps nothing, so the corrected request runs with the same key. Keys are your workspace's own and are forgotten after 24 hours.
curl -X POST https://api.upglory.io/v1/monitors \
-H "Authorization: Bearer $UPGLORY_API_KEY" \
-H "Idempotency-Key: deploy-4821" \
-H "Content-Type: application/json" \
-d '{"kind": "http", "name": "Checkout", "url": "https://shop.example.com"}'AI assistants
An AI assistant connects to Upglory's MCP server at https://api.upglory.io/mcp with the address alone. The first time, your browser opens Upglory and asks you to approve the assistant for one workspace, with Read or, when it asks to change things, Read and write. It can then tell you what is down and, with Read and write, set up a monitor, pause one or acknowledge an incident when you ask, named in the history as you via the assistant.
It acts as you, within what your role allows, and never reaches billing, the team, keys or workspace settings, even for an owner. It has the same limits and answers as the endpoints above and reaches nothing they do not. You see each connected app in Account, Security, and Disconnect ends it at once; leaving the workspace ends it too.
Claude Code, from a terminal, then approve in the browser:
claude mcp add --transport http upglory https://api.upglory.io/mcp
Cursor, in ~/.cursor/mcp.json, or .cursor/mcp.json in a project:
{
"mcpServers": {
"upglory": {
"url": "https://api.upglory.io/mcp"
}
}
}Any other client that signs in to a remote server through the browser connects the same way, with the address alone.
The MCP server
POST /mcp
Streamable HTTP with one JSON answer to each request; nothing is kept between requests. Needs a connected app or any key. A connection approved for Read, or a read key, is offered the 6 tools that read, and Read and write, or a read and write key, all 21.
- list_monitors The workspace's monitors and heartbeats with their status, uptime and last check, as
GET /v1/monitorsanswers them, 100 to a page. - get_monitor One monitor or heartbeat by its id.
- list_incidents Incidents 100 to a page, the open ones first,
openorall,sincea time, andcursorfor the next page. - get_incident One incident by its id.
- list_maintenance The workspace's maintenance windows with what each covers and its state, as
GET /v1/maintenanceanswers them. - get_maintenance One maintenance window by its id.
- pause_monitor Pause a monitor or heartbeat. Its history names the key. Needs a read and write key.
- resume_monitor Resume one, within your plan's count. Needs a read and write key.
- acknowledge_incident Acknowledge an open incident, which stops its repeat alerts. Needs a read and write key.
- post_incident_update Post an update, with a
bodyand an optionalstage, which the status page shows. Needs a read and write key. - create_monitor Create an
httpmonitor, with the fields ofPOST /v1/monitors. Needs a read and write key. - create_heartbeat Create a heartbeat. The answer has its
ping_url. Needs a read and write key. - update_monitor Change an
httpmonitor; only the fields given change. Needs a read and write key. - update_heartbeat Change a heartbeat; only the fields given change. Needs a read and write key.
- delete_monitor Delete a monitor or heartbeat for good. It is marked destructive, so your assistant asks you first. Needs a read and write key.
- resolve_incident Resolve an open incident by hand. The timeline names the key. Needs a read and write key.
- declare_incident Declare an incident by hand on one monitor or heartbeat, with the fields of
POST /v1/incidents. Needs a read and write key. - create_maintenance Plan a maintenance window, with the fields of
POST /v1/maintenance. Needs a read and write key. - update_maintenance Change a window that has not started; only the fields given change. Needs a read and write key.
- end_maintenance End a running window now. Needs a read and write key.
- delete_maintenance Delete a window that is not over. It is marked destructive, so your assistant asks you first. Needs a read and write key.
With a key, for scripts and CI
Where nobody is at a browser to approve, such as a script or CI, a client sends a workspace key in the header instead: the address, and Authorization: Bearer with the key. Its writes are named after the key.
claude mcp add --transport http upglory https://api.upglory.io/mcp \ --header "Authorization: Bearer $UPGLORY_API_KEY"
{
"mcpServers": {
"upglory": {
"url": "https://api.upglory.io/mcp",
"headers": {
"Authorization": "Bearer ${env:UPGLORY_API_KEY}"
}
}
}
}A key in an assistant's settings reads your workspace like any script's. Keep it out of a repository, and give an assistant a Read only key unless it needs to write.
What comes back
A monitor
Both kinds share the first fields; a field of the other kind is left out rather than sent empty.
- id, name, created_at As in the app.
- kind
httpfor an address we check,heartbeatfor a job that pings us. - revision Send it back with an edit, so one based on settings that changed since is refused.
- status For
httpone ofup,degraded,down,paused,pending(no check yet),blocked(Can't check: bot protection on the site asks our checker to prove it is a person, so we cannot see the site; it is not counted as down);degradedmeans one location is failing, or two checks in a row from one location passed but took longer than the monitor's slow threshold. For a heartbeat one ofup,down,waiting(no ping yet),paused. - paused True while nobody is checking it.
- uptime_percent_30d The last thirty days, 0 to 100.
- url, interval_seconds An
httpmonitor's address and how often it is checked. - expected_status, keyword, timeout_seconds What an
httpmonitor counts as a good answer, and how long a check waits. - method, headers The method an
httpmonitor's checks send, and each request header as{"name": "Authorization", "set": true}. A header's value is never in an answer;headersis left out when it sends none. - body, body_type The body an
httpmonitor's checks send, and its type. Only a Read and write key reads them, as only it can change them. - tags The tags it carries, on either kind. Left out when it carries none.
- channels The alert channel ids it notifies, on either kind. Left out when it notifies none.
- regions The locations an
httpmonitor is checked from, as codes:fra,stoorcmh. At most 2 per monitor on paid plans and 1 on Free. - last_checked_at, response_ms An
httpmonitor's newest check in any location, and the fastest good answer of the latest round. - description, period_seconds, grace_seconds, last_ping_at A heartbeat's line about the job, its schedule and its newest ping.
- ping_url The address a heartbeat's job calls. Only a Read and write key reads it: it is the job's secret, and anyone holding it can ping.
An incident
- id, number
numberis the workspace's own count, the #12 the app shows. - state
openorresolved. - monitor_id, monitor_kind, monitor_name What was down, and whether it is
httporheartbeat. - cause The one line the app shows, such as
expected 200, got 502. - opened_at, resolved_at, acknowledged_at The last two are
nulluntil they happen. - duration_seconds How long it lasted, or has lasted so far.
A maintenance window
- id, title, detail As in the app.
- starts_at, ends_at When it starts, and the end it was given.
- ended_at When it was ended early,
nulluntil then. - monitor_ids, page_ids, tags What it covers.
- suppress_alerts True when alerts for what it covers are held while it runs.
- state
scheduled,announced(within three days of its start, on the status page),runningorover.
Errors
Every refusal is {"error": "<code>", "request_id": "<id>"} with a fixed code a script can switch on.
- 400 bad_request A parameter or a body it cannot read. A body's problems come back in
fields, one message per field, and a field the endpoint does not know is refused rather than ignored. - 401 unauthorized No key, a key it does not know, a revoked key, or a key in the address. Every bad key gets the same answer.
- 403 forbidden A read key asking to write.
- 404 not_found No such address, or an id that is not of this workspace.
- 405 method_not_allowed A method the address does not take, a browser's preflight among them.
Allownames the ones it does. - 409 plan_limit Creating or resuming would go over your plan's count, or over the monitors it checks every 30 seconds. A create's answer has the count in
limit. - 409 changed An edit with a
revisionthat is no longer the monitor's. Nothing was written. - 409 idempotency_conflict An
Idempotency-Keyused before with another body. - 409 in_progress An
Idempotency-Keywhose first request is still running. - 409 resolved Acknowledging an incident that is already over.
- 409 window_changed Editing a maintenance window that has started, ending one that is not running, or deleting one that is over. The answer's
stateis the window's. - 409 no_status_page Posting an update on an incident no published status page shows.
- 409 hidden_from_status_page Posting an update on an incident hidden from its status pages.
- 413 too_large A body over 64 KiB.
- 423 workspace_deletion_pending The workspace is booked for deletion. Every request is refused until it is kept in the app.
- 429 rate_limited Too many requests, or too many bad keys from one address.
Retry-Aftersays how many seconds to wait. - 500 internal_error Something failed on our side. Quote the
request_idwhen you write to us.
Limits
- Per key60 requests a minute
- Per workspace300 requests a minute, all its keys together
- Bad keys30 an hour from one address, then 429 until the hour has passed
- Body64 KiB
A key that is used is marked with when it was last used, at most every five minutes, with a coarse network prefix and the country. The key list in the app shows them, so an owner can tell a key nobody uses from one a script still needs.
What no key reaches
Neither Read only nor Read and write reaches these. They are changed in the app, by a person.
- A heartbeat's ping address, for a Read only key. A Read and write key reads it, so a script can set a job up from start to end.
- Alert channels themselves, the workspace's default channels, and the repeat rules. A monitor's own channels are chosen from the ones made in the app.
- Importing monitors from a file.
- Status pages, their design, their domains and their subscribers. An update or a window posted through the API shows on them as one posted in the app.
- An incident's explanation, its write-up, its evidence, and who acknowledged or resolved it.
- Billing, the plan and invoices.
- The team: members, invitations and roles.
- API keys themselves. Keys are made, rotated and revoked in the app, by an owner.
- Any other workspace. A key belongs to one workspace and reads only that one.
Something your scripts need is missing? Tell us what it is for. Write to us