Agent skill
Browserbase Webhooks Skill
Receive and verify Browserbase webhooks (headless browser platform). Use when setting up a Browserbase webhook endpoint, storing the whsec_ signing secret (shown only on create or rotate), debugging Standard Webhooks signature verification (webhook-id, webhook-timestamp, webhook-signature), rotating secrets, or handling Browserbase Functions events like functions.invocations.completed, functions.invocations.failed, functions.builds.completed and functions.builds.failed.
Install this skill
npx skills add hookdeck/webhook-skills --skill browserbase-webhooks
Browserbase is a headless browser platform. Its webhooks POST a signed event to your HTTPS endpoint when a Browserbase Function invocation or build changes state, so you don't have to poll GET /v1/functions/invocations/{id}. Deliveries follow the Standard Webhooks spec.
When to Use This Skill
- How do I receive Browserbase webhooks?
- How do I verify a Browserbase webhook signature?
- Where do I find my Browserbase webhook signing secret? (Only in the create or rotate response.)
- How do I handle
functions.invocations.completedandfunctions.invocations.failed? - Why is my Browserbase
webhook-signatureverification failing? - How do I rotate a Browserbase webhook secret without dropping deliveries?
Verification (core)
| Header | Value |
|---|---|
webhook-id | Unique message identifier |
webhook-timestamp | Unix seconds when the delivery was sent |
webhook-signature | v1,<base64 HMAC-SHA256>, possibly several space-separated entries |
Signed content is {webhook-id}.{webhook-timestamp}.{raw body}. The HMAC key is the base64-decoded part of the whsec_ secret. Browserbase's docs say to use a Standard Webhooks library (standardwebhooks on npm and PyPI) instead of hand-rolling the HMAC. It handles the constant-time comparison and a 5-minute timestamp tolerance.
const { Webhook } = require('standardwebhooks');
// Current secret first; after a rotation the old one keeps verifying for 24h.
const secrets = [process.env.BROWSERBASE_WEBHOOK_SECRET, process.env.BROWSERBASE_WEBHOOK_SECRET_PREVIOUS].filter(Boolean);
if (secrets.length === 0) throw new Error('BROWSERBASE_WEBHOOK_SECRET not set'); // fail closed → 500
function verifyBrowserbaseWebhook(rawBody, headers) { // rawBody: exact bytes, NOT re-serialised JSON
let lastError;
for (const secret of secrets) {
try {
return new Webhook(secret).verify(rawBody, headers); // strips whsec_, base64-decodes key
} catch (err) { lastError = err; }
}
throw lastError; // → 400 Invalid signature
}
Python: from standardwebhooks.webhooks import Webhook, then Webhook(secret).verify(await request.body(), headers).
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Envelope
{
"id": "85e6ff2d-91de-468a-867c-e0d9e4587063",
"type": "functions.invocations.completed",
"resourceId": "a3f1c2d4-7b90-4e51-9c8a-2f6d1e0b4c73",
"data": { }
}
id identifies the event and stays the same across retries, so use it as your deduplication key. type is the event type, resourceId is the invocation or build the event concerns, and data holds type-specific fields. Browserbase may add envelope keys but won't rename or remove them, so ignore keys you don't recognize.
Common Event Types
| Event | Fires when |
|---|---|
functions.invocations.pending | Browserbase accepts and queues an invocation |
functions.invocations.running | An invocation starts executing |
functions.invocations.completed | An invocation finishes successfully |
functions.invocations.failed | An invocation fails |
functions.builds.running | A build (publish of your Function code) starts |
functions.builds.completed | A build finishes successfully |
functions.builds.failed | A build fails |
An endpoint receives only the types it subscribes to. Browserbase rejects a subscription to an unknown type when you register the endpoint. See references/overview.md.
Delivery Semantics
- Return any
2xxwithin 15 seconds. The response body is ignored. A3xxredirect counts as a failure, so register the final URL. - Retries run immediately, then after 5s, 5m, 30m, 2h, 5h, 10h and 10h (eight attempts in total). An endpoint that fails continuously for five days is disabled.
- The same event can arrive more than once. Dedupe on the envelope
id. - Events aren't ordered: a
completedevent can arrive beforerunningfor the same invocation. Branch ontype, not arrival order.
Environment Variables
BROWSERBASE_WEBHOOK_SECRET=whsec_... # required; handlers fail closed without it
BROWSERBASE_WEBHOOK_SECRET_PREVIOUS= # optional, during the 24h rotation window
Local Development
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 browserbase --path /webhooks/browserbase
Register the printed https:// URL as the webhook endpoint (Browserbase rejects http://), then invoke a Function to trigger an event.
Reference Materials
- references/overview.md - Event types, envelope, delivery and retry behaviour
- references/setup.md - Creating the endpoint (dashboard, SDK, API), storing and rotating the secret
- references/verification.md - SDK and manual verification, gotchas, debugging