Skip to main content
You have three ways to receive alerts, in increasing operational complexity:
  1. Polling. A scheduled job calls GET /v1/alerts and walks pages until it sees an id you’ve already stored.
  2. Webhook. Register an endpoint via POST /v1/webhooks and let Signa push alerts to you.
  3. Webhook + reconciliation. Push for low latency, then periodically call POST /v1/alerts/lookup with the IDs you’ve persisted to confirm nothing was lost during a receiver outage.
Pure polling is fine for low-volume internal tools. For production external workflows, prefer webhook + reconciliation.

Deduplicate by event ID

Alerts are immutable, but the same alert.created event can arrive multiple times. Use the stable evt_* value in the webhook-id header as your idempotency key; use data.alert_id for the alert resource.
If your side effect cannot share a transaction with your dedup store (for example, a third-party API call), use a two-stage processing -> done state and only flip to done after the side effect returns success.

Polling pattern

The GET /v1/alerts endpoint accepts:

Reconciliation pattern

For defense-in-depth on top of webhooks, run a periodic job that asks Signa “did you ever fire these IDs?”, useful when you suspect a webhook outage.
alerts.lookup() takes an array of 1-100 prefixed alert IDs (alt_*) per call:
The endpoint is organization-scoped. Malformed IDs, anything that is not a well-formed alt_* ID, fail the whole request with 400 validation_error, listing each bad entry by index. Well-formed but unknown IDs, and IDs that belong to another organization, are silently dropped from the result. Any gap between the IDs you sent and the alerts you got back is real.

Reading event.diff

event.diff is why the alert fired, one entry per changed field:
path is a public trademark field name — the same name the field has on GET /v1/trademarks/{id}, so you can always look it up. Changed child collections appear as the collection name (owners, attorneys, classifications, media) and are opaque: op: "changed", no from/to. Parent fields carry real values. Status is flattened — status.primary is status, everything else is status_<key> (status_stage, status_effective_date, …). Fields Signa tracks internally but does not publish are omitted, not renamed, so a diff can be shorter than the underlying change. Branch on path for routing (status to the docket, owners to conflicts review) and read summary for the human line.
Beta break, effective on deploy. path previously carried Signa’s internal column names (status_primary, mark_text_primary, trademark_owners, …). See the changelog for the full rename table. Redeliveries and retries of older alerts are re-emitted with the public names.

Severity-based routing

A typical production setup runs two jobs on different cadences:

Provenance: every alert’s evidence chain

Each alert carries a provenance block so you can trace it from the office publication to the moment Signa detected it, and see whether that detection was on time.
late_detection is a per-office judgement. Offices that publish daily have a tight target; offices that publish weekly or monthly have a proportionally looser one. A false flag means “detected within the window we hold ourselves to for that office”, not “instant”. Use detection_latency_seconds for your own SLA dashboards, and late_detection as a ready-made trigger for exception review when Signa was slower than intended.