Webhooks

Payload reference

Exact JSON shape Obzerv POSTs to your webhook URL.

The webhook body is a flat JSON object — no envelope, no data wrapper. Same shape on both down and up transitions, with error present only on down.

Down event

{
  "monitor_id": "b8f1d6c2-3a4e-4f9d-9ab1-2e8d7c5f4d3e",
  "monitor_name": "Production API",
  "monitor_url": "https://api.example.com/health",
  "status": "down",
  "timestamp": "2026-04-25T14:29:58Z",
  "error": "HTTP 502 Bad Gateway"
}

Up (recovery) event

{
  "monitor_id": "b8f1d6c2-3a4e-4f9d-9ab1-2e8d7c5f4d3e",
  "monitor_name": "Production API",
  "monitor_url": "https://api.example.com/health",
  "status": "up",
  "timestamp": "2026-04-25T14:32:18Z"
}

Field reference

FieldTypeAlways present?Description
monitor_idUUID stringyesStable identifier — use this to look up the monitor via GET /monitors/{id}.
monitor_namestringyesHuman-readable label set on the monitor. Snapshot at delivery time.
monitor_urlstringyesEndpoint being checked. For non-HTTP monitors this is the host:port or DNS query.
status"up" | "down"yesNew state of the monitor.
timestampstring (RFC 3339 UTC)yesWhen the status change was observed.
errorstringonly on downLast observed error (e.g. HTTP 502, dial tcp i/o timeout, dns no such host). Free-form — don't pattern-match in production.

Headers

POST /your/webhook/path
Content-Type: application/json
User-Agent: Obzerv-Webhook/1.0
X-Obzerv-Signature: sha256=<64-char hex>
HeaderDescription
Content-TypeAlways application/json.
User-AgentObzerv-Webhook/1.0 — useful for log filtering.
X-Obzerv-SignatureHMAC-SHA256 of the raw body, keyed with the channel's signing secret. Format: sha256=<hex>. See Verify webhook signatures.

Receiving sample

Node.js (Express)

import express from 'express';
const app = express();

app.post('/obzerv-webhook', express.json(), (req, res) => {
  const { monitor_name, monitor_url, status, error, timestamp } = req.body;
  console.log(
    status === 'down'
      ? `🔴 ${monitor_name} (${monitor_url}) is DOWN: ${error} @ ${timestamp}`
      : `🟢 ${monitor_name} recovered @ ${timestamp}`,
  );
  res.sendStatus(204);
});

app.listen(8080);

Python (Flask)

from flask import Flask, request
app = Flask(__name__)

@app.post("/obzerv-webhook")
def hook():
    e = request.get_json(silent=True) or {}
    if e.get("status") == "down":
        print(f"🔴 {e.get('monitor_name')} is DOWN: {e.get('error')}")
    else:
        print(f"🟢 {e.get('monitor_name')} recovered")
    return ("", 204)

Next.js (App Router)

import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const event = await req.json();
  // ...handle event
  return new NextResponse(null, { status: 204 });
}