Agent skill
RudderStack Webhooks Skill
Receive and authenticate RudderStack HTTP Webhook destination calls. Use when setting up a RudderStack webhook endpoint, verifying the Basic Auth Authorization header, debugging 401s, or handling forwarded track, identify, page, screen, group and alias events.
Install this skill
npx skills add hookdeck/webhook-skills --skill rudderstack-webhooks
RudderStack's HTTP Webhook destination forwards events (track, identify, page, screen, group, alias) to your endpoint. Webhooks are not signed. With Basic Auth selected, every request carries Authorization: Basic base64(username:password) and your endpoint authenticates the request by comparing those credentials with the ones you stored.
A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS and rotate the credential if it leaks.
When to Use This Skill
- How do I receive RudderStack webhooks?
- How do I verify the HTTP Webhook destination's Basic Auth header?
- Does RudderStack sign webhook payloads? (No: use the shared Basic credential)
- How do I handle
trackandidentifyevents from RudderStack? - Why does my RudderStack webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
const safeEqual = (a, b) => crypto.timingSafeEqual(
crypto.createHash('sha256').update(a, 'utf8').digest(),
crypto.createHash('sha256').update(b, 'utf8').digest());
function verifyRudderstackRequest(headers, user, pass) {
const m = /^basic\s+([A-Za-z0-9+/]+={0,2})\s*$/i.exec(headers['authorization'] || '');
if (!m || !user || !pass) return false;
const decoded = Buffer.from(m[1], 'base64').toString('utf8');
const i = decoded.indexOf(':'); // split on the first colon
if (i === -1) return false;
const userOk = safeEqual(decoded.slice(0, i), user); // evaluate both,
const passOk = safeEqual(decoded.slice(i + 1), pass); // no short-circuit
return userOk && passOk;
}
- Header:
Authorization: Basic base64(username:password); the scheme is case-insensitive. - Python:
hmac.compare_digeston bytes (encode both sides;strraises on non-ASCII). - Return
401on a missing, malformed or mismatched header,400for an unparseable body,200once handled. RudderStack aborts a4XXwith no retries and retries5XXand429for 3 hours, so return503for a temporary problem. - The method is user-chosen: accept
POST,PUT,PATCH,GETandDELETE, and accept an empty body. - Read the body raw (
express.raw({ type: '*/*' })) and authenticate before parsing it. - Bearer Token and API Key auth types are not covered.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
With "Send the event payload as is" (the default) the body is the RudderStack event and type says which kind; for track, event holds the event name. Batching sends an array of events; custom mappings can emit any shape.
type | Sent When |
|---|---|
track | A user performs an action (event, properties) |
identify | A user is identified (traits) |
page | A web page is viewed |
screen | A mobile screen is viewed |
group | A user is associated with a group or account |
alias | Two user identities are merged |
Environment Variables
RUDDERSTACK_WEBHOOK_USERNAME=your-username # Destination > Authentication > Basic Auth
RUDDERSTACK_WEBHOOK_PASSWORD=your-password
Local Development
RudderStack can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 rudderstack --path /webhooks/rudderstack
Use the printed URL as the destination's Base URL.
Resources
- references/overview.md - What RudderStack webhooks are, event types and payload shapes
- references/setup.md - Add the HTTP Webhook destination with Basic Auth
- references/verification.md - Basic auth check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests