Pular para o conteúdo principal

Evaluate Alert

POST 

/v1/alerts/:alert_id/evaluate

Evaluate this alert's condition NOW, outside its schedule, and say what it concluded.

This is a REAL evaluation, not a dry run: it runs the condition's query on the same worker the scheduler uses, records the verdict, and — when the verdict TRANSITIONS into the breach — queues the capture, mails every recipient, and moves lastFiredAt. Calling it twice in a row on a still-breaching alert answers still_firing the second time and sends nothing; that is the hysteresis rule, not a failure.

lastEvaluatedAt DOES NOT MOVE. It reports SCHEDULED runs only — the same rule POST /v1/subscriptions/{id}/test has always stated for lastRun / lastStatus: writing it here would claim the schedule ran when it did not, and a customer reading the row back a week later cannot tell a cron firing from somebody's poke. It also feeds is_due and registration.nextRun, so a poke that stamped it moved the schedule.

lastFiredAt is different and DOES move on a fire here, because it answers "when did this alert last tell somebody" — and this route really did tell them. Which runs were scheduled and which were hand-asked is answerable from GET /v1/alerts/{alert_id}/deliveries, where every send carries its own trigger.

Read outcome:

  • fired — the condition holds and did not hold before. deliveryId is the capture row queued for it.

  • still_firing — it holds and already held. Audited, deliberately quiet.

  • not_fired — it does not hold; the alert is re-armed. Read verdict.observation with it, because not_fired is two different facts: measured means a number came back and was compared (proof a row was examined, and the number is on the verdict), while nothing means the query returned no rows and nothing else — which is ALSO what an alert whose scan set is empty returns (a mistyped filter value, a date nobody has data for yet, a feed that stopped). The truthy shape cannot tell those apart; the sugar shape can, and reports the empty scan as error. note says all of this in words, derived from the verdict.

  • error — it could NOT be measured (bad SQL, a table the dashboard does not bind, the question was deleted, the owner lost dataset access, or — for a SUGAR condition only — a result that is not one numeric cell). verdict is null, error names the failure, and the alert's status is now error so it is visibly broken rather than quietly silent. A failed measurement is never reported as fired: false.

  • warming_up — 202, not 200, and not an error. No SQL worker answered inside this request's budget, so the condition never ran. The alert is untouched: no verdict, no lastEvaluatedAt, no error status. A warm-up was kicked, so a retry in ~a minute (or the next scheduled tick) measures it. Before this outcome existed the request simply waited past the core lambda's 30 s timeout and the caller got an empty 503 — measured on prod 2026-09-09 — which said nothing and left the row equally untouched without saying so.

400 when the alert has no condition — there is nothing to evaluate; use POST /v1/alerts/{alert_id}/test to exercise its capture instead.

Request​

Responses​

Successful Response