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

# Troubleshooting

> Use the diagnostics endpoint to find out why an alert didn't fire, and monitor Signa's own uptime

When an expected alert doesn't arrive, the diagnostics endpoint tells you exactly why, so you can separate query, delivery, and platform issues quickly.

## Step 1: Run the diagnostics

For each (watch, trademark) pair you expected an alert for, call [`GET /v1/watches/{id}/diagnostics`](/api-reference/monitoring/watches/diagnostics):

```ts theme={null}
const trace = await signa.watches.diagnostics('wat_01HK7M...', {
  trademarkId: 'tm_01HK7N...',
});
console.log(trace.reason);
```

The `reason` field gives you the answer in plain English. The endpoint walks the evaluation steps in order and surfaces the first failure.

## Step 2: Interpret `reason`

| `reason` value                                                              | Meaning                                                                                                                                           | Next step                                                                                                                                                                                                                                     |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alert fired`                                                               | Alert was generated. Use `alert_id` to chase webhook delivery.                                                                                    | See Step 3.                                                                                                                                                                                                                                   |
| `watch does not include office {code}`                                      | The watch's `filters.offices` (or `filters.jurisdictions`) doesn't include this trademark's office.                                               | `PATCH` the watch to add the office. The widened criteria apply automatically to future data updates.                                                                                                                                         |
| `trademark evaluated more than 90 days ago; provenance no longer available` | Outside the 90-day diagnostic horizon.                                                                                                            | The trace cannot be regenerated. Check the diagnostics `data_window` object for every retention horizon, and diagnose future misses within it. If the mark still matches the watch, it will be re-evaluated on the next update going forward. |
| `trademark not in candidacy window for the most recent {office} sync run`   | Signa hasn't seen a change to this trademark from that office since the watch last checked it.                                                    | Run [`POST /v1/watches/preview`](/api-reference/monitoring/watches/preview) with the watch's query to confirm the trademark would match today.                                                                                                |
| `trigger event {type} not in watch.trigger_events`                          | The watch's `trigger_events` filter excluded this event type. For example, you watch only `trademark.created` but this was a `trademark.updated`. | Widen `trigger_events` via `PATCH`. The new shape applies to future updates automatically.                                                                                                                                                    |
| `no matching reason available`                                              | Fallback.                                                                                                                                         | File a support ticket with the `request_id`. This should not happen in steady state.                                                                                                                                                          |

## Step 3: Cross-reference webhook delivery

When `alert_fired=true` but you never saw it on your receiver, follow the trace to the delivery log:

```ts theme={null}
// 1. The alert fired. alert_id (alt_*) is the cross-system trace handle.
const trace = await signa.watches.diagnostics('wat_01HK7M...', { trademarkId });
console.log(trace.alert_id);

// 2. List delivery attempts for the endpoint subscribed to alert.created.
const deliveries = await signa.webhooks.listDeliveries('whk_01HK...', {
  since: '2026-05-01T00:00:00Z',
});

// 3. Find the delivery rows for that alert and inspect the outcome.
for await (const d of deliveries) {
  if (d.alert_id === trace.alert_id) {
    console.log(d.status, d.http_status, d.error_reason, d.response_body);
  }
}
```

Likely outcomes:

* `delivered`: receiver returned 2xx but may not have stored it. Inspect `response_body` and your own logs.
* `failed`: non-2xx response. `error_reason` (e.g. `non_2xx_500`) and `http_status` tell you which side broke. Up to 7 attempts total.
* `exhausted`: all attempts failed. Replay manually with [`POST /v1/webhooks/{id}/deliveries/{did}/redeliver`](/api-reference/monitoring/webhooks/redeliver).
* `pending`: still queued. Wait, or check [`/health/ready`](#monitor-signa-uptime) to confirm Signa is up.

If the endpoint was auto-disabled mid-flight, check [`GET /v1/webhooks/{id}`](/api-reference/monitoring/webhooks/retrieve). `status='disabled'` plus `disabled_reason` (`auto_consecutive_100`, `auto_failure_rate_50_over_50`, or `manual`) explains why. Re-enable with [`PATCH /v1/webhooks/{id}`](/api-reference/monitoring/webhooks/update) once the receiver is healthy again.

## Step 4: Confirm Signa is healthy

Before assuming a Signa-side bug, check readiness (see [Monitor Signa uptime](#monitor-signa-uptime) below). Retry the diagnostic flow once the platform reports `ok`.

## Monitor Signa uptime

Two unauthenticated health endpoints let you monitor Signa from your own observability stack, useful for SLA reporting, status-page integration, and incident triage.

| Endpoint            | What it tells you                                                                                                                                                                                                                   |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /v1/health`    | The API process is up. Always returns HTTP 200 and `{"status":"ok"}`; it does not check dependencies, so it cannot report `degraded` or `unhealthy`.                                                                                |
| `GET /health/ready` | The API plus its database, cache, and search dependencies are reachable. Returns `status: "ok"`, `"degraded"` (a non-critical dependency is down, most requests still work), or `"unhealthy"` (503; a critical dependency is down). |

Use `/health/ready` for real uptime and incident-detection monitoring; use `/v1/health` only as a lightweight liveness ping. Both endpoints are unauthenticated, no API key is required for your monitoring agent.

```bash theme={null}
curl https://api.signa.so/health/ready
```

```json theme={null}
{
  "status": "ok",
  "service": "core-api",
  "uptime_ms": 1234567,
  "dependencies": {
    "postgres": { "status": "ok", "latency_ms": 4 },
    "valkey": { "status": "ok", "latency_ms": 1 },
    "opensearch": { "status": "ok", "latency_ms": 6 }
  }
}
```

Example synthetic check, adjust to your monitoring tool of choice. Assert only on the HTTP status code (200 vs 503), that already separates "up" (`ok` or `degraded`) from "down" (`unhealthy`); asserting the body's `status` equals exactly `"ok"` would page you on every non-critical dependency blip:

```yaml theme={null}
type: api
name: Signa API readiness
config:
  request:
    method: GET
    url: https://api.signa.so/health/ready
  assertions:
    - type: statusCode
      operator: is
      target: 200
options:
  tick_every: 60
```

If you want visibility into `degraded` without paging on it, log the response body's `status` field separately as a warning-level signal. A cron or uptime-checker version of the same check:

```bash theme={null}
#!/usr/bin/env bash
set -e
RESPONSE=$(curl -fsS --max-time 5 https://api.signa.so/health/ready)
STATUS=$(echo "$RESPONSE" | jq -r '.status')
if [ "$STATUS" = "ok" ] || [ "$STATUS" = "degraded" ]; then
  exit 0
fi
echo "Signa API ${STATUS}: ${RESPONSE}" >&2
exit 1
```

### What `/health/ready` doesn't cover

`/health/ready` is API-side health only. It does not surface:

* **Data freshness for a specific office.** An office falling behind on updates doesn't flip the API to `degraded`. If you watch a specific office and alerts stop firing, check the [diagnostics endpoint](/api-reference/monitoring/watches/diagnostics), `last_relevant_sync_run.completed_at` will be stale.
* **Webhook delivery.** That's between Signa and your receiver. Cross-check [`GET /v1/webhooks/{id}/deliveries`](/api-reference/monitoring/webhooks/list-deliveries) for the delivery audit log.

If `/health/ready` is green but you suspect a Signa-side issue with a specific watch, use the diagnostics endpoint above.

Signa does not currently publish a public status page. If you maintain your own, surface `/health/ready` and the diagnostics-derived freshness signal separately, they cover different incident classes. For incident questions, email [support@signa.so](mailto:support@signa.so), or report a reproducible platform bug programmatically via [`POST /v1/feedback`](/api-reference/administration/create-feedback) (attach the `request_id` from a failed call so we see exactly what you hit).

## Retention

| Data                                         | Window  |
| -------------------------------------------- | ------- |
| Change records (used for diagnostic lookups) | 90 days |
| Webhook delivery audit                       | 30 days |
| Alerts                                       | 90 days |
| Diagnostic freshness horizon                 | 90 days |

Past the diagnostic horizon, `evaluated=false` and `reason` explains the freshness limit. A watch always continues to evaluate new data going forward, the horizon only limits the backward-looking diagnostic trace.
