> ## Documentation Index
> Fetch the complete documentation index at: https://docs.signa.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Watch

> Create a watch that fires alerts when matching trademarks change

## Overview

Creates a saved monitor that runs on every ingestion sync. Five watch types
(`mark`, `portfolio`, `owner`, `class`, `similarity`) share a single
`query` shape — the `watch_type` selects which scoping field is required.
Resource quota (per-plan `watches.maxResources`) is enforced before insert.

Required scope: `portfolios:manage`.

<Note>
  The monitoring API is live, in beta — see [Known beta
  limitations](/guides/monitoring/beta-limitations). To preview what a watch
  would catch before you create or update it, use
  [`POST /v1/watches/preview`](/api-reference/monitoring/watches/preview),
  which returns the actual matching marks by default.
</Note>

## Body Parameters

<ParamField body="name" type="string" required>
  Display name (1--255 chars).
</ParamField>

<ParamField body="watch_type" type="string" required>
  One of `mark`, `portfolio`, `owner`, `class`, `similarity`. Each type
  requires a specific field to be populated — see
  [Watches guide](/guides/monitoring/watches) for the table.
</ParamField>

<ParamField body="query" type="object" required>
  v1 watch query DSL.

  <Expandable title="WatchQuery">
    <ParamField body="version" type="string" required>Always `"v1"`.</ParamField>
    <ParamField body="q" type="string">Whitespace-separated keyword query (max 20 keywords, each ≥3 chars, no stop words). Required for `watch_type: "similarity"`.</ParamField>
    <ParamField body="filters" type="object">Filter object. Keys are **camelCase** (`trademarkIds`, `ownerId`, `niceClasses`, `jurisdictions`, `offices`, `statusPrimary`, ...) — unknown keys, including snake\_case typos like `nice_classes`, are rejected with `400`. See the [canonical filter-key list](/guides/monitoring/watches#the-query-dsl).</ParamField>
    <ParamField body="trigger_events" type="string[]">Subset of `trademark.created`, `trademark.updated`, `trademark.status_changed`. Default: all three.</ParamField>
    <ParamField body="score_threshold" type="number">Similarity threshold (0..1). Only matches with `_score >= score_threshold` fire alerts. Valid for `watch_type: "similarity"`.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="delivery_mode" type="string">
  Currently only `always_per_alert` is supported. Digest modes land in v1.1.
</ParamField>

<ParamField body="metadata" type="object">
  Free-form metadata (max 8KB).
</ParamField>

## Response

Returns the created `Watch` with status `201`.

<ResponseField name="id" type="string">Watch ID (`wat_*`).</ResponseField>
<ResponseField name="object" type="string">Always `"watch"`.</ResponseField>
<ResponseField name="name" type="string">Display name.</ResponseField>
<ResponseField name="watch_type" type="string">One of the five types.</ResponseField>
<ResponseField name="query" type="object">Saved query DSL.</ResponseField>
<ResponseField name="delivery_mode" type="string">Delivery cadence.</ResponseField>
<ResponseField name="status" type="string">Initial status: `active`.</ResponseField>
<ResponseField name="alert_count_24h" type="integer | null">Null on create.</ResponseField>
<ResponseField name="last_alerted_at" type="string | null">Null on create.</ResponseField>
<ResponseField name="metadata" type="object">Echoed back.</ResponseField>
<ResponseField name="created_at" type="string">ISO timestamp.</ResponseField>
<ResponseField name="updated_at" type="string">ISO timestamp.</ResponseField>
<ResponseField name="request_id" type="string">Request identifier.</ResponseField>

## Errors

* **400** -- invalid `query` (forbidden DSL keys, unknown `filters` keys, `query.match` in any form, stop words, too many keywords) or missing `Idempotency-Key` header.
* **400** -- `watch_type` constraint violated (e.g., `portfolio` without `filters.trademarkIds`).
* **409** -- per-plan resource quota exceeded.
* **413** -- `query` payload exceeds 256 KB.

## Example

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.signa.so/v1/watches" \
    -H "Authorization: Bearer $SIGNA_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: create-class9-watch-2026-06-12" \
    -d '{
      "name": "Watch class 9 filings (US/EU)",
      "watch_type": "class",
      "query": {
        "version": "v1",
        "filters": {
          "niceClasses": [9],
          "jurisdictions": ["US", "EU"]
        },
        "trigger_events": ["trademark.created", "trademark.status_changed"]
      },
      "delivery_mode": "always_per_alert"
    }'
  ```

  ```ts TypeScript theme={null}
  import Signa from '@signa-so/sdk';
  const signa = new Signa({ api_key: process.env.SIGNA_API_KEY });

  const watch = await signa.watches.create({
    name: 'Watch class 9 filings (US/EU)',
    watch_type: 'class',
    query: {
      version: 'v1',
      filters: {
        niceClasses: [9],
        jurisdictions: ['US', 'EU'],
      },
      trigger_events: ['trademark.created', 'trademark.status_changed'],
    },
    delivery_mode: 'always_per_alert',
  });
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "id": "wat_01HK7M3QY8VVR0K1GH1A4N2D8B",
    "object": "watch",
    "name": "Watch class 9 filings (US/EU)",
    "watch_type": "class",
    "query": {
      "version": "v1",
      "filters": {
        "niceClasses": [9],
        "jurisdictions": ["US", "EU"]
      },
      "trigger_events": ["trademark.created", "trademark.status_changed"]
    },
    "delivery_mode": "always_per_alert",
    "status": "active",
    "alert_count_24h": null,
    "last_alerted_at": null,
    "metadata": {},
    "created_at": "2026-05-11T10:00:00.000Z",
    "updated_at": "2026-05-11T10:00:00.000Z",
    "request_id": "req_01ABC..."
  }
  ```
</ResponseExample>
