Webhooks let Guardhouse push notifications to your systems when events of interest occur —
for example, when an incident report is submitted or approved. You register one or more
subscribers (each owns a HMAC secret) and attach subscriptions (event/URI pairs)
to them.
This section requires an API key with KeyType=webhook.
Event catalog
Subscriptions are identified by an event and an eventType. Today the supported pairs
are:
eventType | event | Fires when… |
|---|
incidentreport | submit | An incident report is submitted by a guard. |
incidentreport | approve | An incident report is approved. |
incidentreport | reject | An incident report is rejected. |
timesheet | submit | A timesheet is submitted. |
timesheet | approve | A timesheet is approved. |
timesheet | reject | A timesheet is rejected. |
The catalog will grow. Unknown event / eventType combinations sent to
POST /api/webhook/subscriptions return 400 Bad Request.
Registration flow
1. Create a subscriber
curl -X POST https://gateway-api.guardhousehq.com/api/webhook/subscribers \
-H "Authorization: Bearer $ACCESS_TOKEN"
The response includes the secretToken you’ll use for signature validation. Store it
securely — it is shown only on creation and on rotation.
{
"status": { "code": 0, "errorMessage": "" },
"data": {
"id": 17,
"companyId": 42,
"secretToken": "whsec_4f8a…",
"createdDateTime": "2026-05-04T15:00:00Z",
"modifiedDateTime": null
},
"errors": []
}
2. Subscribe to an event
curl -X POST https://gateway-api.guardhousehq.com/api/webhook/subscriptions \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subscriberId": 17,
"event": "approve",
"eventType": "incidentreport",
"uri": "https://your-app.example.com/webhooks/guardhouse"
}'
The uri must be reachable over HTTPS and respond 2xx within a few seconds.
3. Receive notifications
Guardhouse will POST a JSON payload to your uri whenever the subscribed event fires.
Each request includes a Webhooks-Signature header containing an HMAC-SHA256 of the raw
request body, keyed by your subscriber’s secretToken.
Validating signatures
Compute the HMAC of the raw request body using your secretToken and compare against the
header value with a constant-time comparison.
import crypto from "node:crypto";
function isValidSignature(rawBody, headerSig, secretToken) {
const expected = crypto
.createHmac("sha256", secretToken)
.update(rawBody)
.digest("hex");
// Use timingSafeEqual to avoid timing oracles.
const a = Buffer.from(expected, "hex");
const b = Buffer.from(headerSig, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
import hmac, hashlib
def is_valid_signature(raw_body: bytes, header_sig: str, secret_token: str) -> bool:
expected = hmac.new(
secret_token.encode("utf-8"),
raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, header_sig)
If the signature does not match, respond with 403 and discard the message.
Delivery and retries
- A delivery is considered successful when your endpoint responds
2xx within the
request timeout.
- Failed deliveries (
Failed) are retried with backoff. After several failures the
notification status flips to MaxRetriesReached and is no longer retried — fix the
endpoint and contact support to replay missed events if needed.
- Notifications are delivered at-least-once. Use the event’s identifying ID
(
incidentReportId, timesheetId) to deduplicate.
Managing subscribers and subscriptions
| Operation | Endpoint |
|---|
| List a subscriber’s subs | GET /api/webhook/subscribers/{subscriberId}/subscriptions |
| Activate / deactivate / rotate secret | PUT /api/webhook/subscribers/{id} |
| Update a subscription URI / state | PUT /api/webhook/subscriptions/{id} |
To rotate a subscriber’s secretToken, send { "rotateSecretToken": true } to the
subscriber update endpoint. The response will contain the new token; the previous token
will no longer validate signatures.