Incidents · Reference
API
Open, update and resolve incidents from your own systems, and read what a status page shows. JSON over HTTPS, with a server secret.
Base URL
https://services.my-beast.com/incidents/api/v1Every path below comes after it. The examples read the address from MYBEAST_INCIDENTS_URL, which is Incidents' address without /api/v1:
MYBEAST_INCIDENTS_URL=https://services.my-beast.com/incidents
MYBEAST_INCIDENTS_KEY=sk_…Requests with a body send Content-Type: application/json. Times are ISO 8601 in UTC, like 2026-10-28T09:07:00Z. An incident's id is a UUID; its reference, like INC-12, is its number in the project.
Authentication
Every request carries a server secret for Incidents as a bearer token:
Authorization: Bearer sk_…A project's admins make one on Settings → Keys (how). It reaches its own project only, and opens no other product. Without a valid one the answer is 401.
Open an incident
POST/incidents
Opens an incident at Investigating, with no lead, and tells your team (“An incident declared”). With a dedupe_key that an open incident holds, it opens nothing: it adds a line to that incident's timeline, raises its severity if this one is worse, and answers 200.
| Field | Type | What it is |
|---|---|---|
titlerequired | string | What is wrong, as people will read it on a lock screen. Up to 200 characters. |
severityrequired | string | sev1, sev2 or sev3. |
summaryoptional | string | What is known so far, up to 5,000 characters. It goes out with the first notice and is the first line of the timeline. |
source.productoptional | string | Who is calling, in lowercase letters, digits and hyphens, up to 40: status, monitoring. The timeline names the caller by it. |
source.refoptional | string | Your own id of what went wrong, such as a monitor's, up to 120 characters. Shown on the incident. |
source.urloptional | string | A link back to it, http or https. The incident links to it. |
dedupe_keyoptional | string | Your name for this outage, up to 191 characters, such as status:42. While an incident with it is open, a call with it joins that incident. |
curl -X POST "$MYBEAST_INCIDENTS_URL/api/v1/incidents" \
-H "Authorization: Bearer $MYBEAST_INCIDENTS_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Checkout fails for card payments",
"severity": "sev1",
"summary": "Card payments answer 502 since 09:07. PayPal still works.",
"source": {
"product": "monitoring",
"ref": "checkout-cards",
"url": "https://grafana.example.com/d/checkout"
},
"dedupe_key": "monitoring:checkout-cards"
}'201 Created for a new incident, 200 OK when an open one took the call:
{
"id": "019a2f7c-5e3b-7d21-9c4f-2b8e6a1d3f50",
"reference": "INC-12",
"url": "https://services.my-beast.com/acme/shop/incidents/incidents/12"
}Resolve an incident
POST/incidents/resolve
Resolves the open incident with the dedupe key and tells your team (“Resolved”), with the summary as its message. 404 when no incident with the key is open.
| Field | Type | What it is |
|---|---|---|
dedupe_keyrequired | string | The key the incident was opened with. |
summaryoptional | string | What happened, up to 5,000 characters. It goes out with the notice that it is resolved. |
curl -X POST "$MYBEAST_INCIDENTS_URL/api/v1/incidents/resolve" \
-H "Authorization: Bearer $MYBEAST_INCIDENTS_KEY" \
-H "Content-Type: application/json" \
-d '{
"dedupe_key": "monitoring:checkout-cards",
"summary": "Card payments work again since 09:48."
}'{
"id": "019a2f7c-5e3b-7d21-9c4f-2b8e6a1d3f50",
"reference": "INC-12",
"url": "https://services.my-beast.com/acme/shop/incidents/incidents/12"
}Post an update
POST/incidents/{incident}/updates
Adds an update to an incident of the project, named by its id or its reference (INC-12, in any case). With a status it isn't in, the incident moves and the message goes out with that notice; a resolved or declined one moved to an open status is reopened. Without a new status, the message is an update on the timeline and sends no notice. 404 when the project has no such incident.
| Field | Type | What it is |
|---|---|---|
messagerequired | string | What is new, up to 5,000 characters. It is a public update: it may go out in a notice and on the status page. |
statusoptional | string | Move it to investigating, identified, monitoring, resolved or declined. The message then goes out with that change's notice. |
severityoptional | string | sev1, sev2 or sev3. Raised while it is open, everyone is told again. |
curl -X POST "$MYBEAST_INCIDENTS_URL/api/v1/incidents/INC-12/updates" \
-H "Authorization: Bearer $MYBEAST_INCIDENTS_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "We found the cause: the card provider'\''s new certificate. A fix is going out.",
"status": "identified"
}'{
"id": "019a2f7c-5e3b-7d21-9c4f-2b8e6a1d3f50",
"reference": "INC-12",
"url": "https://services.my-beast.com/acme/shop/incidents/incidents/12"
}List what a status page shows
GET/incidents/public
The incidents someone chose to show on the status page, open or resolved in the last 14 days, newest first. Each has its public updates, oldest first, at the status it had then. Never an internal note, the lead, a follow-up, the write-up, or who wrote anything. Status reads this for its public page.
curl "$MYBEAST_INCIDENTS_URL/api/v1/incidents/public" \
-H "Authorization: Bearer $MYBEAST_INCIDENTS_KEY"{
"incidents": [
{
"reference": "INC-12",
"title": "Checkout fails for card payments",
"severity": "sev1",
"status": "resolved",
"started_at": "2026-10-28T09:07:00Z",
"resolved_at": "2026-10-28T09:48:31Z",
"updates": [
{
"at": "2026-10-28T09:15:22Z",
"status": "investigating",
"body": "Card payments fail at checkout. We are looking into it."
},
{
"at": "2026-10-28T09:31:04Z",
"status": "identified",
"body": "We found the cause: the card provider's new certificate. A fix is going out."
},
{
"at": "2026-10-28T09:48:31Z",
"status": "resolved",
"body": "Card payments work again since 09:48."
}
]
}
]
}resolved_at is null while the incident is open. severity and status are the values below.
Severities and statuses
| Severity | Shown as |
|---|---|
sev1 | SEV1 Critical: Down or losing data for many: everyone drops what they are doing. |
sev2 | SEV2 Major: A main part broken or very slow for some: worked on now. |
sev3 | SEV3 Minor: Something small or with a way around it: worked on today. |
| Status | Means |
|---|---|
investigating | Something is wrong; we look for why. Every incident starts here. |
identified | We know why; the fix is on its way. |
monitoring | The fix is out; we watch that it holds. |
resolved | Over. The write-up can be written. |
declined | Not an incident after all. It ends, with no write-up. |
Errors
An error answers with a status code and a JSON body whose message says what is wrong:
| Status | When |
|---|---|
| 401 | No Authorization header, or a key that is wrong, revoked or another product's. |
| 402 | The project's subscription has ended. Its pages stay readable; nothing new is taken in. |
| 404 | Resolving: no open incident has the key. Posting an update: the project has no incident with that id or reference (The project has no such incident.). |
| 422 | A field is missing or wrong: errors names each one. |
| 422 | Opening an incident in a project that has not added Incidents. |
| 429 | More than 120 calls in a minute with one key. Retry-After says how many seconds to wait. |
{
"message": "unauthenticated"
}{
"message": "Shop's subscription ended on 30.11.2026: nothing new is taken in or added. Renew it at id.",
"type": "subscription_ended",
"link": "https://id.my-beast.com/acme/-/subscription"
}{
"message": "No open incident has this dedupe key."
}{
"message": "The severity field is required.",
"errors": {
"severity": [
"The severity field is required."
]
}
}{
"message": "Shop does not have Incidents: it is not in the project's packages."
}{
"message": "Too Many Attempts."
}Rate limit
Each server secret may make 120 calls a minute, far more than a monitor needs. Past that the answer is 429 with Retry-After, the seconds to wait. A call that answers 401 isn't counted.