Skip to main content
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:
eventTypeeventFires when…
incidentreportsubmitAn incident report is submitted by a guard.
incidentreportapproveAn incident report is approved.
incidentreportrejectAn incident report is rejected.
timesheetsubmitA timesheet is submitted.
timesheetapproveA timesheet is approved.
timesheetrejectA 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

OperationEndpoint
List a subscriber’s subsGET /api/webhook/subscribers/{subscriberId}/subscriptions
Activate / deactivate / rotate secretPUT /api/webhook/subscribers/{id}
Update a subscription URI / statePUT /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.