> ## 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.

# Watch Diagnostics

> Check whether a watch fired, or should have fired, an alert for a specific trademark

## Overview

Answers the question every monitoring customer eventually asks: "I expected an alert for this
trademark. Why didn't I get one?" Given a watch and a trademark, this endpoint walks through
evaluation step by step and reports where it stopped, or confirms that an alert fired. It's
read-only: calling it never changes anything.

Requires the `portfolios:manage` scope.

## Path Parameters

<ParamField path="id" type="string" required>
  Watch ID (`wat_*`).
</ParamField>

## Query Parameters

<ParamField query="trademark_id" type="string" required>
  Trademark ID (`tm_*`) to check against this watch. A trademark ID from another org returns `404`.
</ParamField>

## Response

<ResponseField name="watch_id" type="string">Echoed watch ID (`wat_*`).</ResponseField>
<ResponseField name="trademark_id" type="string">Echoed trademark ID (`tm_*`).</ResponseField>
<ResponseField name="office_code" type="string">Uppercase ST.3 code of the office that issued the trademark (e.g. `US`, `EM`).</ResponseField>

<ResponseField name="evaluated" type="boolean">
  `true` if Signa evaluated this trademark against the watch within the current data window.
  `false` means either the change wasn't recorded in time, or it has aged past the window. See
  `reason`.
</ResponseField>

<ResponseField name="office_in_scope" type="boolean">
  `true` if this watch's filters include the trademark's office.
</ResponseField>

<ResponseField name="candidacy_passed" type="boolean">
  `true` if a change record for this trademark exists within the data window. A recorded change
  is a prerequisite for a match.
</ResponseField>

<ResponseField name="trigger_event_type" type="string | null">
  The lifecycle event associated with the match: `trademark.created`, `trademark.updated`,
  `trademark.status_changed`, `trademark.retracted`, or `trademark.corrected`.
</ResponseField>

<ResponseField name="trigger_event_in_filter" type="boolean">
  `true` if `trigger_event_type` is included in the watch's `query.trigger_events` filter. `false`
  explains a match that was silently dropped.
</ResponseField>

<ResponseField name="opensearch_score" type="number | null">
  The persisted search relevance score (`match_score`) from the most recent
  alert for this (watch, trademark). `null` when no alert exists or the watch
  has no scored (`q`) clause. Informational only.
</ResponseField>

<ResponseField name="score_threshold" type="number | null">
  The watch's stored `query.score_threshold`, surfaced only for legacy watches
  that still carry one. **Inert** — no longer gates matching, and rejected on
  new writes. Prefer `min_match_tier`. `null` when unset.
</ResponseField>

<ResponseField name="min_match_tier" type="'exact' | 'normalized' | 'fuzzy' | 'phonetic' | null">
  The watch's effective `query.min_match_tier`: which match attribution tiers can fire an alert.
  `null` when the watch doesn't gate by tier (every tier can fire). See
  [`min_match_tier`](/guides/monitoring/watches#min_match_tier-similarity-only) for tier meanings.
</ResponseField>

<ResponseField name="alert_fired" type="boolean">
  `true` if an alert exists for this (watch, trademark) pair.
</ResponseField>

<ResponseField name="alert_id" type="string | null">
  Alert ID (`alt_*`) when one fired, otherwise `null`. Retrieve it with
  [Get Alert](/api-reference/monitoring/alerts/retrieve), or cross-reference it against
  [List webhook deliveries](/api-reference/monitoring/webhooks/list-deliveries) to confirm your
  endpoint received it.
</ResponseField>

<ResponseField name="reason" type="string">
  Human-readable explanation for the outcome.

  | Value                                                                        | Meaning                                                                            |
  | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
  | `alert fired`                                                                | An alert exists for this pair. `alert_id` is set.                                  |
  | `watch does not include office {code}`                                       | This watch's filters don't cover the trademark's office.                           |
  | `trademark evaluated more than {N} days ago; provenance no longer available` | The change is older than the data window; Signa can no longer explain the outcome. |
  | `trademark not in candidacy window for the most recent {office} sync`        | No change was recorded recently enough for evaluation to consider this trademark.  |
  | `trigger event {type} not in watch.trigger_events`                           | The change happened, but its event type is excluded by this watch's filter.        |
  | `would alert but rolled into digest`                                         | The watch's delivery mode resolved to a digest instead of an immediate alert.      |
  | `no matching reason available`                                               | None of the above applied. Contact support if you see this.                        |
</ResponseField>

<ResponseField name="delivery_mode_effective" type="'per_alert' | 'digest' | null">
  The delivery mode that applied. Today this is `per_alert` or `null`, since `always_per_alert`
  is the only mode a watch can be created with.
</ResponseField>

<ResponseField name="opposition" type="object | null">
  Computed opposition-window state for this trademark. `null` when no rule applies.

  <Expandable title="opposition">
    <ResponseField name="must_act_by" type="string | null">ISO date: last day to file.</ResponseField>
    <ResponseField name="rule_source" type="string | null">Name of the rule that produced this deadline (e.g. `"USPTO TMEP §1503.02"`).</ResponseField>
    <ResponseField name="rule_version" type="string | null">Rule version.</ResponseField>
    <ResponseField name="window_status" type="'open' | 'closed' | 'not_started' | 'unknown' | null">Coarse window status.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="data_window" type="object">
  Retention horizons for the data behind this response. See
  [retention windows](/guides/monitoring/troubleshooting) for how they interact with alerts and
  webhook deliveries.

  <Expandable title="data_window">
    <ResponseField name="trademark_changes_retention_days" type="integer">Days of change history available.</ResponseField>
    <ResponseField name="deliveries_retention_days" type="integer">Days of webhook delivery history available.</ResponseField>
    <ResponseField name="alerts_retention_days" type="integer">Days alerts remain retrievable.</ResponseField>
    <ResponseField name="diagnostic_freshness_horizon_days" type="integer">The smallest of the windows above. Past this horizon, `evaluated` is `false`.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="request_id" type="string">Request identifier.</ResponseField>

<Accordion title="Internal evaluation fields">
  These fields appear in the response but describe internal evaluation state. Safe to ignore when
  debugging alert delivery.

  * `lease_state`: internal evaluation state; safe to ignore.
  * `evaluation_epoch`: internal evaluation state; safe to ignore.
  * `replay_epoch_origin`: internal evaluation state; safe to ignore.
  * `opensearch_score`: internal evaluation state; safe to ignore (currently always `null`).
  * `last_relevant_sync_run`: internal evaluation state; safe to ignore.
</Accordion>

## Errors

| Status | `type`             | When                                                                                                                                                                                                                       |
| ------ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `validation_error` | `trademark_id` query parameter missing                                                                                                                                                                                     |
| 403    | `forbidden`        | Caller lacks `portfolios:manage`                                                                                                                                                                                           |
| 404    | `not_found`        | The watch or trademark doesn't exist, belongs to another org, or the trademark is out of the watch's scope with no alert on record. These cases return the same response so an ID guess can't disclose another org's data. |

## Code Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.signa.so/v1/watches/wat_8kLm2nPq/diagnostics?trademark_id=tm_9vXq3Rmt" \
    -H "Authorization: Bearer sig_YOUR_KEY"
  ```

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

  const trace = await signa.watches.diagnostics("wat_8kLm2nPq", {
    trademarkId: "tm_9vXq3Rmt",
  });
  console.log(trace.reason);
  ```
</CodeGroup>

<ResponseExample>
  ```json Alert fired theme={null}
  {
    "watch_id": "wat_8kLm2nPq",
    "trademark_id": "tm_9vXq3Rmt",
    "office_code": "US",
    "evaluated": true,
    "office_in_scope": true,
    "candidacy_passed": true,
    "trigger_event_type": "trademark.status_changed",
    "trigger_event_in_filter": true,
    "score_threshold": null,
    "min_match_tier": null,
    "alert_fired": true,
    "reason": "alert fired",
    "delivery_mode_effective": "per_alert",
    "lease_state": "released",
    "evaluation_epoch": 0,
    "replay_epoch_origin": null,
    "last_relevant_sync_run": {
      "id": "018f9b2e-0000-7000-8000-000000000001",
      "office_code": "US",
      "completed_at": "2026-07-06T04:12:00.000Z",
      "search_indexed_at": "2026-07-06T04:18:00.000Z"
    },
    "opensearch_score": null,
    "alert_id": "alt_4tYpL2Qn",
    "opposition": {
      "must_act_by": "2026-09-04",
      "rule_source": "USPTO TMEP §1503.02",
      "rule_version": "2026.1",
      "window_status": "open"
    },
    "data_window": {
      "trademark_changes_retention_days": 90,
      "deliveries_retention_days": 30,
      "alerts_retention_days": 90,
      "diagnostic_freshness_horizon_days": 90
    },
    "request_id": "req_2mR8vNkT"
  }
  ```

  ```json No alert, filtered out theme={null}
  {
    "watch_id": "wat_8kLm2nPq",
    "trademark_id": "tm_9vXq3Rmt",
    "office_code": "EM",
    "evaluated": true,
    "office_in_scope": true,
    "candidacy_passed": true,
    "trigger_event_type": "trademark.updated",
    "trigger_event_in_filter": false,
    "score_threshold": null,
    "min_match_tier": null,
    "alert_fired": false,
    "reason": "trigger event trademark.updated not in watch.trigger_events",
    "delivery_mode_effective": null,
    "lease_state": "released",
    "evaluation_epoch": 0,
    "replay_epoch_origin": null,
    "last_relevant_sync_run": {
      "id": "018f9b2e-0000-7000-8000-000000000002",
      "office_code": "EM",
      "completed_at": "2026-07-06T02:40:00.000Z",
      "search_indexed_at": "2026-07-06T02:45:00.000Z"
    },
    "opensearch_score": null,
    "alert_id": null,
    "opposition": null,
    "data_window": {
      "trademark_changes_retention_days": 90,
      "deliveries_retention_days": 30,
      "alerts_retention_days": 90,
      "diagnostic_freshness_horizon_days": 90
    },
    "request_id": "req_3vXq7RmT"
  }
  ```
</ResponseExample>

## Related Endpoints

* [Retrieve Alert](/api-reference/monitoring/alerts/retrieve) - fetch the alert this diagnosis references
* [List webhook deliveries](/api-reference/monitoring/webhooks/list-deliveries) - confirm your endpoint received the alert
* [Watches guide](/guides/monitoring/watches) - watch types and the query DSL
* [Monitoring troubleshooting](/guides/monitoring/troubleshooting) - retention windows and debugging walkthrough
