Creating, listing, and managing watches requires the
portfolios:manage scope on your API key. The monitoring API is in beta: see Known beta limitations.Pick a watch type
| You want to… | watch_type | Required field |
|---|---|---|
| Track one specific mark you own (renewals, status drift). | mark | filters.trademarkIds (one ID) |
| Watch an entire portfolio of marks at once. | portfolio | filters.trademarkIds (1 or more IDs) |
| Track a competitor by owner. | owner | filters.ownerId |
| Watch new filings in a Nice class (or class set), optionally by jurisdiction. | class | filters.niceClasses |
| Detect confusingly-similar new filings. | similarity | q (text) |
query body uses the same vocabulary as trademark search, with one casing difference: REST search query params are snake_case (nice_classes=9), while watch-DSL filter keys are camelCase (niceClasses: [9]). If you can build the search, you can save it as a watch — just translate the key casing.
The query DSL
This is the canonical accepted shape — it is validated strictly, and anything outside it returns400:
version (required)
Always "v1". Omitting it returns 400.
q keyword constraints
- Whitespace-separated. Up to 20 keywords. Each keyword must be at least 3 characters.
- Stop words are rejected with
400to prevent watches that match too broadly. The current list isthe, and, or, not, for, a, an, of, in, on, to, is.
filters — the full key list
Filter keys are camelCase. Unknown keys — including snake_case typos like nice_classes — are rejected with 400 (previously they were stored and silently ignored, making the watch broader than intended; the error message suggests the camelCase spelling when it recognizes the typo).
Allowed keys:
applicationNumber, attorneyFirmName, attorneyId, challengeStates,
expiryDate, filingDate, filingRoute, firmId, goodsServicesText,
hasMedia, hasProceedings, irNumber, isMadrid, isRetracted,
isSeriesMark, jurisdictions, markFeatureType, markLegalCategory,
niceClasses, office, offices, originOfficeCode, ownerCountry,
ownerHasLei, ownerId, ownerLei, ownerName, ownerPubliclyTraded,
ownerTicker, publicationDate, registrationDate, registrationNumber,
renewalDueDate, rightKind, scopeKind, statusPrimary, statusReason,
statusStage, terminationDate, trademarkIds, updatedAt, viennaCodes.
Notes:
- ID filters accept both forms.
trademarkIds,ownerId,attorneyId, andfirmIdaccept prefixed IDs (tm_*,own_*,att_*,firm_*) or raw UUIDs. Wrong-type or malformed IDs return400with the offending index in the field path. - Office codes are case-insensitive —
filters.offices: ["USPTO"]is accepted and stored lowercase (["uspto"]). Each code must be a 2–10 character string that exists inGET /v1/offices. filters.jurisdictions(e.g.["US", "EU"]) is auto-translated tofilters.officesat create time, so either spelling of scope works.
trigger_events
Any non-empty subset of these five values (anything else returns 400; an empty array is also rejected — omit the field to subscribe to the default set):
Default set (applied when trigger_events is omitted):
trademark.createdtrademark.updatedtrademark.status_changed
trademark.retractedtrademark.corrected
trademark.retracted / trademark.corrected are about the source feed, not legal status. A trademark.retracted alert fires on a pure is_retracted false → true flip — the upstream office feed pulled or retracted the record. trademark.corrected fires on the reverse true → false flip, when a previously-retracted record reappears (a source-side correction). These are distinct from a legal cancellation, which surfaces as trademark.status_changed (see the cancellation note below). Both are opt-in: a watch that omits trigger_events, or that lists only the default three, will never receive them — existing watches are unaffected.Cancellation monitoring is built in. Whenever a matching mark’s status changes — including when it is cancelled, withdrawn, abandoned, or otherwise goes dead — the watch fires a
trademark.status_changed alert. A cancellation is a status transition (the mark’s stage moves to cancelled and its primary status to inactive), so any mark, owner, portfolio, or class watch already covers it with no special setup. Subscribe to trademark.status_changed (or leave trigger_events at the default) to be alerted the moment a blocking mark is cancelled.score_threshold (similarity only)
For similarity watches, alerts fire only when _score >= score_threshold. It must be a JSON number between 0 and 1 inclusive — a quoted string like "0.85" returns 400. The field is silently ignored for other watch types.
0.7is a reasonable starting point.0.85is appropriate for production opposition workflows.
customer_reference — your own label
customer_reference is a top-level field on the watch (alongside name and query, not inside the query DSL) — a free-text passthrough string you control. Signa never interprets it; it’s there for your own correlation (a case number, internal watch ID, team name, routing key).
- Set it on create,
PATCH, or in a bulk create — max 200 characters. Passnullto clear it. - It’s returned on the watch resource (
GET /v1/watches/{id}). - It is frozen at emit time and echoed onto every alert the watch produces, as
data.customer_reference— on both the RESTAlertresource and thealert.createdwebhook body. (Changing it later affects only alerts emitted after the change; already-emitted alerts keep the value they were stamped with.)
Per-jurisdiction watches
Build separate watches per jurisdiction when you need different delivery cadence per region, or when different regional teams own different jurisdictions. Otherwise passfilters.jurisdictions: ["US", "EU", "GB"] once and let a single watch cover the set.
Worked examples
Track one mark
Track a portfolio
Track a Nice class
Detect a similar mark
Preview before you launch
Dry-run the query against the last N days of data to estimate volume:filters or raise score_threshold.
Preview semantics worth knowing before you script against it (full details on
Preview Watch):
estimate_basis. When the response carriesestimate_basis: "candidacy_upper_bound", the count is an upper-bound estimate, not an exact match count (the scan hit the server-side cap, the search backend was unreachable, or the ~20s time budget expired partway). Absent field = exact count.- One preview at a time. A second concurrent preview for your org
returns
429with aRetry-Afterheader (~20s). Honor it — the SDK does. preview_timeout(504). If the time budget expires before any usable result exists you get apreview_timeoutenvelope withretryable: false. Narrow the query instead of retrying.- Latency.
class/mark/ownerpreviews complete in a few seconds; broadsimilaritypreviews are the heaviest and may approach the budget. - ID forms. Preview accepts the same prefixed (
tm_*/own_*) or raw-UUID ID filters as create.
Create up to 100 at once
See what a watch would catch
To dry-run a watch before you create or update it, usePOST /v1/watches/preview. By default it returns the actual matching marks (set count_only: true to get just the count). After you update a live watch (widen its offices, broaden trigger_events), the new criteria take effect automatically on the next ingestion sync — the watch re-evaluates with its current shape going forward. See Preview before you launch.
What’s not allowed in query
These are rejected with 400:
- DSL/presentation keys (anywhere in the query, at any depth):
function_score,script_score,script,sort,cursor,aggregations,aggs,highlight— scripting and presentation concerns don’t belong in a saved monitor. query.matchin any form — both the object form (match: {"nice_classes": [9]}) and the string form (match: "fuzzy"). Neither has ever been honored by the evaluator. Matching is driven bywatch_typeplusq/score_threshold(similarity) and the scoping fields underfilters.- Unknown
filterskeys — including snake_case typos of valid keys (nice_classesinstead ofniceClasses). See the full key list. - Unknown
trigger_eventsvalues (anything outside the five listed intrigger_events) and emptytrigger_eventsarrays.
Common errors
| Status | Cause |
|---|---|
400 | Invalid query DSL, stop words in q, more than 20 keywords, a forbidden key, or a missing required field for the selected watch_type. |
409 | Your plan’s watch limit has been reached. |
413 | query exceeds 256 KB. |