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_ready or intervals_ready events?
  • How do I know when a customer must re-authenticate (customer_must_reauthenticate)?
  • Why is my Bayou webhook-signature verification failing?
  • How do I dedupe Bayou webhook retries when the payload has no event ID?

Verification (core)

HeaderValue
webhook-idUnique message ID
webhook-timestampUnix seconds when the webhook was sent
webhook-signatureOne 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 to svix-* when webhook-id is 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):

EventobjectFired when
new_billBillA new bill was discovered
updated_billBillA bill was updated
bills_readyCustomer (bills_parsed)The initial bill discovery finished for a customer
intervals_readyCustomer (available_data)The initial interval discovery finished for a customer
customer_has_filled_credentialsCustomerA customer shared valid utility credentials
new_unparsed_billBill (status: unparsed)A bill was discovered but couldn't be parsed yet
customer_must_reauthenticateCustomer (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 2xx quickly.
  • new_unparsed_bill.object.file_url is a short-lived pre-signed URL and customer_must_reauthenticate.object.onboarding_token is a credential. Don't log them.

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_must_reauthenticate with a test customer.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 11, 2026

View on GitHub →