Docs menu
Webhooks and events
Everything that happens to an incident is an event. You get events two ways, and most partners use both: webhooks push each one to your server as it happens, and the event feed lets you catch up on anything you missed.
| Event | When |
|---|---|
incident.created | An agent raised an SOS. |
incident.location | New location while it's open, at most every 15 seconds per incident. |
incident.acknowledged | A supervisor (or your system) is responding. data.responder says who. |
incident.escalated | Nobody acknowledged in time: every admin is alerted. |
incident.note | Someone added a note. |
incident.safe | The agent said they're safe. |
incident.resolved | Closed by a supervisor or your system; data.outcome is resolved or false_alarm. |
The payload
Every event has the same envelope. data is the incident as it is now.
{
"id": "evt_6c1f0e2a9b44d7e15a03c2f1",
"type": "incident.created",
"livemode": true,
"created_at": "2026-11-14T09:41:07Z",
"data": {
"id": "inc_4821",
"status": "open",
"escalation_step": 0,
"livemode": true,
"agent": { "external_id": "AGT-20931", "display_name": "Field agent, Ikeja" },
"team": "Ikeja",
"site": "Allen Avenue",
"note": "Followed after a visit",
"location": { "lat": 6.6018, "lng": 3.3515, "accuracy_m": 12, "postcode": "LA-17-B04-KC-21", "at": "2026-11-14T09:41:07Z" },
"battery": 41,
"acknowledged_at": null,
"responder": null,
"closed_at": null,
"outcome": null,
"resolution_note": null,
"created_at": "2026-11-14T09:41:07Z"
}
}Add a webhook
In the dashboard, open Webhooks, choose test or live, and enter an https:// URL. You can listen to every event or pick some. The signing secret (whsec_...) is shown once. Send test ping posts a signed ping event so you can check your endpoint.
Check the signature
Every delivery carries Hospify-Signature: t=<unix seconds>,v1=<hex> and Hospify-Event-Id. Verify it on the raw body, before parsing the JSON.
import express from "express";
import { verifyWebhook } from "@hospify/org-sdk/server";
app.post("/webhooks/hospify", express.text({ type: "application/json" }), async (req, res) => {
try {
const event = await verifyWebhook(req.body, req.get("Hospify-Signature"), process.env.HOSPIFY_WEBHOOK_SECRET!);
if (await alreadyHandled(event.id)) return res.sendStatus(200);
await handle(event); // event.type, event.data.agent.external_id ...
res.sendStatus(200);
} catch {
res.sendStatus(400);
}
});Answering and retries
- Answer with any 2xx within 10 seconds. Do the slow work after answering.
- Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours.
- An event can arrive more than once. Use
idto ignore repeats. - The dashboard shows every delivery, its status and the payload, and can retry one by hand.
The event feed
GET /v1/org/events returns events oldest first, 100 at a time. Keep the last id you processed and ask for ?after= it. Events are kept for 30 days.
let after = await db.get("hospify_last_event");
for (;;) {
const page = await hospify.events.list({ after, limit: 500 });
for (const event of page.data) {
await handle(event);
after = event.id;
}
await db.set("hospify_last_event", after);
if (!page.has_more) break;
}