Every webhook Bayou sends is signed so you can verify that it genuinely came from Bayou and wasn't tampered with in transit. Bayou follows the Standard Webhooks specification, so you can verify signatures with any Standard Webhooks library or in a few lines of code.
Verification is optionalWebhooks are delivered whether or not you verify them, so nothing about your
integration is blocked on this.
Your signing secret
Each company has its own signing secret, available in the Bayou Dashboard under Settings, just below the Webhook URL field. It looks like this:

Treat the secret like a password: store it in your secret manager, and never commit it to source control.
Signature headers
Every webhook delivery is an HTTP POST with three signature headers:
| Header | Example | Description |
|---|---|---|
webhook-id | wh_12345 | Unique identifier for this webhook message. |
webhook-timestamp | 1750000000 | Unix timestamp (seconds) when the webhook was sent. |
webhook-signature | v1,yW3priuFZD+w+ji8R8... | One or more space-delimited signatures of the request body. |
Verifying with a library (recommended)
Standard Webhooks publishes verification libraries for most languages. Pass the
raw request body — not a re-parsed or re-serialized version of it.
from standardwebhooks import Webhook
# HMAC key from your company settings page
wh = Webhook("whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw")
# body: raw request body as received (str or bytes)
# headers: the incoming request headers
wh.verify(body, headers) # raises WebhookVerificationError if invalidimport { Webhook } from "standardwebhooks";
const wh = new Webhook("whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw");
// payload: raw request body string, headers: incoming request headers
wh.verify(payload, headers); // throws if invalidVerifying manually
If you'd rather not add a dependency, the scheme is:
- Build the signed content:
{webhook-id}.{webhook-timestamp}.{body}, wherebodyis the raw request body bytes. - Get the key: strip the
whsec_prefix from your secret and base64-decode the remainder. - Compute
HMAC-SHA256(key, signed_content)and base64-encode the result. - Compare it against each signature in
webhook-signature. The header value is versioned (v1 <signature>) and may contain several space-delimited signatures during secret rotation — the delivery is authentic if anyv1signature matches. Use a constant-time comparison. - Reject deliveries whose
webhook-timestampis more than 5 minutes from the current time, to protect against replay attacks.
How to test
You can use this example to check your implementation. Given the following webhook details:
- Secret:
whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw webhook-id:wh_12345webhook-timestamp:1750000000- Body:
{"event": "bills_ready", "object": {"id": 123456}}
the expected signature header is:
v1,yW3priuFZD+w+ji8R847LYfQxOKmZlBAHEt7OQP0CMI=

