Browse documentation
Concept Webhook ingestion

Webhook authentication and signatures

Combine the trigger URL, selected service-account bearer token, and optional API-key or raw-body HMAC verification safely.

View as Markdown

Every production delivery has two mandatory credentials: the secret trigger token in the URL and a bearer token issued for the exact service account selected on that trigger. An optional inbound connector can add a third check.

Mandatory service-account authentication

The selected service account needs the webhooks scope and an active run-as member in the trigger's organization. Exchange its client credentials and send:

Authorization: Bearer <short-lived token>

Using a different same-organization account is rejected with 401. An expired or invalid token is also rejected. Deactivating the workflow immediately makes the trigger stop resolving and returns 404; rotating a service-account secret prevents future exchanges but does not revoke an already minted token before its expiry.

Optional API-key verification

Attach an inbound connector using API key and configure a dedicated header, for example x-provider-key. Send it in addition to the bearer token:

curl --request POST \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--header "x-provider-key: $PROVIDER_KEY" \
--data-binary @delivery.json \
"$HODOFLOW_BASE_URL/api/v1/webhooks/$TRIGGER_TOKEN"

The configured value is compared in constant time. A missing or empty secret fails closed.

Optional HMAC-SHA256 verification

For an HMAC connector, compute lowercase hexadecimal HMAC-SHA256 over the exact raw request bytes. The default signature header is x-signature, but the connector can configure another name.

SIGNATURE="$(openssl dgst -sha256 -hmac "$WEBHOOK_SIGNING_SECRET" -hex delivery.json | awk '{print $2}')"
curl --request POST \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--header "x-signature: $SIGNATURE" \
--data-binary @delivery.json \
"$HODOFLOW_BASE_URL/api/v1/webhooks/$TRIGGER_TOKEN"

Do not parse and reserialize the JSON between signing and sending: whitespace, key order, and the final newline change the bytes and therefore the digest. --data-binary preserves the file bytes. Compute the signature again only when the body changes; an unchanged retry uses the same signature and delivery ID.

Unsupported connector modes

Basic and Bearer connector authentication cannot be used by webhook triggers. Both need the Authorization header already reserved for the mandatory service-account token. A legacy trigger configured that way fails with 422 and {"error":"Unsupported trigger authentication"}; update the connector to API key or HMAC before retrying.

All authentication failures intentionally use a small response surface:

{"error":"Unauthorized"}

The response does not reveal whether the service account, connector value, or signature was wrong. Use the request ID and your own redacted delivery logs to correlate attempts; never log the trigger token, bearer token, API key, signing secret, or full Authorization header.

Next steps

Related documentation