Webhooks

Signed run.* and dataset.updated events — Standard Webhooks verification in 10 lines

Register an endpoint and DeepSieve POSTs you events instead of making you poll:

bash
curl -s -X POST $BASE_URL/v1/webhooks \
  -H "Authorization: Bearer $DEEPSIEVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-app.example.com/hooks/deepsieve",
       "events": ["run.completed", "run.failed", "dataset.updated"]}'

The response includes "secret": "whsec_..." — shown once; use it to verify signatures. Endpoints must be public HTTPS; up to 10 active endpoints per workspace.

Events

EventWhen
run.completed / run.failed / run.cancelleda research run reaches a terminal state
dataset.updateda monitored value was re-verified and changed (payload: table, record_id, column, new + superseded evidence ids)

Verify signatures (Standard Webhooks)

Headers on every delivery: webhook-id, webhook-timestamp (unix seconds), webhook-signature (v1,<base64 HMAC-SHA256> over "{id}.{timestamp}.{body}"). Any Standard Webhooks library works, or by hand:

python
import base64, hashlib, hmac, time

def verify(secret: str, headers: dict, body: bytes) -> bool:
    ts = headers["webhook-timestamp"]
    if abs(time.time() - int(ts)) > 300:   # ±5 min tolerance
        return False
    msg = f"{headers['webhook-id']}.{ts}.".encode() + body
    mac = hmac.new(secret.encode(), msg, hashlib.sha256)
    expected = "v1," + base64.b64encode(mac.digest()).decode()
    return hmac.compare_digest(expected, headers["webhook-signature"])
typescript
import crypto from "node:crypto";

function verify(secret: string, headers: Record<string, string>, body: string): boolean {
  const ts = headers["webhook-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const mac = crypto.createHmac("sha256", secret)
    .update(`${headers["webhook-id"]}.${ts}.${body}`).digest("base64");
  return crypto.timingSafeEqual(Buffer.from(`v1,${mac}`),
    Buffer.from(headers["webhook-signature"]));
}

Delivery & retries

2xx acknowledges. Anything else retries with backoff: 1m, 5m, 30m, 2h, 12h, then the delivery is dropped. webhook-id is stable across retries — use it to deduplicate. Test with POST /v1/webhooks/{id}/ping. Manage with GET/PATCH/DELETE /v1/webhooks/{id} (PATCH keeps the secret; DELETE disables). Rotate a leaked secret with POST /v1/webhooks/{id}/rotate — it returns a new whsec_ once and the old secret stops signing immediately, so re-point your verifier first. Bring a disabled endpoint back with POST /v1/webhooks/{id}/enable (the secret is unchanged).

Webhooks — DeepSieve API