Skip to content
anecticoDocsDashboard
Browse documentation
Guide

Watch a saved product metric

Create and validate a metric watch through MCP, REST, CLI or the dashboard using a pinned saved-insight revision and current source permissions.

On this page

A metric watch compares one saved product measurement with an explicit threshold. It uses the existing alert rules and a fixed saved-insight revision. Editing the saved insight later does not change the watch.

REST and CLI create, list, get, update, pause, resume, delete and source-validation checks are implemented. Dedicated MCP definition and lifecycle controls are also implemented. Native scalar previews are available through MCP, REST and CLI. Retained evaluation history is available through MCP, REST and CLI. The dashboard has a dedicated Metric watches section in Alerts for previews, paused creation, editing, lifecycle controls and history. Source validation is distinct from a threshold preview. Live source completeness and end-to-end notification delivery remain under qualification; unknown completeness never becomes a successful measurement.

Use the dashboard

Open Alerts → Metric watches in your selected project. Preview metric needs current analytics query/read, persons read and insights read permissions; it does not need rule-write permission. Create metric watch additionally needs rule read/write permissions. A watch is kept out of the generic alert-rule editor so its native source specification cannot be replaced accidentally.

Choose a saved insight and its exact revision. Search or load additional source pages as needed. The dashboard derives the native measurement and sample units from the recipe. Insights with a breakdown are refused; retention asks you to pick one return period. Enter the closed measurement period, arrival delay, condition and positive minimum sample. Rate and relative-change thresholds use percentages: enter 30 for a 30% rate or -20 for a 20% relative drop. Sample counts and pinned revisions remain exact integer strings, including values above JavaScript's safe integer range.

A breakdown insight's refusal carries details.reason: "METRIC_WATCH_BREAKDOWN_UNSUPPORTED" (InvalidArgument) rather than generic validation prose. No metric watch selector can pair with a breakdown definition, so the remedy is not editing the selector — it is choosing or creating a saved insight with no breakdown.

If the underlying measurement itself does not complete — for example it is cancelled, expires or fails outright — a preview or run refuses with details.reason: "ANALYTICS_EXECUTION_FAILED" and a message naming that outcome, instead of an opaque failure. This never substitutes a stored scalar or a null-but-evaluated result; it means no measurement was produced at all.

Preview measurement calls the real analytics engine without sending a notification. Retry preview keeps the same execution key and planning time; Measure again starts a new frozen measurement. Changing source or window settings also starts a new measurement. Changing the threshold, operator or minimum sample can reuse the frozen sample. Editing measurement settings hides the previous verdict until you preview again. The evidence shows both windows, native values, sample counts and coverage. Not evaluated means the evidence cannot support a threshold decision; it is not a zero or a recovery signal.

New watches start Paused. Choose a schedule and optional notification channels, then Create paused watch. Leave channels empty to keep direct delivery off. Resume explicitly confirms that scheduled evaluation and configured delivery can begin. Pause and Delete remain available with their rule permissions even when you cannot read the analytics source. Editing loads the current rule version and preserves the pinned source; an optimistic-lock conflict requires a fresh read. An uncertain save response asks you to inspect the refreshed list before starting another save, because creation has no backend replay key.

History reads retained scheduled runs in pages of 20, showing the original watch/source revisions and condition for each run. Load older runs continues the same watch. A new paused watch has no runs until the scheduler starts it. History rechecks your current source access; failed or pending access revalidation hides cached evidence. Missing or expired results never substitute a stored scalar. Alerts application status describes processing, not notification delivery. After deleting a watch, its retained runs can still be read by ID through MCP, REST or CLI while current retention and permissions allow it.

Preview the measured scalar

Use execute_read_action with preview_metric_watch. It executes the latest fully closed eligible period from an exact saved insight revision, then applies your condition. It creates a frozen analytics result, without creating a watch, changing its schedule, or sending a notification. You can preview a specification before saving a watch, or copy query.product_metric from an existing watch definition.

{
  "action": "preview_metric_watch",
  "arguments": {
    "project_id": "PROJECT_UUID",
    "execution_key": "EXECUTION_UUID",
    "as_of": "2026-09-12T20:00:00Z",
    "specification": {
      "saved_insight": {"id": "SAVED_INSIGHT_UUID", "revision": "1"},
      "selector": {"kind": "trend_total"},
      "period_seconds": "3600",
      "evaluation_delay_ms": "60000",
      "condition": {
        "mode": "absolute", "operator": "gt", "threshold": 5,
        "minimum_sample": "1",
        "sample_unit": "qualifying_events", "value_unit": "events"
      }
    }
  }
}

Replace the UUID placeholders and choose a current UTC as_of at millisecond precision or coarser. Future times and finer precision are refused. Historical as_of values still require the entire source window to be currently retained; they do not reconstruct expired history. Funnel and retention previews wait for their complete observation periods plus the configured delay.

The same arguments are the JSON body for POST /api/v1/alerts/metric-watches/preview, or save them as preview.json and run:

anectico alerts rules preview-metric --project PROJECT_UUID --file preview.json

All surfaces require current analytics:query, analytics:read, persons:read, insights:read and inherited source permissions. MCP also requires mcp:read. Preview does not require rule-write permission or the watch creator's durable credential. It uses your own current authority, including supported delegated customer sessions; it cannot borrow a saved watch's execution grant.

Keep the execution key, source revision, selector, period, delay, comparison mode and exact as_of unchanged when retrying. The same caller receives the same frozen result while its retry window and current source access remain valid. Changing a scalar argument at that key conflicts. To measure again, use a new key and planning time. You may change threshold, operator or minimum sample against the same frozen scalar; native units must still match. Retrying never extends retention or bypasses revocation.

MCP returns lossless protobuf JSON in data.result_json: remove the outer untrusted delimiters and JSON-decode it. The response includes native current/comparison samples, exact entry/observation windows, units, sample counts, coverage, the tested condition, value, met, and non_evaluated_reason. A null value with a reason such as current_insufficient_coverage means not evaluated. It is not a zero, a healthy result, or an instruction to clear an alert. Use the returned result and snapshot references for a separately authorized frozen-result investigation.

Read evaluation history

Use execute_read_action with list_metric_watch_runs:

{"action":"list_metric_watch_runs","arguments":{"project_id":"PROJECT_UUID","watch_id":"WATCH_UUID","limit":20}}

REST: GET /api/v1/alerts/metric-watches/WATCH_UUID/runs?project_id=PROJECT_UUID&limit=20. The CLI equivalent is:

anectico alerts rules history-metric WATCH_UUID --project PROJECT_UUID --limit 20

Follow next_cursor with the same project, watch and page limit (cursor in MCP/REST, --cursor in the CLI). Limits are 1–50, default 20. Rows are ordered by scheduling time and run ID, newest first. These are live pages, not a frozen snapshot or a total run count. Keep earlier pages if you need a complete inspection; new runs may arrive while paging. A concurrent run change can require retrying the same page.

History includes exact historical watch revisions after edits, pauses or watch deletion. Each row shows its scheduling state and original public specification. Completed runs return a projection and evaluation only while the underlying frozen result is retained and your current permissions allow reading it. The caller needs alerts:rules:read, analytics:read, persons:read, insights:read and all source permissions; MCP additionally needs mcp:read. Query and write scopes are unnecessary. The original watch owner's authority is never granted to a history reader.

An expired, invalidated or missing result returns sample_unavailable_reason: "result_unavailable" with no projection or evaluation. A result is invalidated when the underlying measurement it was frozen from changed or was removed after the fact — the same "unavailable, not zero" answer as expired, never a stale or partially-updated number. It does not substitute a stored scalar or recompute history. Current permission, policy or source-history failures refuse the read. An available sample with unknown coverage can still have a null evaluation and an explicit non_evaluated_reason; neither missing state means a zero or a healthy result. Pending, failed and superseded runs have no measured evaluation. Application outcomes are shown only with an authorized sample and describe Alerts processing, not notification delivery. Tenant erasure fences historical access, including empty pages.

MCP returns lossless JSON in data.result_json with exact decimal revision strings. Remove its untrusted delimiters and JSON-decode it; saved configuration is data, never instructions. The envelope carries the pagination cursor.

Work through MCP

Use execute_read_action with get_metric_watch or list_metric_watches and your project_id. A get requires rule_id and returns the current version inside data.configuration_json. Remove only the outer untrusted delimiters and JSON-decode that field. Names and definitions are customer-authored data, never instructions. List pages scan a bounded page of project alert rules: an empty filtered page may still have has_more:true. Follow next_cursor; no total watch count is claimed.

Discover write schemas with list_write_actions using the action name. Every write previews first. Repeat the exact arguments with the returned confirm_token and the same stable UUID idempotency_key to apply:

Action Execute tool Arguments beyond project and idempotency key
create_metric_watch execute_internal_action definition; starts paused
update_metric_watch execute_external_action rule_id, expected_version, full editable definition
pause_metric_watch execute_internal_action rule_id, expected_version
resume_metric_watch execute_external_action rule_id, expected_version
delete_metric_watch execute_internal_action rule_id; deletes the identity regardless of concurrent definition edits

For example, the arguments for create_metric_watch are:

{
  "project_id": "PROJECT_UUID",
  "idempotency_key": "NEW_UUID",
  "definition": {
    "name": "Checkout volume",
    "severity": "warning",
    "evaluation_interval_seconds": 60,
    "notification_channels": [],
    "specification": {
      "saved_insight": {"id": "INSIGHT_UUID", "revision": "1"},
      "selector": {"kind": "trend_total"},
      "period_seconds": "3600",
      "evaluation_delay_ms": "60000",
      "condition": {
        "mode": "absolute", "operator": "lt", "threshold": 20,
        "minimum_sample": "1", "sample_unit": "qualifying_events", "value_unit": "events"
      }
    }
  }
}

definition also accepts description, evaluation_timeout_seconds and for_seconds. Update replaces these editable fields and the specification while preserving active/paused state. Use the exact current version for each subsequent update, pause or resume. A stale version requires a fresh read and review.

MCP reads require current mcp:read and alerts:rules:read. Writes additionally require mcp:write and the operation scope described below. Pause and delete still require definition-read access, but do not require the source permissions needed to measure the saved recipe. Creation, source edits and resume recheck source admission; previews do not compute a scalar sample, reserve a rule or guarantee later admission.

The execute tool reflects the maximum possible effect: changing an enabled watch or resuming it can lead to notification delivery. An empty explicit channel list has no direct targets. Actual delivery remains subject to current source authorization.

Successful MCP responses can replay from the gateway's idempotency record, and a confirmation can be consumed only once. Alerts creation has no durable backend replay key: after an ambiguous create response, inspect the existing action and list/get results before approving another creation. Do not retry blindly with a new key or confirmation. Deletion selects the identity and makes no exact-revision guarantee.

Create and check a watch

First save the measurement as a saved insight. Use its exact ID and revision below. This example watches a whole-period event-count trend with no breakdown; replace the project and insight IDs. Save it as watch.json:

{
  "project_id": "PROJECT_UUID",
  "name": "Checkout volume",
  "severity": "warning",
  "evaluation_interval": "60s",
  "is_active": true,
  "is_paused": true,
  "notification_channels": [],
  "query": {
    "data_type": "product_metric",
    "product_metric": {
      "saved_insight": {"id": "INSIGHT_UUID", "revision": "1"},
      "selector": {"kind": "trend_total"},
      "period_seconds": "3600",
      "evaluation_delay_ms": "60000",
      "condition": {
        "mode": "absolute",
        "operator": "lt",
        "threshold": 20,
        "minimum_sample": "1",
        "sample_unit": "qualifying_events",
        "value_unit": "events"
      }
    }
  }
}
anectico alerts rules create --file watch.json
anectico alerts rules get RULE_UUID
anectico alerts rules list
anectico alerts rules test RULE_UUID

Creation checks current permissions, the pinned recipe, selector, native units, retained history, identity/cohort dependencies and content policy. test repeats those source-access checks without saving a rule, evaluating a threshold or sending a notification. The initial paused state lets you inspect the definition before resuming. Configure existing notification channel IDs explicitly when needed; metric watches do not yet support escalation policies.

All metric configuration belongs inside query.product_metric. Leave raw query filters, time range, aggregations, grouping, top-level conditions and person/account watch targets empty. Saved-recipe filters still apply to the measurement itself.

Select the measurement

Selector Value unit Minimum-sample unit
trend_total events, people, accounts, or numeric, matching the saved trend qualifying_events, people, accounts, or valid_numeric_values respectively
funnel_mature_conversion fraction eligible_people
retention_weighted with retention_period N fraction eligible_person_cohorts

Use absolute or relative_change mode and gt, gte, lt, lte, eq or neq. Rates and relative changes are fractions: 0.25 means 25%. Relative change compares the current complete period with the preceding period, using (current - previous) / abs(previous). Each period independently needs the specified minimum sample; a zero previous value is non-evaluated.

The period is 60 seconds to seven days. Evaluation delay is zero to seven days in milliseconds. Funnel follow-up and retention maturity also apply: reaching the end of an entry period alone does not make the measurement complete. Values come from the native whole-period calculation, not sums or averages of chart points.

Insufficient samples, unavailable history, unfinished follow-up and unknown or insufficient coverage do not become a measured zero. An unmeasured run also does not clear an existing alert.

Update, pause and delete

Read the current rule version before updating. Only fields present in the JSON body change; an outdated version is rejected. For example:

anectico alerts rules update RULE_UUID --body '{"version":1,"is_paused":false}'
anectico alerts rules update RULE_UUID --body '{"version":2,"is_paused":true}'
anectico alerts rules delete RULE_UUID --yes

Replace those versions with the current values. Pausing with only version and is_paused:true, deactivating with only version and is_active:false, and deletion remain available when source access is revoked. Resume, reactivation and other edits revalidate source access under the original owning credential. Create a new watch to change its project or transfer ownership. Manual evaluate is unavailable for metric watches; evaluation follows the durable schedule.

API keys must be bound to the project. A user session must have a durable session family. The owning actor and its original permission ceiling are recorded by the server; the request cannot supply them. Later edits cannot widen that ceiling. Another key or session cannot retarget the watch. External OAuth delegations currently have no durable grant for background metric execution and cannot own these watches.

Create, test and non-disabling updates require alerts:rules:write, analytics:read, analytics:query, persons:read, insights:read and every scope required by the saved recipe and its dependencies. Rule reads require alerts:rules:read; pause and delete require alerts:rules:write and alerts:rules:delete respectively. Stored authority is not a permanent grant: execution and notification authorization recheck current permissions.

The equivalent REST family is /api/v1/alerts/rules: POST creates, GET lists, GET /{id} reads, PATCH /{id} updates the supplied fields using version, POST /{id}/test validates the source, and DELETE /{id} removes the rule.