Webhooks
A webhook sends an HTTPS POST to your server whenever something happens in your account, so you don’t have to keep asking the API.
Event types
Section titled “Event types”| Type | When it is sent |
|---|---|
event.created |
A camera or recorder reports an event (for example motion or a line crossing) |
recorder.offline |
A recorder or bridge stops answering |
recorder.online |
It is back |
clip.ready |
A clip export is ready to download |
clip.failed |
A clip export could not be made |
Add a webhook
Section titled “Add a webhook”Admins can add webhooks in IC Secure under Account, on the API keys tab, or through the API (POST /v1/webhooks with a read and write key).
- Select Add webhook and enter your endpoint URL. It must start with
https://and be reachable from the internet. - Choose the event types you want, or leave all of them selected.
- Save, and copy the signing secret (it starts with
whsec_). It is shown only once. You can make a new one later with New secret. - Use Send test to send a
pingand see what your server answered.
What you receive
Section titled “What you receive”Every delivery is a JSON body with the same envelope:
{ "id": "6f1c...", "type": "recorder.offline", "createdAt": "2026-10-26T15:04:05.000Z", "data": { "recorder": { "id": "...", "name": "Front office NVR", "siteId": "...", "online": false, "lastSeenAt": "2026-10-26T15:01:12.000Z" } }}Headers sent with each delivery:
| Header | Value |
|---|---|
IC-Secure-Event |
The event type, for example event.created |
IC-Secure-Delivery |
A unique ID for this delivery attempt |
IC-Secure-Signature |
t=<unix seconds>,v1=<signature> (see below) |
For clip.ready, data.clip has the clip’s details but no download link. Call GET /v1/clips/{id} to get one.
Check the signature
Section titled “Check the signature”Always check the signature before trusting a delivery. The signature is an HMAC-SHA256, using your signing secret, of the timestamp, a dot, and the raw request body: "<t>.<body>", written as hex.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody must be the exact bytes you received, before any JSON parsing.export function isFromIcSecure(secret, rawBody, header) { const parts = Object.fromEntries(header.split(',').map((p) => p.split('='))); const t = Number(parts.t); // Refuse anything more than 5 minutes old, so a copied request can't be replayed. if (!t || Math.abs(Date.now() / 1000 - t) > 300 || !parts.v1) return false; const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest(); const got = Buffer.from(parts.v1, 'hex'); return got.length === expected.length && timingSafeEqual(got, expected);}Answer quickly
Section titled “Answer quickly”Reply with any 2xx status within 10 seconds. Do the slow work after you reply, for example by putting the event on a queue.
If your server does not answer with 2xx, IC Secure tries again after 30 seconds, 2 minutes, 10 minutes, 30 minutes, 1 hour, and then every 2 hours, for up to 24 hours.
A delivery can occasionally arrive more than once. Use the envelope’s id to ignore repeats.
When deliveries keep failing
Section titled “When deliveries keep failing”If every delivery to an endpoint has failed for 3 days, IC Secure turns that webhook off and stops sending to it. The webhook list shows that it is off and why. Fix your server, then select Turn back on.
You can see the recent deliveries for each webhook, with your server’s answer and any error, for the last 30 days.
