Skip to content

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.

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

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).

  1. Select Add webhook and enter your endpoint URL. It must start with https:// and be reachable from the internet.
  2. Choose the event types you want, or leave all of them selected.
  3. 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.
  4. Use Send test to send a ping and see what your server answered.

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.

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);
}

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.

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.