# Sentry webhook trigger

> Run a workflow on Sentry issue events: new, resolved, assigned, archived, and unresolved, with signature verification.

Source: https://cotera.co/docs/reference/webhooks/sentry

---

Run a workflow on Sentry issue events — a new issue fires, an issue is resolved, an issue is assigned. The issue payload becomes the workflow's input, so you can triage, route, or enrich it. Pair it with the [Sentry tool](https://cotera.co/docs/reference/tools/individual-tools/sentry.md) to look up the issue's frequency and tags before deciding what to do.

For behavior shared by every provider (the delivery URL, testing), see the [webhook triggers reference](https://cotera.co/docs/reference/webhooks.md).

## Setup

Sentry has no create-webhook API — you wire it up in Sentry once, on an integration:

1. Add a **From an app** trigger and pick **Sentry**. Cotera gives you a delivery URL.
2. In Sentry, open (or create) an **Internal Integration** under **Settings → Custom Integrations**, add Cotera's URL as the **Webhook URL**, and enable the **issue** resource.
3. Provide the integration's **Client Secret** to Cotera — that's the secret Sentry signs deliveries with.

There's nothing to tear down on Cotera's side; remove the integration in Sentry to stop deliveries.

## Events

| Event key          | Fires when                       |
| ------------------ | -------------------------------- |
| `issue.created`    | A new issue is created in Sentry |
| `issue.resolved`   | An issue is resolved             |
| `issue.assigned`   | An issue is assigned             |
| `issue.archived`   | An issue is archived             |
| `issue.unresolved` | An issue is marked unresolved    |

## Payload

The Sentry issue webhook payload is passed through as the workflow input:

```json
{
  "action": "created",
  "installation": { "uuid": "a8e2d8b0-0000-4000-8000-000000000000" },
  "data": {
    "issue": {
      "id": "1234567890",
      "shortId": "BACKEND-7",
      "title": "TypeError: Cannot read property \"id\" of undefined",
      "culprit": "webpack/bootstrap",
      "level": "error",
      "status": "unresolved",
      "platform": "javascript",
      "project": { "id": "2", "name": "Backend", "slug": "backend" },
      "metadata": { "type": "TypeError", "value": "Cannot read property \"id\" of undefined" },
      "count": "1",
      "userCount": 0,
      "web_url": "https://cotera.sentry.io/issues/1234567890/",
      "firstSeen": "2023-07-15T10:30:00Z",
      "lastSeen": "2023-07-15T10:30:00Z"
    }
  },
  "actor": { "type": "application", "id": "sentry", "name": "Sentry" }
}
```

| Field               | Description                                                                                              |
| ------------------- | -------------------------------------------------------------------------------------------------------- |
| `action`            | The issue action — `created`, `resolved`, `assigned`, `archived`, or `unresolved`.                       |
| `data.issue`        | The Sentry issue: `id`, `shortId`, `title`, `level`, `status`, `project`, `metadata`, counts, and links. |
| `actor`             | Who triggered the event (a user or Sentry itself).                                                       |
| `installation.uuid` | The Sentry integration the delivery came from.                                                           |

Sentry also sends a `Sentry-Hook-Resource` header (`issue`) and a `Request-ID` header, which Cotera uses to deduplicate retries.

## Signature verification

Sentry signs each delivery and sends it in the `Sentry-Hook-Signature` header — an HMAC-SHA256, in hexadecimal, of the raw request body. The signing key is the Sentry integration's **Client Secret** that you gave Cotera during setup. Cotera verifies it on every delivery and rejects anything that fails.

