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
| Field | Type | Always present? | Description |
|---|---|---|---|
monitor_id | UUID string | yes | Stable identifier — use this to look up the monitor via GET /monitors/{id}. |
monitor_name | string | yes | Human-readable label set on the monitor. Snapshot at delivery time. |
monitor_url | string | yes | Endpoint being checked. For non-HTTP monitors this is the host:port or DNS query. |
status | "up" | "down" | yes | New state of the monitor. |
timestamp | string (RFC 3339 UTC) | yes | When the status change was observed. |
error | string | only on down | Last 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>| Header | Description |
|---|---|
Content-Type | Always application/json. |
User-Agent | Obzerv-Webhook/1.0 — useful for log filtering. |
X-Obzerv-Signature | HMAC-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 });
}