Skip to content
Ads LighthouseStart free trialStart trial

Integrations · Alerts

Brand search alerts by webhook

Feed alerts into your own tools, ticket queue or data pipeline with a JSON POST request for every event.

7-day free trial. Cancel anytime before it ends.

What it does

  • Alerts for a new advertiser, an advertiser observed again after at least 30 days, a brand mention in ad copy, or one of your affiliate IDs.
  • A scheduled summary, daily or weekly at the hour you choose, even when nothing changed: checks saved, new and returning advertisers, brand mentions, affiliate IDs, policy cases and the top advertisers.
  • A JSON body with the event id, type, project brand, time, a link to the evidence and the observation details.
  • Headers that name the event type and carry a stable delivery id.
  • A signature on every request, made with the rule's own secret, so your endpoint can confirm the request came from Ads Lighthouse unchanged.
  • Up to three attempts, 15 and 60 minutes apart, when your endpoint times out or returns 408, 429 or a 5xx status.

Set it up

  1. Prepare an HTTPS endpoint on your system that accepts POST requests with a JSON body.
  2. In Ads Lighthouse, open Alerts and select Rules.
  3. Choose the project and event type, choose Webhook as the channel and enter your URL.
  4. Select Add rule. An account owner or admin can then open Webhook signing secrets on the same page and copy the rule's secret into your endpoint's settings.
  5. Verify the x-ads-lighthouse-signature header on each request. Store each event id and ignore any id you have already processed.

Verify the signature

Compute the HMAC over the raw request body before you parse it. Compare signatures in constant time, and reject timestamps older than 5 minutes. The rule's secret is under Alerts > Rules > Webhook signing secrets.

Node.js (TypeScript)
import { createHmac, timingSafeEqual } from "node:crypto";

const TOLERANCE_SECONDS = 300;

export function verifyAdsLighthouseSignature(
  secret: string,
  header: string,
  rawBody: string | Buffer,
  nowSeconds = Math.floor(Date.now() / 1000),
): boolean {
  let timestamp = Number.NaN;
  const signatures: string[] = [];
  for (const part of header.split(",")) {
    const [key, value = ""] = part.split("=", 2);
    if (key === "t" && /^\d+$/.test(value)) timestamp = Number(value);
    else if (key === "v1") signatures.push(value);
  }
  if (!Number.isSafeInteger(timestamp) || Math.abs(nowSeconds - timestamp) > TOLERANCE_SECONDS)
    return false;
  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest();
  return signatures.some((signature) => {
    const received = Buffer.from(signature, "hex");
    return received.length === expected.length && timingSafeEqual(received, expected);
  });
}
Python
import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300


def verify_signature(secret: str, header: str, raw_body: bytes) -> bool:
    timestamp = None
    signatures = []
    for part in header.split(","):
        key, _, value = part.partition("=")
        if key == "t" and value.isdigit():
            timestamp = int(value)
        elif key == "v1":
            signatures.append(value)
    if timestamp is None or abs(time.time() - timestamp) > TOLERANCE_SECONDS:
        return False
    signed = str(timestamp).encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(expected, signature) for signature in signatures)

Limits to know

  • Delivery is at least once: a retried request repeats the same event id, so your endpoint should deduplicate.
  • The URL must use HTTPS and resolve to a public address. Private and local network addresses are rejected.
  • Other refusals from your endpoint, such as 400 or 403, are not retried.
  • A new secret applies from the next delivery, with no overlap. Update your endpoint right after you rotate it: a request it rejects with a 4xx status is not sent again.
  • Payloads contain only a fixed set of observation fields, never internal identifiers or raw collection data.

Help articles

Questions

What does the webhook body contain?

An id, the event type, the brand, the time it fired, a link to the evidence and a data object with fields such as domain, query, url, engine, device, country, location and observation time, plus fields specific to the event type.

How do I avoid processing the same alert twice?

Use the id in the body, which is also sent in the x-ads-lighthouse-delivery header. It stays the same across retries, so keep the ids you have processed and skip repeats.

How is a webhook signed?

Each request carries x-ads-lighthouse-signature: t=<Unix time in seconds>,v1=<signature>. The signature is the hex HMAC-SHA256 of the timestamp, a period and the raw body, keyed with the rule's signing secret. A retry is signed again with a new timestamp and keeps the same delivery id.

Where can I see failed deliveries?

The Alerts page lists every event with its delivery status, number of attempts and last error, so you can fix the destination and check the next delivery.

Put your brand searches on autopilot

Choose your keywords, markets and devices, and let alerts tell the right people when something changes.

Your privacy choices

We use storage needed for sign-in and your workspace. With your permission, optional analytics and marketing tags can help us understand visits and measure campaigns. Read our Cookies & browser storage notice.