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
- Send a controlled first delivery.
- Design stable delivery IDs and retry behavior.
- Rotate credentials using Rotation and revocation.