Webhooks
Webhooks push organization events to your HTTPS endpoint as they happen. Available on Pro and Enterprise plans; managed by owners (in-app under Billing & plan → Developer, or via the API).
Managing subscriptions
GET /orgs/{orgId}/webhooks
POST /orgs/{orgId}/webhooks
DELETE /orgs/{orgId}/webhooks/{id}
Creating:
POST /orgs/{orgId}/webhooks
{ "url": "https://example.com/hooks/miseline", "events": ["catering.quote_accepted", "tray.delivered"] }
{
"id": "2b83fc70-9340-4694-b176-bcf8bfd314c1",
"url": "https://example.com/hooks/miseline",
"events": ["catering.quote_accepted", "tray.delivered"],
"secret": "whsec_1MkzDArsg8jZkU4EF-io8KoYFNGlXmxA",
"active": true,
"lastDeliveryAt": null,
"lastStatus": null,
"failureCount": 0
}
The secret is returned once, on creation — store it; listing never includes it. Subscribe to specific events or to everything with "events": ["*"].
Events
| Event | Fires when |
|---|---|
catering.quote_accepted | A client accepts a catering quote (it becomes the contract) |
catering.invoice_paid | A catering invoice is marked paid |
policy.published | A policy version is published |
work_order.resolved | An equipment work order is resolved |
tray.delivered | A tray-line ticket is marked delivered |
alert.created | Any operational alert opens (temperature excursion, offline sensor, expiring lot, certification lapse, nutrition risk) |
report.published | A site or account report is generated |
signoff.decided | A dietitian approves or rejects a submitted review |
walkthrough.submitted | A guided walkthrough's responses are submitted |
engagement.finalized | An audit engagement's report is finalized |
The delivery
Deliveries are POSTs to your URL:
POST /hooks/miseline
content-type: application/json
x-miseline-event: catering.quote_accepted
x-miseline-signature: 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
{
"event": "catering.quote_accepted",
"orgId": "05b955b6-c196-4e31-a383-4251dee16f8a",
"at": "2026-08-08T20:40:01.598Z",
"data": { "eventId": "…", "quoteId": "…", "totalCents": 184500 }
}
data varies per event and carries ids you can dereference through the regular API.
Verifying the signature
x-miseline-signature is the hex HMAC-SHA256 of the raw request body, keyed with your subscription's secret. Verify before trusting a delivery:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, signatureHeader, secret) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signatureHeader, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
Compute over the raw bytes — parsing and re-serializing the JSON can change the byte sequence and break the comparison.
Delivery semantics
- Deliveries are best-effort, at-most-once: there is no automatic retry queue. Design your handler to reconcile via the API rather than depend on every event arriving.
- Your endpoint has 8 seconds to respond; any
2xxcounts as delivered. Respond fast and process async. - Every attempt updates
lastDeliveryAt,lastStatus(0means unreachable/timeout), andfailureCount(reset on success) — pollGET /orgs/{orgId}/webhooksto monitor health. - Deliveries can arrive out of order; use
atand the ids indatawhen sequence matters.