Webhooks
Webhooks enqueue outbound notifications when DataSquares events happen, so your receiver does not poll. Delivery is asynchronous and best-effort: the product action has already succeeded if enqueueing or delivery later fails. Point one at Slack for readable messages or at your own endpoint for automation.
Two webhook kinds
Section titled “Two webhook kinds”| Kind | Payload |
|---|---|
| generic | structured JSON — for your own services and automation |
| slack | a ready-to-post Slack incoming-webhook message (text + blocks, markdown sections with a compact field list) — paste a Slack webhook URL and events render readably in the channel, no middleware |
Events
Section titled “Events”Each webhook subscribes to the events it cares about. The ones you’ll use first:
| Event | Fires when |
|---|---|
alert.fired |
a metric alert crosses its threshold |
digest.sent |
a metric digest runs — Slack hooks get one readable line per metric, with pace status |
source.sync_failed |
a scheduled source refresh fails |
metric.anomaly_detected |
a certified metric’s scheduled anomaly sweep finds an unusual movement |
model.created / model.published |
a data model is created or published |
dashboard.created / dashboard.published / dashboard.deleted |
a dashboard changes lifecycle state |
report.deleted |
a report is deleted |
report.delivered |
a scheduled report delivery completes |
pipeline.run_failed |
a pipeline run reaches failed state |
usage.budget_exceeded |
product usage crosses its configured budget |
access.granted / access.revoked |
a resource access grant changes |
compute.queue_stuck, compute.vm_unreachable, compute.provision_failing |
compute recovery detects an operational fault |
webhook.test |
you press test — verify the pipe before trusting it |
Event names are free-form subscriptions rather than a server-validated enum. Subscribing to a name that nothing emits is accepted but produces no delivery.
Managing webhooks
Section titled “Managing webhooks”Webhooks are managed through the REST API today (no web UI yet): create a webhook with its URL, kind, and event subscriptions; send a test delivery; and inspect recent deliveries to debug a silent channel.
You can update the name, URL, subscriptions, headers, secret, or enabled state, and delete a webhook. Kind cannot be changed after creation; create a new webhook to switch between generic and Slack formatting. Secrets are used internally and are never returned by list/create responses. Webhook management currently requires an authenticated workspace but has no finer-grained webhook permission scope.
curl -X POST https://<your-host>/api/webhooks \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{ "name": "Operations Slack", "url": "https://hooks.slack.com/services/T000/B000/XXXX", "kind": "slack", "events": ["alert.fired", "source.sync_failed"] }'Generic payload and signatures
Section titled “Generic payload and signatures”Generic deliveries use this envelope:
{ "event": "alert.fired", "timestamp": "2026-08-27T10:00:00.000Z", "data": {}}Every attempt includes X-DataSquares-Timestamp and a stable
X-DataSquares-Delivery-Id for receiver-side deduplication. If a secret is
configured, X-DataSquares-Signature is
sha256=<HMAC-SHA256(secret, timestamp + "." + deliveryId + "." + rawBody)>.
Reject stale timestamps and compare signatures in constant time. The delivery
ID stays the same across retries; the signed timestamp changes per attempt.
Custom headers are merged first. DataSquares then overwrites protected headers such as content type, user agent, delivery ID, timestamp, and signature so a stored custom header cannot forge them.
Delivery, retries, and diagnostics
Section titled “Delivery, retries, and diagnostics”Each subscribed webhook gets its own delivery record. The default retry budget is five attempts. The direct-processing fallback uses exponential delays of 1, 2, 4, 8, then 16 seconds; BullMQ deployments use the queue retry path with the same per-row attempt budget. Each outbound request has a configurable timeout (10 seconds by default).
Targets are checked when configured and again at delivery time to resist DNS rebinding. Public deployments reject private, loopback, and cloud-metadata addresses; a self-hosted operator may explicitly allow private targets. Redirects are never followed because the redirect destination has not passed the target check. A blocked target is marked permanently failed.
POST /api/webhooks/{id}/test queues a webhook.test delivery and returns the
test envelope; it does not wait for a successful HTTP response. Inspect
GET /api/webhooks/{id}/deliveries for newest-first status, attempts, last
error, retry time, and delivery time. The list defaults to 50 rows and supports
up to 200 per page with an offset.