Webhook Notifications
Using generic webhooks, you can send incident notifications in JSON format to any HTTPS endpoint. This is convenient for integrating with your own systems or automation tools.
Requirements
Section titled “Requirements”- HTTPS required: HTTP (unencrypted) URLs cannot be used
- Public endpoint: Private network URLs are blocked
- Signature verification secret: Any string of 1 or more characters
External Service Preparation
Section titled “External Service Preparation”- Prepare a publicly reachable HTTPS endpoint that accepts a JSON
POSTbody - Generate a signature-verification secret known only to Manako and the receiver
- Verify the HMAC-SHA256 signature using the raw request body and
X-Manako-Signature - Return a 2xx response after accepting the request. Manako does not follow 3xx redirects
Manako Configuration
Section titled “Manako Configuration”- In the dashboard, open Settings > Integrations, then select Configure for Webhook
- Enter a recognizable Name
- Enter the receiver under Webhook URL (HTTPS)
- Enter the shared secret under Secret
- Select Add
Payload
Section titled “Payload”Notifications are sent as POST requests. The Content-Type is application/json.
Common Fields
Section titled “Common Fields”Fields included in all events:
| Field | Type | Description |
|---|---|---|
event | string | Event type |
teamId | string | Team ID |
timestamp | string | ISO 8601 formatted timestamp |
monitorName, monitorId, and incidentId are included in incident events. Maintenance events contain monitorIds and monitorNames instead.
Additional Fields by Event
Section titled “Additional Fields by Event”| Event | Field | Type | Description |
|---|---|---|---|
incident.created | monitorUrl | string | Monitored URL |
incident.created | severity | string | Severity ("critical" or "warning") |
incident.created / incident.resolved | title | string | undefined | Title for manual incidents (omitted for automatic incidents) |
maintenance.started | monitorIds, monitorNames, maintenanceUntil | string[] / string | Affected monitors and scheduled end time |
maintenance.ended | monitorIds, monitorNames | string[] | Affected monitors |
Payload Examples
Section titled “Payload Examples”{ "event": "incident.created", "monitorName": "API Server", "monitorId": "01JWAB1234567890ABCDEF", "teamId": "01JWAB0987654321FEDCBA", "incidentId": "01JWABINCIDENT12345678", "monitorUrl": "https://api.example.com/health", "severity": "critical", "timestamp": "2025-01-15T10:30:00.000Z"}{ "event": "incident.resolved", "monitorName": "API Server", "monitorId": "01JWAB1234567890ABCDEF", "teamId": "01JWAB0987654321FEDCBA", "incidentId": "01JWABINCIDENT12345678", "timestamp": "2025-01-15T10:45:00.000Z"}Signature Verification
Section titled “Signature Verification”Notification requests include an X-Manako-Signature header. You can verify that the request was sent from Manako by validating the HMAC-SHA256 signature of the request body using your secret.
The signature format is sha256=\{hex_digest\}.
Verification Code Examples
Section titled “Verification Code Examples”import crypto from "node:crypto";
function verifySignature(body, secret, signature) { const expected = "sha256=" + crypto.createHmac("sha256", secret).update(body).digest("hex"); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature), );}
// Express exampleapp.post("/webhook", express.text({ type: "application/json" }), (req, res) => { const signature = req.headers["x-manako-signature"]; if (!verifySignature(req.body, process.env.WEBHOOK_SECRET, signature)) { return res.status(401).send("Invalid signature"); } const payload = JSON.parse(req.body); console.log(`Event: ${payload.event}, Monitor: ${payload.monitorName}`); res.status(200).send("OK");});import hashlibimport hmacimport os
def verify_signature(body: bytes, secret: str, signature: str) -> bool: expected = "sha256=" + hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature)
# Flask example@app.route("/webhook", methods=["POST"])def webhook(): signature = request.headers.get("X-Manako-Signature", "") if not verify_signature( request.get_data(), os.environ["WEBHOOK_SECRET"], signature ): return "Invalid signature", 401 payload = request.get_json() print(f"Event: {payload['event']}, Monitor: {payload['monitorName']}") return "OK", 200Troubleshooting
Section titled “Troubleshooting”- The URL is rejected during setup: It must be public HTTPS and must not resolve to a private or reserved address.
- The receiver redirects: Manako sends with
redirect: "manual"; configure the final receiver URL directly. - The signature does not match: Do not reserialize the JSON. Calculate
sha256={hex}over the raw body using the same Secret stored in Manako. - A test arrives but normal notifications do not: Test uses
event: "test". For normal notifications, also check monitor-specific channel links. If links exist but all are disabled, Manako does not fall back to team-wide channels. - Notifications stop during rapid state changes: After more than three state changes for the same monitor within 10 minutes, the flap guard suppresses subsequent notifications.
- Notifications appear duplicated: A sent retry for the same incident, channel, and event type is skipped, and a database UNIQUE constraint also prevents duplicate records. Failed or pending records are retried.
Test sends an actual signed request with event: "test" and has a 60-second cooldown after success. A failed Webhook does not stop other channels.
The flap guard and incident-level sent check apply to incident notifications. Maintenance notifications bypass both controls and go to active, verified team channels.