Agent skill
Rebrandly Webhooks Skill
Receive and authenticate Rebrandly webhooks. Use when setting up a Rebrandly Clickstream or account webhook endpoint, checking the authentication header, debugging 401s, or handling link click events.
Install this skill
npx skills add hookdeck/webhook-skills --skill rebrandly-webhooks
Rebrandly (branded short links) sends a JSON webhook for link clicks. There are two products: enterprise Clickstream webhooks, where you give Rebrandly an endpoint URL, an HTTP method (POST or PUT) and an authentication header key and value; and the self-serve Webhooks feature in account settings.
Rebrandly webhooks are not signed. Add an authentication header to the webhook and enter it here.
Requests are unsigned. You authenticate the sender with a secret header you add yourself.
When to Use This Skill
- How do I receive Rebrandly webhooks?
- How do I authenticate Rebrandly Clickstream requests?
- Does Rebrandly sign webhook payloads? (No: use a shared header)
- How do I handle Rebrandly click events (
data[]batches)? - Why does my Rebrandly webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
function safeEqual(a, b) {
const ba = Buffer.from(String(a), 'utf8');
const bb = Buffer.from(String(b), 'utf8');
return ba.length === bb.length && crypto.timingSafeEqual(ba, bb);
}
function verifyRebrandlyRequest(headers, secret = process.env.REBRANDLY_WEBHOOK_SECRET,
headerName = process.env.REBRANDLY_WEBHOOK_HEADER || 'Authorization') {
if (!secret) return false; // never accept when unconfigured
const received = headers[headerName.toLowerCase()];
return typeof received === 'string' && received.length > 0 && safeEqual(received, secret);
}
- The header name is yours: set
REBRANDLY_WEBHOOK_HEADER(defaultAuthorization), look it up case-insensitively, and compare the whole value withREBRANDLY_WEBHOOK_SECRET. Do not strip or parse aBearerprefix; if you typed one at Rebrandly, it is part of the secret. - Rebrandly's own blog names the self-serve header inconsistently (
X-Rebrandly-Secretandapikey). Capture one real delivery and setREBRANDLY_WEBHOOK_HEADERto the header that arrives. - Missing header or mismatch:
401. Check it first, for every HTTP method. - Python:
hmac.compare_digeston bytes (encode both sides;strraises on non-ASCII). - Read the body raw (
express.raw({ type: '*/*' })) and parse it only after the check. Handle an empty body. - Security limits: 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.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
HTTP Methods
Clickstream uses the POST or PUT method you give Rebrandly at setup, so the examples accept GET, POST, PUT, PATCH and DELETE on /webhooks/rebrandly. Hookdeck's Rebrandly source pre-selects POST without managing the method, so any other method the sender uses must also be allowed on the source.
Event Types
Only click events exist; there is no event-type field. The body is { "data": [ click, ... ], "key": "..." }. Loop over data[].
| Event | Sent When |
|---|---|
click | A short link is clicked (one entry per click in data[]) |
Each click has timestamp, client (language, location, agent, session), referral and route (link id, slashtag, creator, domain, destination). Each event is sent once; there are no retries.
Environment Variables
REBRANDLY_WEBHOOK_HEADER=Authorization # header name you gave Rebrandly
REBRANDLY_WEBHOOK_SECRET=your-shared-secret # exact, whole header value
Local Development
Rebrandly can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 rebrandly --path /webhooks/rebrandly
Resources
- references/overview.md - Rebrandly webhooks, click payload shape
- references/setup.md - Configure the authentication header and endpoint
- references/verification.md - Header check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests