Skip to content

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.

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

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.

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.

Terminal window
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 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.

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.