Guides
Webhooks
Subscribe to every audited change, verify signatures, handle retries.
Every state-changing action in Panoptes writes one audit row inside its own transaction. When a branch has an active webhook subscription, an event is staged beside that row — so an event is sent if and only if the change committed.
Event names
Events are <module>.<verb>, for example pos.void_line, inventory.post_transfer,
hr.run_payroll. The catalogue is served by the Webhooks endpoints.
A subscription may name exact events, a module wildcard (pos.*) or everything (*).
- Subscribing to
hr.*,accounting.*oraudit.*requiresview_hr,view_glorview_audit— those payloads carry before-and-after values.*requires all three. - Credential and secret lifecycle, evidence views and face registration are never broadcast.
Delivery
Panoptes POSTs to your URL with a JSON body and these headers:
| Header | Value |
|---|---|
x-panoptes-signature | sha256=<hex HMAC-SHA256 of the raw body, keyed by the subscription secret> |
x-panoptes-event-id | Unique event id — use it to de-duplicate |
x-panoptes-event-type | e.g. pos.create_sale |
x-panoptes-api-version | The subscription's API version |
x-panoptes-retry-number | 0 on the first attempt |
Answer any 2xx quickly. Anything else is retried on a backoff ladder; a delivery that keeps
failing is marked exhausted and can be replayed from the dashboard.
Verifying the signature
import crypto from 'node:crypto';
function verify(rawBody, header, secret) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return (
header.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected))
);
}
Always verify against the raw request bytes, before JSON parsing.
Testing
Use the subscription's Send test action to post a test event to your endpoint, and inspect deliveries (status, response body, attempts) from the same screen.