Gareth Wilson Gareth Wilson

Guide to Rebrandly Webhooks: Features and Best Practices

Published


Rebrandly webhooks stream click data from your branded links into your own systems. Every time someone clicks a short link or scans a Rebrandly QR code, Rebrandly can post the click, with its time, device, location, referrer and destination, to your endpoint, so you can feed a warehouse, update a CRM or trigger a follow-up while the visitor is still engaged.

This guide covers Rebrandly's two webhook products, what a click payload contains, how to authenticate requests with a secret header, how delivery works without retries, and the practices that keep a Rebrandly receiver from losing clicks.

What are Rebrandly webhooks?

Rebrandly has two separate features that send click webhooks:

  • Clickstream webhook delivery is part of Clickstream, Rebrandly's real-time click event stream. It is provisioned by Rebrandly: you give them an endpoint URL, an HTTP method (POST or PUT) and an authentication header key and value, and Rebrandly confirms when the integration is active. Clickstream can also deliver to an AWS S3 bucket instead.
  • Webhooks is a self-serve feature in the Rebrandly app. You create a webhook with a name, URL, secret key and workspace, and use Send test event to check it. Rebrandly's help centre puts it under Analytics > Webhooks, and Rebrandly's blog under Account Settings > Webhooks. The number of webhooks depends on your plan: one on Professional, five on Growth and up to 100 on Enterprise.

Rebrandly's developer documentation covers Clickstream. The self-serve feature is described in a help article and a technical blog post rather than in the developer docs.

Neither product signs requests. Each sends a secret header, and your endpoint compares it with the value you stored.

Rebrandly webhook features

FeatureClickstreamSelf-serve Webhooks
SetupProvisioned by Rebrandly on requestIn the Rebrandly app
MethodPOST or PUT, chosen at setupPOST
EventsClicksClicks, including QR code scans
AuthenticationThe header key and value you give RebrandlyA secret key. The blog names the header both X-Rebrandly-Secret and apikey
SignatureNoneNone
ScopeYour Clickstream accountOne workspace per webhook
RetriesNot documentedNone. "Each event is sent once"
Response limitNot documentedThe blog says 5 seconds in one place and 30 seconds in another
Source IPsNot publishedNot published

What the payload contains

Only click events exist, and nothing in the request names the event type. The body is an object with a data array of clicks and a key. Here is Rebrandly's Clickstream example, trimmed:

{
  "data": [
    {
      "timestamp": "2019-07-02T14:07:06.981094Z",
      "client": {
        "language": "pt",
        "location": { "country": "br" },
        "agent": {
          "browser": { "name": "facebook-app", "version": "227.0.0.43.158" },
          "os": { "name": "android", "version": "android-9.0-pie" },
          "device": { "name": "smartphone" },
          "type": "human"
        },
        "session": { "started": "2019-07-02T14:07:06.981094Z", "first": true }
      },
      "referral": {
        "hostname": "m.facebook.com",
        "protocol": "http",
        "path": "/",
        "origin": { "type": "social", "name": "facebook" }
      },
      "route": {
        "id": "a1ec86bf91bfe763ad02f602259ea0a9",
        "slashtag": "",
        "domain": { "id": "9a5ae320f3e247ef8c9b6061131bb517", "raw": "" },
        "destination": {
          "raw": "https://www.yourdestination.com/?utm_source=cs_facebook_post&utm_medium=customersuccess",
          "hostname": "www.yourdestination.com"
        }
      }
    }
  ],
  "key": "abcxyz123"
}

data is an array, and Rebrandly's blog says clicks may be batched depending on volume, so always loop over it. route ties the click to a link: slashtag is the back-half of the short link and destination.raw is the full destination with its query string, which is where your UTM parameters are. client.agent.type separates human clicks from bots.

The self-serve examples are a little different: they show client.geo with country, region and city, and a route.createdAt, and the key identifies the webhook configuration, which helps when several workspaces send to one endpoint. Parse both shapes defensively.

Inspect Rebrandly webhooks as they arrive. Point a self-serve webhook at a Hookdeck Inspect URL and click Send test event to see the exact headers and body, with no account or setup required.

Setting up Rebrandly webhooks

Generate a long random value for the secret, for example with openssl rand -hex 32.

For Clickstream:

  1. Make sure Clickstream is enabled on your account.
  2. Choose a header name, such as Authorization, and the value, such as Bearer <your random value>.
  3. Send Rebrandly the endpoint URL, the method (POST or PUT) and the header key and value.
  4. Wait for Rebrandly to confirm the integration is active.

For self-serve Webhooks:

  1. In the Rebrandly app, open Webhooks and create a webhook.
  2. Enter a name and your endpoint URL.
  3. Paste or copy the secret key, depending on what the form offers, and store it on your side.
  4. Choose the workspace whose links should send clicks.
  5. Create the webhook, then use Send test event from its menu.

Because Rebrandly's own material names the self-serve header two ways, check which header the test event actually carries before you finalise your receiver.

With Hookdeck. Rebrandly webhooks are not signed. Add an authentication header to the webhook, then create a Hookdeck source with the Rebrandly type and enter the header name in API Key Header and the exact value in API Key. Hookdeck finds the header case-insensitively and compares the whole value, so for Authorization: Bearer abc123 enter Bearer abc123. Use the source URL as the webhook URL. For a self-serve webhook, send a test event first and read the header name from the stored request in Hookdeck. The source pre-selects POST but doesn't manage the method, so if your Clickstream integration uses PUT, allow PUT on the source as well.

Securing Rebrandly webhooks

Read the header you configured, compare its whole value with the stored one in constant time, and only then parse the body. Accept PUT as well as POST if your Clickstream integration uses it.

const crypto = require('crypto');
const express = require('express');

const HEADER = (process.env.REBRANDLY_WEBHOOK_HEADER || 'Authorization').toLowerCase();
const SECRET = process.env.REBRANDLY_WEBHOOK_SECRET; // the exact, whole header value

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function handleRebrandly(req, res) {
  const received = req.get(HEADER);
  if (!SECRET || typeof received !== 'string' || !safeEqual(received, SECRET)) {
    return res.sendStatus(401);
  }

  let payload;
  try {
    payload = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Store every click in payload.data, then respond. There are no retries.
  res.sendStatus(200);
}

const app = express();
const raw = express.raw({ type: '*/*' });
app.post('/webhooks/rebrandly', raw, handleRebrandly);
app.put('/webhooks/rebrandly', raw, handleRebrandly);

The same check in Python:

import hmac
import os

HEADER = os.environ.get("REBRANDLY_WEBHOOK_HEADER", "Authorization")
SECRET = os.environ.get("REBRANDLY_WEBHOOK_SECRET", "")


def verify_rebrandly_request(headers) -> bool:
    if not SECRET:
        return False  # fail closed when unconfigured
    received = headers.get(HEADER)  # Starlette and Flask headers are case-insensitive
    if not received:
        return False
    # Compare bytes: compare_digest raises TypeError on a non-ASCII str.
    return hmac.compare_digest(received.encode("utf-8"), SECRET.encode("utf-8"))

A static header authenticates the sender, not the body. It can't detect a modified payload, and with no signed timestamp a captured request can be replayed and still pass. Use HTTPS and a long random value, and expect to update your receiver if the secret is rotated in Rebrandly.

Make Rebrandly webhooks production-ready. Hookdeck Event Gateway checks your secret header, stores every click the moment it arrives, and delivers it to your services with retries, which Rebrandly's self-serve webhooks don't have.

Rebrandly webhook limitations and pain points

No retries

The Problem: Clicks that arrive during a deploy or an outage are gone.

Why It Happens: For the self-serve feature, Rebrandly's blog is explicit: "We don't retry failed deliveries. Each event is sent once. If your endpoint is down or returns an error, that event is gone."

Workarounds:

  • Keep the receiver highly available, and do nothing in the request but store the clicks.
  • Cross-check against Rebrandly's own analytics for critical reporting. The blog notes that older clicks are kept for 30 days.

How Hookdeck Can Help: Hookdeck accepts and stores each delivery as soon as it arrives, so your own downtime no longer loses clicks. Hookdeck then delivers to your service with retries on your schedule and lets you replay anything that failed.

Unclear response limit

The Problem: You don't know how long your handler can take.

Why It Happens: The same blog post tells you to "Respond within 5 seconds" and also says a request times out if you take "longer than 30 seconds".

Workarounds:

  • Design for the shorter limit: store the clicks and return 200 immediately.

How Hookdeck Can Help: Hookdeck answers Rebrandly straight away, so your service's processing time no longer matters to Rebrandly.

Which header?

The Problem: Your receiver checks the wrong header, and every request fails.

Why It Happens: For Clickstream you choose the header. For self-serve webhooks, Rebrandly's blog shows X-Rebrandly-Secret: your_secret_key in its request headers and also says "We send this in the apikey header", and its code samples read apikey.

Workarounds:

  • Send a test event and look at the headers that arrive.
  • Make the header name configuration, not code.

How Hookdeck Can Help: Hookdeck stores every request with its headers, so the first test event shows exactly what Rebrandly sends. Hookdeck's Rebrandly source compares the header you name with the exact value you enter, answers Rebrandly with a 200 either way, and records a request without the right header as failed verification, so it never reaches your destination.

Bursts during campaigns

The Problem: A big campaign floods your endpoint.

Why It Happens: Rebrandly sets no rate limit on events. "Your endpoint receives events as fast as people click your links."

Workarounds:

  • Queue clicks and process them asynchronously.
  • Scale the receiver for your largest campaigns, not your average day.

How Hookdeck Can Help: Hookdeck absorbs the burst and delivers to your service at a max delivery rate you set, so a campaign spike doesn't overload your database.

Best practices

Loop over data

Treat every request as a batch, even when it holds one click.

Order by timestamp

Events may arrive out of order. Sort or bucket by each click's timestamp, not by arrival time.

Deduplicate defensively

Clicks carry no documented ID. If you replay requests, build a key from key, the click's timestamp and route.id. See our guide to webhook idempotency.

Filter bots

Use client.agent.type to keep bot traffic out of your conversion metrics.

Acknowledge fast

Store and return 200. Enrichment, CRM updates and warehouse loads belong in a background job. See why to process webhooks asynchronously.

Conclusion

Rebrandly sends batches of click events through Clickstream or its self-serve Webhooks feature, authenticated by a secret header rather than a signature, and Rebrandly says self-serve deliveries that fail are not retried. Confirm which header your webhook sends, compare its whole value, loop over data, order by timestamp, and store clicks before doing anything else.

Hookdeck Event Gateway sits in front of your receiver, checks the header and stores every click as it arrives, so an outage on your side delays your click data instead of losing it.

Get started with Hookdeck for free and handle Rebrandly webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.