Receive real-time HTTPS notifications when provisioning events occur. Register endpoints, verify HMAC-SHA256 signatures, and manage delivery.
Outbound webhooks let you register HTTPS endpoints that receive a signed POST request each time a provisioning event occurs in your organisation. Use them to keep downstream systems — your HRMS, access control platform, or audit log — in sync without polling the API. Requires the webhooks scope.
user.created — A user was provisioned via SCIM or the Management API.user.updated — A user's profile attributes were changed.user.deactivated — A user was deactivated and blocked from signing in.user.reactivated — A previously deactivated user was reactivated.room.member_added — A user was added to a room.room.member_removed — A user was removed from a room. A PUT full member sync emits individual room.member_added / room.member_removed events for the users it adds or removes.group.created — A SCIM group was created.group.deleted — A SCIM group was deleted.sms_list.created — An SMS recipient list was created.sms_list.updated — A list was renamed, re-timed, or given a new external_id.sms_list.deleted — A list and its members were deleted.sms_list.members_added — Numbers were added to or updated on a list.sms_list.members_removed — Numbers were removed from a list.// POST /webhooks
{
"url": "https://ingest.example.com/whoot-events",
"events": ["user.created", "user.deactivated", "user.deleted"],
"description": "HRMS sync endpoint"
}
// Response — 201 Created
{
"id": "d4e5f6a7-b8c9-0d1e-a2b3-c4d5e6f7a8b9",
"url": "https://ingest.example.com/whoot-events",
"events": ["user.created", "user.deactivated", "user.deleted"],
"secret": "whs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "active",
"description": "HRMS sync endpoint",
"created_at": "2026-05-19T10:30:00.000Z"
}secret field is returned only once in the creation response. Store it securely before closing the response — it cannot be retrieved later. You will use this secret to verify the signature of every incoming delivery.url (required) — Must be an HTTPS URL. Plain HTTP is not accepted.events (required) — An array of one or more event names from the list above.secret (optional) — Provide your own signing secret (minimum 16 characters), or omit to have one generated automatically.description (optional) — A human-readable label for the registration.GET /webhooks — List all webhook registrations for the organisation. Secrets are excluded from list and get responses.GET /webhooks/{id} — Get a single webhook registration.PATCH /webhooks/{id} — Update the URL, event subscriptions, status (active or disabled), or description.DELETE /webhooks/{id} — Permanently remove a webhook registration.POST /webhooks/{id}/test — Send a synthetic test event to the webhook URL and return the HTTP status and response body your endpoint returned.Every delivery includes two headers:
- X-Whoot-Signature — The HMAC-SHA256 signature of the raw request body, formatted as sha256=<hex>.
- X-Whoot-Event — The event name (e.g. user.created).
Verify the signature before trusting the payload. Use a constant-time comparison to prevent timing attacks.
// Node.js — verify a webhook signature
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
if (signatureHeader.length !== expected.length) return false;
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}All events share a common envelope. The data object contains the affected resource.
{
"event": "user.created",
"tenant_id": "f0e9d8c7-b6a5-4321-fedc-ba9876543210",
"tenant_slug": "acme",
"timestamp": "2026-05-19T10:30:00.000Z",
"data": {
"user": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "carol@acme.com",
"display_name": "Carol Santos",
"active": true
}
}
}2xx range are treated as success. All other codes — including redirects — are treated as failure.last_trigger_status field on the webhook record reflects the outcome of the most recent delivery attempt (success or failed).Set status: "disabled" to pause deliveries without deleting the registration. Events fired while a webhook is disabled are not queued or replayed — they are silently dropped. Re-enable by setting status: "active" at any time.
// PATCH /webhooks/{id} — pause deliveries
{ "status": "disabled" }
// PATCH /webhooks/{id} — resume deliveries
{ "status": "active" }The sms_list.* events identify the list and how many numbers moved. They deliberately do not carry the numbers themselves — your system sent them, and repeating them back widens where that personal data travels for no gain.
{
"event": "sms_list.members_added",
"tenant_id": "f0e9d8c7-b6a5-4321-fedc-ba9876543210",
"tenant_slug": "acme",
"timestamp": "2026-08-13T09:41:22.104Z",
"data": {
"list": {
"id": "0fcb33a8-9f3d-4a53-8127-64e7d22f65f5",
"external_id": "fixture-88213",
"name": "Fixture 2026-09-05 stewards"
},
"added": 128,
"api_key_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}