Uptimia

Webhook Payload Reference

10 min read Updated Sep 10, 2026

Every custom webhook alert is one POST with a flat JSON body of eleven top-level keys. The same eleven arrive for all twelve monitor families and every status, so one receiver handles everything, and grouped incidents add one extra object beside them. Custom Webhooks Setup covers creating the integration.

The Request Uptimia Sends

Property Value
Method POST
Content type application/json (added for you; Content-Length is set too)
Body A JSON object, never an array and never form-encoded
Extra headers Only the lines you entered in Custom Headers, sent verbatim
Redirects Not followed
TLS Verified. Uptimia connects to the same address it looked up just before sending, so nothing can swap the address in between
Timeout 10 seconds to connect, 10 seconds total for a single-monitor alert, 30 seconds total for a grouped digest

The payload keys are a published contract. Existing keys are never renamed and never reordered. New keys can appear beside them, so parse by name and ignore what you do not recognize.

The Fields

Field Type What it carries
id integer The monitor's id. Not the incident id, and not unique on its own: ids restart per family, so pair it with monitor_type
monitor_type string The family slug: uptime, transaction, fpl, rum, ssl, domain, virus, server, heartbeat, blacklist, dns, api. Speed monitors emit fpl; the interface calls them Speed
monitor_name string The monitor's name as you set it
monitor_unique_id string The optional Unique ID from the monitor's Advanced tab. Uptime monitors only; "" everywhere else and whenever it is unset
monitor_status string down, up, or test
severity string critical or trouble while down, "" on recovery, test for a test send
incident_start_time string ISO 8601 with offset, in your account's time zone: 2026-08-31T14:02:11+02:00
incident_end_time string ISO 8601 on recovery, "" while the incident is open
incident_duration_seconds integer 0 until the incident closes; the closed incident's length on the recovery POST
message string The same one-line alert text the chat channels receive. Uptime, transaction, Real User and Speed-recovery alerts open with the *ALERT*: marker. Every other family, and Speed while down, opens with UPTIMIA ALERT (CRITICAL), (TROUBLE), (UP) or (HEALTHY). A grouped digest carries the digest's own summary line instead, as in the grouped example below. Branch on the other fields, never on this string
monitor_notes string The monitor's note, "" when there is none

A Down Alert

{
  "id": 41827,
  "monitor_type": "uptime",
  "monitor_name": "Checkout API",
  "monitor_unique_id": "checkout-prod",
  "monitor_status": "down",
  "severity": "critical",
  "incident_start_time": "2026-08-31T14:02:11+02:00",
  "incident_end_time": "",
  "incident_duration_seconds": 0,
  "message": "*ALERT*: Project Checkout API is DOWN",
  "monitor_notes": "Runbook: wiki.example.com/checkout-oncall"
}

A Recovery Alert

{
  "id": 41827,
  "monitor_type": "uptime",
  "monitor_name": "Checkout API",
  "monitor_unique_id": "checkout-prod",
  "monitor_status": "up",
  "severity": "",
  "incident_start_time": "2026-08-31T14:02:11+02:00",
  "incident_end_time": "2026-08-31T14:19:40+02:00",
  "incident_duration_seconds": 1049,
  "message": "*ALERT*: Project Checkout API is UP after 17 minutes",
  "monitor_notes": "Runbook: wiki.example.com/checkout-oncall"
}

Field Values Across the Four Events

Field Down Recovery Test Grouped digest
monitor_status down up test down or up
severity critical or trouble "" test critical, trouble or ""
monitor_name The monitor's name The monitor's name Test monitor The group's anchor monitor
monitor_unique_id Set or "" Set or "" Test unique ID (optional) Anchor's, or ""
incident_start_time ISO 8601 ISO 8601 None When the group opened
incident_end_time "" ISO 8601 None When the group resolved, else ""
incident_duration_seconds 0 Seconds None (a string) 0 until the group resolves
monitor_notes Note or "" Note or "" "" Note only when the digest covers one monitor
group absent absent absent present

Note: The test send is the one payload that breaks the types. incident_duration_seconds arrives as the string None rather than a number, so a receiver with strict schema validation rejects the test while handling live alerts correctly. Branch on monitor_status == "test" and return early.

Severity, and Why It Is Empty on Recovery

severity describes the incident being reported. A recovery reports the end of one rather than a condition of its own. The field is then "", not omitted and not null. While a monitor is down it is one of two values:

Value Meaning
critical The monitor is failing outright: unreachable, wrong status code, failed assertion
trouble A degraded or warning-tier condition: a certificate or domain expiring soon, a page loading too slowly

Do not treat trouble as informational for reporting purposes. Trouble time counts against availability exactly as critical time does; How Uptime Is Calculated covers the arithmetic.

Timestamps and Duration

incident_start_time is set on the first POST and repeated unchanged on the recovery POST, which makes it the half of a duplicate-check key that never changes. Both timestamps are DATE_ATOM strings, ISO 8601 with a numeric UTC offset. They are rendered in your account's time zone, so the offset moves with daylight saving. Parse the offset; never assume UTC.

While an incident is open, incident_end_time is "" and incident_duration_seconds is 0. Both are filled on the recovery POST, for every monitor family. The duration is the closed incident's full length in seconds, not the time since the last alert.

Monitor Notes

monitor_notes carries the monitor's free-text note, up to 500 characters, set in the monitor editor's Advanced tab. An on-call script uses it to print a runbook link or an escalation phone number beside the alert. It is "" when no note is set and on every test payload. Monitor Notes covers where else the note appears.

Grouped Incidents: The group Object

When several monitors fail inside the join window, Uptimia sends one digest instead of one alert per monitor. The digest keeps all eleven fields, which describe the anchor monitor (the first monitor that failed in the group) plus the group's own start, end and duration. It adds a group object beside them. Alert Groups covers when grouping applies.

Key Type What it carries
id integer The alert group's id
size integer Member incidents in the group
distinct_monitors integer Distinct monitors among those members; lower than size when one monitor failed, recovered and failed again
down_count integer Distinct monitors still down at send time
members array One object per member incident: type, id, name, status (down, or up once that member recovered)
acknowledged boolean Whether someone has acknowledged the group
acknowledged_by string Label of the person who acknowledged, "" when nobody has
acknowledged_at integer Unix epoch seconds, 0 when not acknowledged (the one timestamp in the payload that is not ISO 8601)
{
  "id": 41827,
  "monitor_type": "uptime",
  "monitor_name": "Checkout API",
  "monitor_unique_id": "",
  "monitor_status": "down",
  "severity": "critical",
  "incident_start_time": "2026-08-31T14:02:11+02:00",
  "incident_end_time": "",
  "incident_duration_seconds": 0,
  "message": "DOWN | 3 monitors are DOWN — Checkout API and 2 more",
  "monitor_notes": "",
  "group": {
    "id": 902,
    "size": 3,
    "distinct_monitors": 3,
    "down_count": 3,
    "members": [
      { "type": "uptime", "id": 41827, "name": "Checkout API", "status": "down" },
      { "type": "uptime", "id": 41830, "name": "Cart service", "status": "down" },
      { "type": "api",    "id": 118,   "name": "Payments chain", "status": "down" }
    ],
    "acknowledged": false,
    "acknowledged_by": "",
    "acknowledged_at": 0
  }
}

On a grouped payload, monitor_notes is populated only when the digest is about one monitor: exactly one still down on a down payload, or a single-monitor group on the recovery. With two or more monitors in the digest there is no one note to carry. The key is then present and empty rather than absent.

Why a Webhook Gets Acknowledgment Facts but No Acknowledge Link

Human channels (email, SMS, Slack, Telegram, Discord, Mattermost, Microsoft Teams and Twilio) can carry a signed link that acknowledges the group in one click. Webhooks and PagerDuty do not. They are machine and log pipelines, and anything that reads the feed could replay a signed acknowledgment token. They receive the facts (acknowledged, acknowledged_by, acknowledged_at) but cannot silence a live escalation. Acknowledging Incidents and MTTA covers the workflow.

Responding Correctly

Answer with any 2xx and Uptimia treats the alert as delivered: 200, 201, 202 and 204 all count. A 3xx does not count. Redirects are never followed, so a receiver that redirects to a canonical path fails on every attempt. Any other status, a TLS error or a timeout is a failure.

Return the 2xx before you do slow work. Uptimia gives the whole request 10 seconds. An endpoint that opens a ticket synchronously and then answers will time out, and Uptimia records the send as failed and retries it, leaving you with duplicate tickets.

Delivery Guarantees and Deduplication

A failed delivery goes back on the queue, is retried up to 5 times over the next 15 minutes, and is then dropped. Integrations Overview has the schedule. Delivery is at-least-once, so an endpoint slow enough to time out and then succeed on a retry receives the same event twice.

Deduplicate on monitor_type + id + incident_start_time + monitor_status. That tuple is identical across every retry of one event. It differs for the down and the recovery of the same incident.

Grouped digests are the exception. One group sends several down payloads: the opening digest, an update each time more monitors join, and one per escalation step and repeat round. Every one of them carries the anchor's id, the group's incident_start_time and monitor_status: "down", so that tuple cannot tell them apart. When a group object is present, add message to the key. The digest, the join update, each escalation step and each repeat round read differently, while a retry of one send repeats the same line.

Authenticating the Request

There is no HMAC and no signature header. Anyone who knows the URL can post to it, so protect the URL itself:

  1. Use HTTPS and a long, unguessable path segment: a random 32-character token, not /uptimia.
  2. Add a secret header in Custom Headers, one per line in Name: value format, and reject any request that does not carry it.
  3. Rotate both by editing the integration; the change applies to the next alert with no re-attach on the monitors.

The webhook integration form with two lines typed into Custom Headers, the only place to add a shared secret, since Uptimia signs no request.

Uptimia checks the URL when you save it, and again before every send, so you cannot use an endpoint on a private or reserved address, such as anything on your own internal network. Integrations Overview lists the rules, and Custom Webhooks Setup has the address ranges.

A Worked Receiver

This Flask endpoint checks the secret header, skips test sends, deduplicates, and answers 204, an empty success response.

from flask import Flask, request, abort

app = Flask(__name__)
SECRET = "abc123"
seen = set()

@app.post("/uptimia-9f3c1a7e4b2d")
def uptimia():
    if request.headers.get("Authorization") != f"Bearer {SECRET}":
        abort(401)

    e = request.get_json(force=True)

    if e["monitor_status"] == "test":
        return "", 204

    key = (e["monitor_type"], e["id"], e["incident_start_time"], e["monitor_status"])
    if e.get("group"):
        key += (e["message"],)
    if key in seen:
        return "", 204
    seen.add(key)

    group = e.get("group")
    if group:
        print(f'group {group["id"]}: {group["down_count"]} monitors down — {e["message"]}')
    else:
        print(f'{e["monitor_name"]} is {e["monitor_status"]} — {e["message"]}')
        if e["monitor_notes"]:
            print("note:", e["monitor_notes"])

    return "", 204

Replay a real body against it while you develop, instead of waiting on an outage:

curl -i -X POST https://hooks.example.com/uptimia-9f3c1a7e4b2d \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer abc123' \
  -d '{"id":41827,"monitor_type":"uptime","monitor_name":"Checkout API","monitor_unique_id":"checkout-prod","monitor_status":"down","severity":"critical","incident_start_time":"2026-08-31T14:02:11+02:00","incident_end_time":"","incident_duration_seconds":0,"message":"*ALERT*: Project Checkout API is DOWN","monitor_notes":""}'

Related Articles

Was this article helpful?