Watch & webhooks

Core·11 min·updated 2026-07-09

Watch turns the register into a stream. Subscribe an entity list or a saved query, and 31 event types arrive a median 4.2 hours after they print in a gazette.

Two kinds of subscription

An ID list watches entities you already hold — your customers, your suppliers, your portfolio. A saved query watches a definition, so the target universe maintains itself: every logistics company in Hamburg with more than 50 employees, whether or not it existed when you wrote the query.

Subscribe a saved query
$ curl -s https://api.spotit.ai/v1/subscriptions \
    -H "Authorization: Bearer $SPOTIT_KEY" \
    -d '{
      "query": {"country":"DE","nace":"49.41","headcount_min":50},
      "events": ["entity.status_changed","entity.insolvency_filed","query.entered"],
      "delivery": {"url":"https://example.com/hooks/spotit"},
      "backfill_days": 90
    }'

query.entered and query.exited fire when an entity starts or stops matching the definition. That is how a sourcing list stops going stale between refreshes.

Verify the signature

Every delivery carries Spotit-Signature: an HMAC-SHA256 of the raw body with your endpoint secret, and a timestamp. Verify before you parse, compare in constant time, and reject anything older than five minutes.

Verification
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(raw: string, header: string, secret: string) {
  const [t, sig] = header.split(",").map((p) => p.split("=")[1]);
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;

  const expected = createHmac("sha256", secret).update(t + "." + raw).digest();
  const given = Buffer.from(sig, "hex");
  return expected.length === given.length && timingSafeEqual(expected, given);
}

Delivery, retries and the dead letter queue

  • Return a 2xx within 5 seconds. Do the work afterwards — acknowledge, then process.
  • Non-2xx or timeout retries at 1 min, 5 min, 30 min, 2 h, 6 h and 24 h with jitter.
  • After the last attempt the event moves to a dead-letter queue and stays there for 30 days. GET /v1/events?status=dead lists it.
  • Events are at-least-once. Deduplicate on event_id; it is stable across retries.
  • You are billed 0.05 credits on successful delivery only. Retries are free, and so are events you never manage to receive.

Replay

Deployed a bad handler on a Friday? POST /v1/subscriptions/{id}/replay re-delivers a window of events, up to 30 days back, at no charge. Replayed events carry replay: true so an idempotent handler can tell them apart in your own logs.

Or poll, if you would rather

GET /v1/events?cursor=… walks the same stream from a cursor you keep. Polling costs nothing, survives a firewall that will not accept inbound webhooks, and is the right choice inside a warehouse job.

Worth knowing

The complete list of 31 event types, with annual volumes, is in the API reference.

Try it against your own data

The Developer plan is free forever and needs no card. 2,500 credits is enough to answer the only question that matters: does it resolve your records.