Alerts & channels
Where a detected incident actually goes: email, Slack, or your own webhook — and how to keep the noisy ones quiet without going blind.
A detector firing becomes an incident. An incident becomes a message somewhere you actually look. That second step is what alert channels configure.
Channels are per project
Each project routes its own alerts. One client’s incidents never land in another client’s Slack, which is the whole point when you run agents for a book of clients: one project per client, one Slack channel per project.
Add one from Alerts on the project, or from a coding agent with rendfly_add_alert_channel (email only over MCP — a Slack or webhook URL is a secret and does not belong in a chat transcript).
| Channel | Available on | What arrives |
|---|---|---|
| every tier, including Free | The incident, its evidence, and a link to the trace | |
| Slack | every tier, including Free | Same, as a message to the webhook’s channel |
| Webhook | Pro and up | A JSON POST to your endpoint |
| Team and up | A short message to a number you own |
What a webhook receives
An HTTPS POST, Content-Type: application/json, user agent rendfly-alerts/1:
{
"event": "alert.opened",
"project": "Support Bot",
"trigger_kind": "retry_storm",
"opened_at": "2026-08-06T09:20:01Z",
"summary": { "count_below": 3, "window_size": 20, "threshold": 0.2 },
"dashboard_url": "https://rendfly.com/app/workspaces/.../incidents/..."
} event is one of alert.opened, alert.resolved, or alert.test. The payload never carries conversation content — summary holds numeric aggregates only. To read the run itself, follow dashboard_url or pull the trace over MCP.
Verifying the signature
Turn on signing when you create the webhook and rendfly returns a signing secret once. Every request then carries:
X-Rendfly-Signature: sha256=<hex>
X-Rendfly-Timestamp: <unix seconds> The HMAC is computed over "<timestamp>.<raw body>" with SHA-256 — the timestamp is inside the signed material, so a captured request cannot be replayed with a fresh one. Verify against the raw body before parsing it, and reject anything with a timestamp outside your tolerance window:
import hashlib, hmac, time
def verify(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
if abs(time.time() - int(timestamp)) > 300: # 5-minute tolerance
return False
expected = hmac.new(
secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature) Destination URLs are validated against SSRF at creation and again at send time: HTTPS only, no private or link-local address ranges, and redirects are refused rather than followed.
Delivery
Alerts are queued, not sent inline with detection: the sweep that raises an incident writes the deliveries, and the dispatcher sends them. A failing destination is retried up to five attempts before the delivery is left failed — a Slack outage delays your alert, it does not lose it.
Use Send test on a channel to prove the path end to end. It sends an alert.test event through the real delivery machinery, so a green test means the real thing will arrive too.
When an alert is noise
Some incidents are correct and uninteresting. An agent that legitimately retries trips retry_storm. A nightly batch job trips cost_spike. The detector is right; the behaviour is normal for that agent.
Mute the incident instead of resolving it. A mute covers the whole grouping key — project, detector, failure class — so the same shape of incident stays quiet in the future. Occurrences keep being recorded and the incident is still readable; only the alerts stop. Unmute puts it back.
That distinction matters:
- Resolve — “this was a real problem and I fixed it.” If it fires again, the incident reopens as
regressed. - Mute — “this is normal here.” Nothing pages you again for this shape.
Neither one turns a detector off. There is no per-project detector toggle, on purpose: a detector you disabled six months ago is a failure you stopped being able to see.
Related
- Failure detectors & incidents — what fires, and what an incident carries
- MCP server — add a channel and triage incidents from your editor
- Quickstart — the full path from signup to first alert