sentinel

Webhook integration

Receive signed JSON alerts at your own endpoint

Endpoint requirements

  • HTTPS only
  • a public hostname
  • redirects are not followed
  • 3xx counts as failed delivery
  • respond with 200 within 10 seconds

The payload shape

{
  "version": 1,
  "id": "3f6d3f0e-6a4e-4b9c-9a2e-7f1d2c8b5a90",
  "event": "overdue",
  "sent_at": "2026-07-16T04:01:00.000Z",
  "monitor": {
    "name": "acme-production",
    "slug": "0a1b2c3d-4e5f-6789-abcd-ef0123456789",
    "url": "https://boringbackup.com/monitors/0a1b2c3d-…",
    "schedule": "0 3 * * *",
    "timezone": "Europe/Sofia",
    "grace_seconds": 3600
  },
  "last_ping_at": "2026-07-14T03:00:12.000Z"
}

Event kinds

eventfired when
overdueno good backup by deadline + grace
recovereda good backup landed while overdue
testyou pressed the test button

Verifying the signature

Every request carries a timestamp and an HMAC of it plus the body — verify both, reject stale timestamps, and a captured request can't be replayed. The secret is shown only on create. If lost, it can be rotated.

X-Sentinel-Timestamp: 1784167260
X-Sentinel-Signature: sha256=<hex hmac>

The signature is HMAC-SHA256 over "{timestamp}.{body}" keyed with the integration's secret:

# Rails
body      = request.body.read
timestamp = request.headers["X-Sentinel-Timestamp"]
signature = request.headers["X-Sentinel-Signature"]

expected = "sha256=" + OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{body}")

head :unauthorized unless ActiveSupport::SecurityUtils.secure_compare(expected, signature)
head :unauthorized if (Time.now.to_i - timestamp.to_i).abs > 300

Retries & idempotency

Webhooks are retried up to 5 times with a minute cooldown between attempts.

Use theid field as an idempotent key.

Further reading