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.
Register a subscription
Section titled “Register a subscription”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:
factflow webhook create \ --url https://your-service.dnb.no/hooks/factflow \ --events execution.completed,execution.failed \ --secret SHARED_SECRETReturns a webhook_id.
Event types
Section titled “Event types”| Event | Fired when |
|---|---|
execution.started | Execution transitions to running |
execution.completed | Terminal state completed |
execution.failed | Terminal state failed |
execution.cancelled | Terminal state cancelled |
storage.object_created | Any storage write |
lineage.failed | A lineage row transitions to failed |
Multiple events per subscription are supported.
Filters
Section titled “Filters”Optional filtering narrows the subscription:
config_id— only events for a specific pipeline configexecution_id— only for one executionroute_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.
Delivery mechanics
Section titled “Delivery mechanics”- HTTP POST to your URL, JSON body
X-Factflow-Signature: sha256=<hmac>header — HMAC-SHA256 of the body using the sharedsecretX-Factflow-Event: <event_type>headerX-Factflow-Delivery: <delivery_id>header (idempotency key for your handler)
Retry policy
Section titled “Retry policy”If your endpoint responds non-2xx or times out:
- Retry 1: after 1 minute
- Retry 2: after 5 minutes
- Retry 3: after 30 minutes
- Retry 4: after 2 hours
- Retry 5: after 12 hours
- 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.
Inspect deliveries
Section titled “Inspect deliveries”factflow webhook deliveries WEBHOOK_IDfactflow webhook deliveries WEBHOOK_ID --status failedEach delivery row has: timestamp, response code, response body snippet, retry count, next-retry time, final status (delivered, pending_retry, dead_letter).
Manual retry
Section titled “Manual retry”Re-arm a dead-lettered or stuck delivery:
factflow webhook retry DELIVERY_IDTest delivery
Section titled “Test delivery”Fire a synthetic event at your endpoint to verify the integration:
factflow webhook test WEBHOOK_IDSynthetic event payload has a stable shape matching the real events but with __test__: true metadata.
Toggle, update, delete
Section titled “Toggle, update, delete”factflow webhook toggle WEBHOOK_ID # enable / disablefactflow webhook update WEBHOOK_ID --url ... # partial updatefactflow webhook delete WEBHOOK_ID # removes subscription + delivery historyVerifying signatures
Section titled “Verifying signatures”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.
Related
Section titled “Related”- CLI: webhook
- Reference: API — webhooks schemas
- factflow-server reference — webhook subpackage