> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
title: Sentry
description: Start AGNT5 workflows automatically from Sentry issues, errors, and comments.
last_verified: 2026-06-11
---

Connect Sentry to AGNT5 and your workflows will start automatically whenever an issue, error, or comment event fires. Every delivery is signature-verified before any workflow runs.

---

## Setup

### 1. Create the integration in Studio

1. Go to **Settings → Integrations** and click **Add Integration**.
2. Pick **Sentry**.
3. Fill in a name for the integration.
4. Choose the environment that should receive triggers.

<DocsImage
  src="/docs/integrations/sentry-integration-light.png"
  darkSrc="/docs/integrations/sentry-integration-dark.png"
  alt="Studio New Sentry Webhook panel showing Name, Environment, Webhook URL, and Signing Secret fields."
  caption="Fill in the name, select the environment, copy the webhook URL, and paste the signing secret from Sentry."
  width={1746}
  height={1060}
/>

### 2. Create an integration in Sentry

1. Go to [**Settings → Integrations → Custom Integrations**](https://agnt5.sentry.io/settings/developer-settings/) and click **Create New Integration**.
2. Fill in the required details and paste the webhook URL from AGNT5 Studio.
3. Under **Permissions**, configure access for each resource. For example:

   <Callout type="info">
   | Permission | Value |
   |---|---|
   | Project | Read |
   | Team | Read |
   | Release | No Access |
   | Distribution | No Access |
   | Issue & Event | Read |
   | Organization | Read |
   | Member | Read |
   | Alerts | No Access |
   </Callout>

4. Under **Webhooks**, check the event types you want to trigger workflows. For example:

   <Callout type="info">
   issue, error, comment, seer, preprod_artifact
   </Callout>
5. Click **Save Changes**.
6. Copy the **Client Secret** and paste it into the **Signing secret** field in Studio.

_[screenshot: Sentry Custom Integrations page with the Create New Integration button and the Client Secret field highlighted]_

---

## Events

Each Sentry event has a name in the format `sentry.<resource>.<action>`. Pass this name to `event()` in your workflow trigger.

| Resource | Action | Full event name |
|---|---|---|
| `issue` | `created` | `sentry.issue.created` |
| `issue` | `resolved` | `sentry.issue.resolved` |
| `issue` | `assigned` | `sentry.issue.assigned` |
| `issue` | `ignored` | `sentry.issue.ignored` |
| `error` | `created` | `sentry.error.created` |
| `comment` | `created` | `sentry.comment.created` |

---

## Workflow examples

### Issue events

Workflows receive the webhook envelope as keyword arguments (`**envelope`). The `body` field is the raw verified request body. It may arrive as a JSON string or an already-parsed dict, so parse defensively.

```python
import json
from agnt5 import WorkflowContext, workflow
from agnt5.types import event


def _parse_body(envelope: dict) -> dict:
    body = envelope.get("body", {})
    if isinstance(body, str):
        try:
            body = json.loads(body)
        except json.JSONDecodeError:
            return {}
    return body if isinstance(body, dict) else {}


@workflow(
    name="triage_sentry_issue",
    triggers=[event("sentry.issue.created")],
)
async def triage_sentry_issue(ctx: WorkflowContext, **envelope) -> dict:
    body = _parse_body(envelope)
    issue = body.get("data", {}).get("issue", {})

    ctx.logger.info(
        "Sentry issue.created: %s (level=%s)"
        % (issue.get("title", "unknown"), issue.get("level", "unknown"))
    )

    # add your triage logic here, e.g. open a ticket, page on-call
    return {
        "issue_id": issue.get("id"),
        "title": issue.get("title"),
        "level": issue.get("level"),
        "event_type": envelope.get("event_type"),
    }
```

### Comment events

Comment payloads nest the comment text several levels deep. Extract defensively:

```python
@workflow(
    name="log_sentry_comment",
    triggers=[event("sentry.comment.created")],
)
async def log_sentry_comment(ctx: WorkflowContext, **envelope) -> dict:
    body = _parse_body(envelope)
    data = body.get("data", {})
    comment = data.get("comment") or body.get("comment") or {}
    issue = data.get("issue") or body.get("issue") or {}
    actor = body.get("actor") or {}
    user = comment.get("user") or comment.get("author") or {}

    author = (
        comment.get("author_name")
        or user.get("name")
        or actor.get("name")
        or "unknown"
    )
    text = (
        comment.get("text")
        or comment.get("message")
        or data.get("text")
        or ""
    )

    ctx.logger.info(
        "Sentry comment from %s on %s: %s"
        % (author, issue.get("short_id", "unknown"), text[:200])
    )
    return {"author": author, "issue": issue.get("short_id"), "comment": text}
```

### Viewing triggered runs in Studio

After a Sentry event fires, the run appears in **Studio → Runs** within seconds. Open the run to see the envelope inputs and workflow output in the trace.

_[screenshot: Studio Runs list showing a completed triage_sentry_issue run, with the run detail open showing the sentry.issue.created event_type and the workflow output]_

---

## Envelope structure

Every Sentry-triggered run receives this envelope as `**envelope`:

```json
{
  "_webhook": true,
  "source": "sentry",
  "integration_id": "int_abc123",
  "event_type": "sentry.issue.created",
  "idempotency_key": "req_9f3c…",
  "timestamp": 1733337600,
  "headers": { "sentry-hook-resource": "issue", "request-id": "req_9f3c…" },
  "body": "{\"action\":\"created\",\"data\":{\"issue\":{…}}}"
}
```

`body` is the raw signature-verified request body. Parse it with `json.loads` before use.

---

## Signature verification

- Every delivery is verified using `sentry-hook-signature` before any workflow runs.
- Requests with an invalid or missing signature are rejected with `401`.

---

## Delivery semantics

- Sentry retries failed deliveries automatically.
- AGNT5 deduplicates using the `Request-ID` header.
- A retry replays the original run instead of starting a new one.

---

## Related

- [Event sources overview](/docs/integrations/event-sources/overview.md)
- [Webhooks](/docs/build/webhooks.md): trigger mechanism, signature verification, and delivery semantics
- [Workflows](/docs/build/workflows.md): how to write and run workflows
