Agent skill
Bayou Energy Webhooks Skill
Receive and verify Bayou Energy (bayou.energy) utility-data webhooks, signed per the Standard Webhooks spec. Use when setting up a Bayou webhook endpoint, getting the company whsec_ signing secret, debugging webhook-id / webhook-timestamp / webhook-signature verification, or handling events like new_bill, updated_bill, bills_ready, intervals_ready, customer_has_filled_credentials, new_unparsed_bill and customer_must_reauthenticate.
Install this skill
npx skills add hookdeck/webhook-skills --skill bayou-energy-webhooks
Bayou Energy is a utility-data API that retrieves customers' utility bills and interval data. Its webhooks report new and updated bills, readiness of the initial bill and interval pulls, and credential status. Every delivery is signed per the Standard Webhooks spec.
When to Use This Skill
- How do I receive Bayou Energy webhooks?
- How do I verify a Bayou webhook signature?
- Where do I find my Bayou webhook signing secret?
- How do I handle
new_bill,bills_readyorintervals_readyevents? - How do I know when a customer must re-authenticate (
customer_)?must_ reauthenticate - Why is my Bayou
webhook-signatureverification failing? - How do I dedupe Bayou webhook retries when the payload has no event ID?
Verification (core)
| Header | Value |
|---|---|
webhook-id | Unique message ID |
webhook-timestamp | Unix seconds when the webhook was sent |
webhook-signature | One or more space-separated v1,<base64 HMAC-SHA256> entries |
Signed content is {webhook-id}.{webhook-timestamp}.{raw request body}. The key is the base64-decoded part of the secret after whsec_. There is one secret per company. The examples use the standardwebhooks library (npm, PyPI), which reads the three headers, tries every v1 entry in constant time and returns the parsed body.
const { Webhook } = require('standardwebhooks');
// rawBody: exact request body string, NOT re-serialised JSON.
// secret: the full "whsec_..." value from Settings in the Bayou dashboard.
function verifyBayouWebhook(rawBody, headers, secret = process.env.BAYOU_ENERGY_WEBHOOK_SECRET) {
return new Webhook(secret).verify(rawBody, {
'webhook-id': headers['webhook-id'],
'webhook-timestamp': headers['webhook-timestamp'],
'webhook-signature': headers['webhook-signature'],
}); // returns { event, object }; throws → respond 400
}
Python: from standardwebhooks.webhooks import Webhook, then Webhook(secret).verify(await request.body(), headers) (catch Exception, since malformed headers raise a plain ValueError).
Timestamp window. The library always enforces a fixed 5-minute window. Hookdeck retries and replays resend the original timestamp, so the library rejects a retry that arrives more than 5 minutes after the first attempt. If you receive through Hookdeck, use the manual verifier in references/verification.md, where the window can be dropped or widened.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/. They read the
webhook-*headers and fall back tosvix-*whenwebhook-idis absent, as Hookdeck does.
Event Envelope
{ "event": "customer_must_reauthenticate",
"object": { "id": 12345, "external_id": "MSCOTT123", "reason": "credentials_are_invalid",
"onboarding_token": "...", "onboarding_link": "https://bayou.energy/companies/12345/sign_up?key=..." } }
event sits beside object on every example. object is the bill or the customer, depending on the event. There is no event ID in the payload, so dedupe on the webhook-id header.
Common Event Types
The docs list seven webhooks (the list may not be exhaustive):
| Event | object | Fired when |
|---|---|---|
new_bill | Bill | A new bill was discovered |
updated_bill | Bill | A bill was updated |
bills_ready | Customer (bills_parsed) | The initial bill discovery finished for a customer |
intervals_ready | Customer (available_data) | The initial interval discovery finished for a customer |
customer_ | Customer | A customer shared valid utility credentials |
new_unparsed_bill | Bill (status: unparsed) | A bill was discovered but couldn't be parsed yet |
customer_ | Customer (reason) | A customer must share credentials again (credentials_are_invalid or mfa_factor_has_expired) |
Full payloads: references/overview.md and Bayou's webhook docs.
Delivery Semantics
- Bayou POSTs JSON to the company's single webhook URL; every webhook goes there.
- Verification is optional on Bayou's side: deliveries are sent whether or not you verify. Verify anyway.
- Bayou doesn't state a retry policy, so make handlers idempotent and respond with a
2xxquickly. new_is a short-lived pre-signed URL andunparsed_ bill.object.file_ url customer_is a credential. Don't log them.must_ reauthenticate.object.onboarding_ token
Environment Variables
BAYOU_ENERGY_WEBHOOK_SECRET=whsec_xxxxx # full value incl. whsec_; handlers fail closed (500) without it
Get it from the Bayou dashboard under Settings, just below the Webhook URL field.
Local Development
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 bayou-energy --path /webhooks/bayou-energy
Paste the printed URL into the Webhook URL field in Settings in the Bayou dashboard. Bayou's fake utility lets you trigger events like customer_ with a test customer.
Reference Materials
- references/overview.md - Event catalog and payload fields
- references/setup.md - Webhook URL, signing secret, testing
- references/verification.md - Library and manual verification, window handling, debugging