Operate

Webhooks And Secret Refs

Breyta webhook security guide for authenticated ingestion with profile-managed secrets.

Goal

Run webhook-triggered flows safely with explicit auth config, secret refs, and predictable payload constraints.

Quick Answer

Use an event trigger with :config {:source :webhook ... :auth ...} and bind secrets through flow profile bindings.

Canonical Trigger Shape

{:triggers [{:type :event
             :label "Order webhook"
             :enabled true
             :config {:source :webhook
                      :event-name "orders.updated"
                      :path "/hooks/orders"
                      :auth {:type :hmac-sha256
                             :header "Stripe-Signature"
                             :secret-ref :webhook-signing-secret}}}]}

Notes:

  • flows-api also accepts :type :webhook and normalizes it
  • canonical stored trigger form is :type :event + :source :webhook

Secret Slot Setup

{:requires [{:slot :webhook-signing-secret
             :type :secret
             :label "Webhook Signing Secret"}]}

Bind secret values through flow configuration; never hardcode secret strings in flow files.

Auth Modes

  • :hmac-sha256 for providers like Stripe/GitHub
  • :signature for generic signed payload schemes (HMAC/ECDSA)
  • :bearer and :api-key for token/header auth
  • :none should be avoided on public webhook endpoints

Payload Limits That Matter

  • webhook total payload max: 50 MB
  • signed multipart webhook payload max: 5 MB
  • raw MIME field payload max: 50 MB

Rotation And Operations

  • rotate by updating bound secret values, then rerun with the target (flows run --target live when validating live behavior)
  • if replay detection trips, ensure providers send unique signatures/event ids
  • preserve raw request body for signature verification paths

Draft Vs Live Webhook Behavior

Webhook behavior follows runtime target selection:

Aspectdraftlive
Intended usageDev/staging-style iteration while authoring.Stable installed runtime ingress for consumers.
Definition/config sourceLatest pushed working copy + draft target configuration.Installed released version + live/installation configuration.
Endpoint stability expectationCan change frequently during iteration.Treated as stable runtime surface between controlled release/promote events.
Endpoint shapeNot guaranteed to match live endpoint values.Not guaranteed to match draft endpoint values.

Guidelines:

  • Do not derive webhook URLs from slug/event-name assumptions.
  • Treat server-returned trigger endpoints as authoritative for the target you are testing.
  • For installation/live ingress, inspect trigger endpoints with breyta flows installations triggers <installation-id>.

Trigger Strategy By Target

Use trigger types differently across targets:

TargetPreferred trigger during development/operationsWhy
draft:manual runs first; optional webhook testing via draft-event routeFast iteration and deterministic debugging.
live:schedule (for unattended jobs) and installation/live webhooksStable, continuous runtime behavior for consumers.

Draft webhook test route (workspace-auth):

  • POST /:workspace-id/api/events/draft/*event-path

Example (when trigger path is /hooks/orders):

curl -X POST "$BREYTA_API_URL/$BREYTA_WORKSPACE/api/events/draft/hooks/orders" \
  -H "Authorization: Bearer $BREYTA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"orderId":"ord_123"}'

Common Pitfalls

  • missing :auth on webhook trigger -> auth error at runtime
  • :auth present but missing :type -> validation error
  • signature auth without raw body preservation -> verification fails

Frequently Asked Questions

Should webhook auth live in trigger config or in :requires?

Both. Trigger config declares auth behavior; :requires declares where secret material is bound.

Can I keep webhooks unauthenticated in production?

No. Public webhooks should use authenticated trigger config.

Related

As of Feb 17, 2026