# Test Alert

```
POST 
/v1/alerts/:alert_id/test
```

Run this alert once now, outside its schedule. IT MAILS ITS RECIPIENTS.

**A test run is a live send, not a dry run.** Once the capture exists, every address on the alert — email, Slack webhook, in-app user — is reached exactly as on a real fire: `deliver_alert_capture` runs the channels before it ever looks at the delivery kind. That is what testing an alert means, and it is the only way to see the message a recipient actually gets. What the `test` kind changes is the BOOKKEEPING, not the send: `lastFiredAt` does not move and an `error` status is not cleared, because nothing fired. For a check that reaches nobody, use `POST /v1/alerts/{alert_id}/verify` (resolves the recipe, captures nothing, sends nothing) or `POST /v1/alerts/{alert_id}/backtest`.

**It says what a firing would say.** The condition is run READ-ONLY so the body carries the alert's own `say` sentence, rendered from a row measured just now — the point of the button being to answer "what will my team see?" before the alert is live, and the alert's name being the one part of that the author already knows. A template with a placeholder nobody filled, or one that reads badly with a real number in it, is visible here instead of at the first real breach. When the condition cannot be run the send still goes out and says so, with the engine's own words for why.

**And it cannot be mistaken for a firing.** Every channel marks it: the subject carries a test-run label, the body says nothing fired and that the alert's schedule and history were left untouched, the in-app title does not say "triggered". A rehearsal a recipient reads as a performance is a worse defect than a contentless one.

Measuring is not evaluating: `lastEvaluatedAt`, `lastVerdict`, `status` and `lastFiredAt` are all untouched, so a hand test cannot consume the slot the schedule was going to use, silence the fire that follows, or call a healthy alert broken.

**202 is an acknowledgement, not a delivery.** Read `executed` and `status`.

* `queued` + `executed: true` + `deliveryId` — the capture recipe was handed to the renderer. `deliveryId` is the `Screenshot` EXPORT row (the record dashboard subscriptions already use); poll it. The lane is asynchronous by construction — only the image-based `playwright` lambda owns a browser — so this route cannot report a finished capture, and never claims one.
* `not_executed` + `executed: false` — nothing ran, and `message` says why: this deployment cannot reach the renderer, the viz is gone, the alert has no user to impersonate, or the recipe was refused before a browser was booked (over 20 steps).

`captured` means an object exists in storage, and `failed` names the recipe step that broke; both are what the delivery row carries once the renderer is done. `lastEvaluatedAt` is untouched here — a capture is not an evaluation (that is slice 4). It never says `success`.

## Request[​](#request "Link direto para request")

## Responses[​](#responses "Link direto para Responses")

* 202
* 404
* 422

Successful Response

Not found

Validation Error
