Skip to main content
Configure a webhook URL on your account and DotLookup will POST a signed JSON payload to it after every nightly ETL run, containing diffs for the carriers on your watchlist. One webhook URL per account. One delivery per ETL run with all changes batched. Mirrors the existing email-digest behavior; lets programmatic consumers drop polling.
All endpoints below require an API key. See Authentication.

Lifecycle

  1. Configure: PUT /v1/watchlist/webhook with a URL. DotLookup generates a random 32-byte secret and returns it once in the response — store it; it’s used as the HMAC key and is never returned again.
  2. Receive: After every nightly ETL, DotLookup POSTs the event payload to your URL with an X-Dotlookup-Signature header. Verify the HMAC; respond 2xx to acknowledge.
  3. Retry: A non-2xx response triggers retries at 1m, 5m, and 30m. After three failures, the delivery is marked failed and you can either re-trigger via watchlist catch-up or rotate the URL.
  4. Inspect: GET /v1/watchlist/webhook/deliveries returns the recent attempt log for debugging.

Verify the signature

Every delivery includes X-Dotlookup-Signature: sha256=<hex> — the HMAC-SHA256 of the raw request body using your webhook secret as the key. Reject any request whose signature doesn’t match.
The signature is over the raw request body — verify before parsing JSON, since any rewriting (whitespace normalization, key reordering) will break the HMAC.

Event payload

Headers on every delivery: The etl_run_id is the pages step’s etl_runs.id, the same value surfaced on risk-history and the change feed. Receivers can dedupe on (account_id, etl_run_id) if they’re worried about a duplicate delivery from a retry.

Configure the webhook

The secret field is returned only on initial create or after POST /rotate-secret. Updates that don’t rotate the secret never include it, so it can’t accidentally leak into logs or UI state. Lose the secret? Rotate it.

URL validation

v1 blocks only non-http(s) schemes and obvious loopback host literals (localhost, 127.0.0.1, ::1, 0.0.0.0). DNS-resolution-based private-IP filtering is intentionally not enforced — accounts already control their own webhook URL, and operators self-hosting may legitimately point at internal services.

Failure modes

  • Non-2xx response or timeout (10s) — retried at 1m, 5m, 30m. After three failures, the delivery row is marked failed.
  • Slow receiver — does not block other accounts; dispatch is fanned out across a fixed goroutine pool.
  • Process restart during retry window — in-flight retries are lost. Recover via the watchlist changes endpoint to backfill the missing window, or via the email digest which uses a separate idempotency log.