Docs
Operate

Webhooks And Secret Refs

Breyta webhook security guide for authenticated ingestion with target-bound secrets.

Goal

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

Quick Answer

Use :invocations for the callable webhook payload, then expose webhook ingress
with :interfaces {:webhook [...]}. Bind secret material through target
bindings.

Canonical Interface Shape

{:requires [{:slot :webhook-signing-secret
             :type :secret
             :label "Webhook Signing Secret"}]
 :invocations {:order-updated
               {:inputs [{:name :order-id
                          :type :text
                          :label "Order ID"
                          :required true}]}}
 :interfaces {:webhook [{:id :order-webhook
                         :invocation :order-updated
                         :enabled true
                         :event-name "orders.updated"
                         :description "Receive order update webhooks."
                         :auth {:type :hmac-sha256
                                :header "Stripe-Signature"
                                :secret-ref :webhook-signing-secret}}]}}

Notes:

  • older event-style webhook definitions are still accepted for existing flows
  • webhook paths are generated by Breyta; inspect the generated interface endpoint
    for the target you are testing instead of hardcoding :path in the flow source
  • the interface details panel shows the generated webhook path and whether the
    referenced secret binding is configured
  • author draft/live endpoints are flow-source scoped:
    /api/flows/{flow-slug}/interfaces/draft/{interface-id}
    and
    /api/flows/{flow-slug}/interfaces/live/{interface-id}
  • installed consumer endpoints are installation scoped:
    /api/flows/{flow-slug}/installations/{installation-id}/interfaces/{interface-id}
  • workspace-scoped interface endpoint forms remain available as alternate
    compatibility URLs when you need an explicit workspace id in the path
  • legacy :config :fields webhook payload validation is still accepted for
    existing flows, but new payload contracts should be expressed as invocation
    inputs

Secret Slot Setup

The :requires secret slot in the canonical shape above declares where the
webhook signing secret is bound.

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

GitHub X-Hub-Signature-256 example:

{:auth {:type :signature
        :algo :hmac-sha256
        :signature-header "X-Hub-Signature-256"
        :signed-message :payload
        :signature-format :hex
        :signature-prefix "sha256="
        :secret-ref :github-webhook-secret}}

GitHub signs the exact raw request body and sends the hex digest with a
sha256= prefix.

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 shapeFlow-source scoped with draft in the path.Flow-source scoped with live in the path for author testing; installed consumer endpoints stay installation scoped.

Guidelines:

  • Do not derive webhook URLs from slug/event-name assumptions.
  • Treat server-returned interface endpoints as authoritative for the target you are testing.
  • For installation/live ingress, inspect interface metadata with
    breyta flows installations interfaces <installation-id>.
  • For keyed concurrency, choose a :key-field that exists in the raw incoming
    webhook payload before the flow body normalizes input. For GitHub pull
    request events, prefer :number or [:pull_request :id] over a later derived
    request key.

Runtime Surface Strategy By Target

Use runtime surfaces differently across targets:

TargetPreferred runtime surface during development/operationsWhy
draftManual interface runs first; optional webhook testing via generated draft interface endpointFast iteration and deterministic debugging.
liveTop-level schedules for unattended jobs, plus installation/live webhook interfacesStable, continuous runtime behavior for consumers.

Draft webhook interface endpoints are workspace-authenticated and source scoped:

  • POST /api/flows/{flow-slug}/interfaces/draft/{interface-id}

Legacy draft-event routes are still accepted for older tooling, but new clients
should use the generated interface endpoint.

Example:

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

Common Pitfalls

  • missing :auth on webhook interface -> 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 interface config or in :requires?

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

Can I keep webhooks unauthenticated in production?

No. Public webhooks should use authenticated interface config.

Related

As of May 11, 2026