Skip to content
CribScore
Skip to content
CribScore Docs

API Reference

Webhooks API

Subscribe to facility events, verify the signature, and route the payload into Slack, Teams, n8n, or your own automation layer.

Subscribe#

Register a callback URL with one or more facility IDs and event types. Each subscription returns a `subscription_id` and a signing secret. Keep the secret private — use it to verify deliveries.

curl -X POST "https://api.cribscore.co/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "facility_id": "fac_01HZ8X9K2D7N3M5P0AYR4FTC2W",
    "callback_url": "https://automation.example.com/cribscore",
    "events": ["score_change", "license_change", "violation_recorded"]
  }'

Delivery shape#

Every delivery is a POST with JSON body `{ event, payload }`. Two headers identify the delivery: `X-CribScore-Signature` (HMAC-SHA256 of the raw body) and `X-CribScore-Event` (event name).

Bodyjson
{
  "event": "score_change",
  "delivery_id": "dlv_01HZ8YK4X3F8B9T5RC2WN6PHJD",
  "occurred_at": "2026-05-15T07:42:11Z",
  "payload": {
    "facility_id": "fac_01HZ8X9K2D7N3M5P0AYR4FTC2W",
    "previous_safety_score": 87,
    "new_safety_score": 81,
    "delta": -6,
    "source_url": "https://www.ccld.dss.ca.gov/.../2026-05-14.pdf"
  }
}

Verify the signature#

Compute `HMAC_SHA256(body, signing_secret)` and compare with the `X-CribScore-Signature` header in constant time. Reject deliveries that fail verification — never trust an unverified payload, especially when fanning out to chat or email.

import crypto from "node:crypto";

const SIGNING_SECRET = process.env.CRIBSCORE_WEBHOOK_SECRET ?? "";

export function verifyCribscoreSignature(rawBody: string, header: string): boolean {
  const expected = crypto
    .createHmac("sha256", SIGNING_SECRET)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(header.replace(/^sha256=/, ""), "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Starter templates#

Pre-built relays for Slack, Microsoft Teams, email, and n8n — copy a template, drop your signing secret, ship the relay.

  • Slack: https://www.cribscore.co/automation/slack-incoming-webhook.json
  • Teams: https://www.cribscore.co/automation/teams-webhook.json
  • Email relay: https://www.cribscore.co/automation/email-relay.json
  • n8n starter: https://www.cribscore.co/automation/n8n-starter.json
Template URLsbash
Starter templates (copy + paste, signed-payload ready):
- https://www.cribscore.co/automation/slack-incoming-webhook.json
- https://www.cribscore.co/automation/teams-webhook.json
- https://www.cribscore.co/automation/email-relay.json
- https://www.cribscore.co/automation/n8n-starter.json

Endpoint reference#

POST/v1/webhooks

Create a webhook subscription for a facility.

NameTypeRequiredDescription
facility_idstringrequiredTarget facility ULID.
callback_urlstringrequiredHTTPS POST URL.
eventsstring[]requiredSubset of `score_change` | `license_change` | `violation_recorded`.
Requestbash
curl -X POST "https://api.cribscore.co/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "facility_id": "fac_01HZ8X9K2D7N3M5P0AYR4FTC2W",
    "callback_url": "https://automation.example.com/cribscore",
    "events": ["score_change", "license_change", "violation_recorded"]
  }'
Responsejson
{ "subscription_id": "sub_...", "signing_secret": "whsec_..." }
GET/v1/webhooks

List subscriptions for your API key.

NameTypeRequiredDefaultDescription
limitintegeroptional201-100.
offsetintegeroptional00+.
Requestbash
curl -sS "https://api.cribscore.co/v1/webhooks" -H "Authorization: Bearer YOUR_API_KEY"
Responsejson
{ "data": [...], "meta": {} }
DELETE/v1/webhooks/{subscription_id}

Cancel a subscription.

NameTypeRequiredDescription
subscription_idstringrequiredSubscription ULID.
Requestbash
curl -X DELETE "https://api.cribscore.co/v1/webhooks/sub_01HZ8YK4X3F8B9T5RC2WN6PHJD" \
  -H "Authorization: Bearer YOUR_API_KEY"
Responsejson
{ "deleted": true, "subscription_id": "sub_..." }