Skip to content
Ads Lighthouse

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 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 a secret that belongs to the rule, so your endpoint can check that the request came from Ads Lighthouse and was not changed.
  • Up to three attempts, 15 and 60 minutes apart, when your endpoint times out or answers 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. Account owners and administrators can then open Webhook signing secrets on the same page and copy the rule's secret into your endpoint's configuration.
  5. Check the x-ads-lighthouse-signature header of each request, then store the event id you receive and ignore any id you have already processed.

Verify the signature

Compute the HMAC over the raw request body before parsing it, compare in constant time and refuse timestamps older than 5 minutes. Find the rule's secret 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 refused.
  • Other refusals from your endpoint, such as 400 or 403, are not retried.
  • Rotating a secret takes effect on the next delivery, with no overlap. Update your endpoint right after rotating: a request it refuses with a 4xx status is not sent again.
  • Payloads contain only allowlisted observation fields, never internal identifiers or raw collection responses.

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?

Alerts lists every event with its delivery status, number of attempts and the 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.