Skip to content

Webhooks Design Decisions ​

Design record for inbound webhook support. Feature documentation lives in docs/features/webhooks.md.

Decisions Log ​

#DecisionRationale
1Webhook trigger semantics — webhook calls create_run_from_request()Keeps webhook execution purely event-driven and flow-target based.
2public policy for /hooks/{path_token}HMAC verification is handled in the service layer, not by RBAC middleware. The endpoint must be accessible without JWT.
3Reuse secrets table for HMAC keysWebhookEndpoint.secret_id FK → secrets.id. Leverages existing secret-backend resolution. No new secret storage mechanism.
4enabled: bool instead of status enumMatches NotificationDestination pattern. Binary state (on/off) is sufficient — avoids enum sprawl.
5Store body SHA-256 hash, never raw bodyCompliance-safe. Hash in request_body_hash enables correlation without retention risk.
6Phase 4 outbound webhooks = new NotificationDestination typeReuses subscription model, Temporal dispatch workflow, and delivery tracking. No parallel system.
7Phase 1 rate limiting: in-memoryPragmatic for single-instance deployment. Migrate to Redis in Phase 2+ if horizontal scaling is needed.
8Single-flow targeting (supersedes the earlier webhook_endpoint_targets N:N association)Each endpoint targets exactly one flow via webhook_endpoints.flow_id. Nested child flows (flow.run steps) made webhook-level fanout redundant — a webhook that must start several flows targets one parent flow whose graph runs the children. Migration 039_webhook_single_flow aborts if a live endpoint still links multiple flows.
9Fanout lives in the flow graph, not the webhook layerInbound execution creates exactly one run of the linked flow; WebhookTargetType survives only as response vocabulary (WebhookTriggeredRun.target_type).

Released as open source under the AGPL-3.0-or-later license. Development is sponsored by Rexonix s.r.o.. Contact — [email protected].