Skip to content

Webhooks

Webhooks let external systems react to Factflow events without polling. 8 endpoints under /api/v1/webhooks. Event types: execution status changes, storage writes, lineage failures.

Terminal window
curl -X POST http://localhost:8000/api/v1/webhooks \
-H 'Content-Type: application/json' \
-d '{
"url": "https://your-service.dnb.no/hooks/factflow",
"event_types": ["execution.completed", "execution.failed"],
"secret": "shared-secret-for-hmac",
"filters": {
"config_id": "CONFIG_ID"
}
}'

CLI:

Terminal window
factflow webhook create \
--url https://your-service.dnb.no/hooks/factflow \
--events execution.completed,execution.failed \
--secret SHARED_SECRET

Returns a webhook_id.

EventFired when
execution.startedExecution transitions to running
execution.completedTerminal state completed
execution.failedTerminal state failed
execution.cancelledTerminal state cancelled
storage.object_createdAny storage write
lineage.failedA lineage row transitions to failed

Multiple events per subscription are supported.

Optional filtering narrows the subscription:

  • config_id — only events for a specific pipeline config
  • execution_id — only for one execution
  • route_id — only for a specific route (storage / lineage events)
  • adapter_name — only for a specific adapter

Filters are AND’d. Unfiltered subscriptions receive every matching event type globally.

  • HTTP POST to your URL, JSON body
  • X-Factflow-Signature: sha256=<hmac> header — HMAC-SHA256 of the body using the shared secret
  • X-Factflow-Event: <event_type> header
  • X-Factflow-Delivery: <delivery_id> header (idempotency key for your handler)

If your endpoint responds non-2xx or times out:

  1. Retry 1: after 1 minute
  2. Retry 2: after 5 minutes
  3. Retry 3: after 30 minutes
  4. Retry 4: after 2 hours
  5. Retry 5: after 12 hours
  6. Dead-letter after 6 failed attempts

Response codes that trigger retry: 5xx, 408 (timeout), 429 (rate limited), network errors. 4xx (except 408/429) are treated as terminal — no retry.

Terminal window
factflow webhook deliveries WEBHOOK_ID
factflow webhook deliveries WEBHOOK_ID --status failed

Each delivery row has: timestamp, response code, response body snippet, retry count, next-retry time, final status (delivered, pending_retry, dead_letter).

Re-arm a dead-lettered or stuck delivery:

Terminal window
factflow webhook retry DELIVERY_ID

Fire a synthetic event at your endpoint to verify the integration:

Terminal window
factflow webhook test WEBHOOK_ID

Synthetic event payload has a stable shape matching the real events but with __test__: true metadata.

Terminal window
factflow webhook toggle WEBHOOK_ID # enable / disable
factflow webhook update WEBHOOK_ID --url ... # partial update
factflow webhook delete WEBHOOK_ID # removes subscription + delivery history

Handler pseudocode:

import hmac, hashlib
def verify(body_bytes: bytes, signature_header: str, secret: str) -> bool:
algo, signature = signature_header.split("=", 1)
assert algo == "sha256"
expected = hmac.new(secret.encode(), body_bytes, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)

Never skip signature verification. Without it, anyone who knows your webhook URL can inject events.