---
sidebar_position: 3
title: Sentry triage recipe
---

# `sentry-triage` — agent-driven Sentry triage

An hourly Sentry triage agent that fetches unresolved issues, classifies them by severity, and **routes them to the right human**. Three nodes, end-to-end agent-driven, deployed from the marketplace in one click.

```
fetch_issues  →  classify  →  dispatch_alerts
(deterministic    (LLM —      (LLM —
 + Sentry API)    severity)    Slack/Lark, agent-driven routing)
```

## What it does

1. **fetch_issues** — calls Sentry's REST API for issues unresolved + unassigned + `lastSeen:-60m`. Hydrates each with `suspectCommits[]` (author email from Sentry's GitHub integration) for downstream routing.
2. **classify** — labels each issue `NOISE | LOW | MEDIUM | HIGH | CRITICAL` based on a configurable rubric (impact metric, surface area, payment paths, security tags). Skips below-threshold issues.
3. **dispatch_alerts** — the routing brain. Three layers of decisioning:
   - **Free-form `DISPATCH_RULES`** in env (highest priority) — natural language like *"send to Sam for billing issues"*
   - **Structured env vars** — `SLACK_CHANNEL`, `ROUTING_PREFER_AUTHOR`, `ROUTING_HIGH_SEVERITY_GROUP`
   - **Defaults** — channel-only post, threshold `MEDIUM`

The agent uses [`slack_lookup_user_by_email`](../skills/slack), [`slack_list_usergroups`](../skills/slack), [`slack_search_users`](../skills/slack) (or the Lark equivalents) to resolve names → IDs, then `slack_post_message` / `lark_send_message` to deliver. Channel post, user DM, usergroup mention — same agent decides per-issue based on what you wrote in the rules.

## Deploy from the marketplace

```bash
zibby agent templates deploy sentry-triage --project <project-id>
```

Or via the dashboard: `/marketplace/workflows` → Sentry Triage → Deploy.

After deploy, configure ENV (Apps → agent → ENV tab):

| Env var | Required? | Default | What it does |
|---|---|---|---|
| `SLACK_CHANNEL` *or* `LARK_RECEIVE_ID` | Yes (one of) | — | Channel id (Slack `C…`) / chat id (Lark `oc_…`) for fallback posts |
| `SEVERITY_THRESHOLD` | No | `MEDIUM` | Skip anything below: `NOISE` / `LOW` / `MEDIUM` / `HIGH` / `CRITICAL` |
| `ROUTING_PREFER_AUTHOR` | No | `false` | If `true`, when a suspect commit author is known, DM them |
| `ROUTING_HIGH_SEVERITY_GROUP` | No | — | Slack usergroup handle (`@oncall`) mentioned on CRITICAL/HIGH |
| `SLACK_MENTIONS` *or* `LARK_MENTIONS` | No | `[]` | JSON array of mentions prepended on CRITICAL only |
| `DISPATCH_RULES` | No | — | Free-form natural-language override (see below) |

## DISPATCH_RULES — natural-language routing

When you set `DISPATCH_RULES`, the agent treats it as **authoritative**; the structured env vars become fallbacks for things the rules don't cover.

```
DISPATCH_RULES="
- CRITICAL bugs in /payment/ → DM Sam and post to #incidents
- HIGH severity → DM the suspect commit author if known, else post to #engineering
- Anything mentioning 'security' → also mention the @security usergroup
- Frontend bugs (zibby-frontend project) → only Sarah, never page on-call
- NOISE → skip entirely
"
```

The agent reads issue metadata (severity, message, tags, suspectCommit author email, project name) and applies rules in order. **Same rule + same issue is deterministic** — temperature 0, schema-enforced output, every dispatch records who got it and why under `dispatched[].recipient.{kind,id,label}`.

## Author-DM path

When `ROUTING_PREFER_AUTHOR=true` and Sentry has a `suspectCommits[0].author.email`:

```
1. agent reads issue.suspectCommits[0].authorEmail
2. → slack_lookup_user_by_email(email)
3a. ✓ returns {id, name}  → slack_post_message(channel: <user-id>, text: …)
3b. ✗ users_not_found     → channel fallback
```

Requires the [Sentry → GitHub integration](https://sentry.io/settings/integrations/github/) installed and Code Mappings configured. Without it, `suspectCommits[]` is empty and the agent falls back to channel-only routing automatically.

If you deployed your backend with `RELEASE_SHA` Sentry release-tracking on, suspect commits populate within ~minutes of new issues being created. (The platform-side wiring — `Sentry.init({release})` + `sentry-cli releases set-commits --auto` at deploy time — is what makes per-issue blame work; without it, every issue lands with `suspectCommits: []`.)

## Customize the prompts

Each node's prompt lives in its own module — fork the template, edit, redeploy:

```bash
zibby agent download <uuid>
# edit nodes/dispatch-node.js
zibby agent deploy ./sentry-triage   # same UUID, new version
```

Or fork the whole template repo if you want long-term divergence — it's just a `@zibby/workflow-templates/sentry-triage/` directory in the published package.

## Cadence

Default: hourly cron, fires `sinceMinutes=60`. Change in the trigger config (Apps → agent → Triggers) — keep the SQL safe `since` between 5 and 1440 minutes (`inputSchema` enforces this).

→ Next: [`zibby test`](./test) (the browser-testing recipe) or [Build your own agent](../get-started/your-first-workflow).
