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.