# Opsgenie Webhooks

Opsgenie (Atlassian incident alerting) can post a JSON webhook to your URL whenever an alert
action happens: an alert is created, acknowledged, closed, escalated and so on. Webhooks are
**not signed**. You add a secret custom header to the Webhook integration, and your endpoint
authenticates each request by comparing that header with the value you stored.

> **Opsgenie is end-of-sale and Atlassian support ends in April 2027.** Customers are being moved
> to Jira Service Management and Compass. Existing Webhook integrations keep working until then;
> plan to re-point alert automation at the replacement product.

## When to Use This Skill

* How do I receive Opsgenie webhooks?
* How do I authenticate an Opsgenie Webhook integration? (No signature: use a secret custom header)
* How do I handle `Create`, `Acknowledge`, `Close` or `Escalate` alert actions?
* How do I handle a custom alert action name?
* Why does my Opsgenie webhook endpoint return 401?

## Verification (core)

```javascript
const crypto = require('crypto');

// Lengths first: timingSafeEqual throws on buffers of different sizes.
function safeEqual(a, b) {
  const x = Buffer.from(String(a), 'utf8');
  const y = Buffer.from(String(b), 'utf8');
  return x.length === y.length && crypto.timingSafeEqual(x, y);
}

function verifyOpsgenieRequest(headers,
    headerName = process.env.OPSGENIE_WEBHOOK_HEADER || 'Authorization',
    secret = process.env.OPSGENIE_WEBHOOK_SECRET) {
  if (!secret) return false;                          // never accept when unconfigured
  const received = headers[headerName.toLowerCase()]; // Node lowercases header names
  return typeof received === 'string' && received !== '' && safeEqual(received, secret);
}

```

* The header **name** is whatever you typed as the custom header on the Webhook integration. Read
  it from `OPSGENIE_WEBHOOK_HEADER` (default `Authorization`) and look it up case-insensitively.
* Compare the **whole value** with `OPSGENIE_WEBHOOK_SECRET`, exactly as typed in Opsgenie. If you
  entered `Bearer abc123`, the secret is `Bearer abc123`. Don't strip or parse a `Bearer ` prefix.
* Return `401` for a missing header or a mismatch, `400` for a body that isn't a JSON object,
  `200` once handled.
* Read the body raw (`express.raw({ type: '*/*' })`) and authenticate before parsing it.
* Python: `hmac.compare_digest(a.encode(), b.encode())`. Always pass bytes; `str` arguments raise
  `TypeError` on non-ASCII.

**What this does and doesn't give you.** A static shared credential authenticates the sender, not
the body, and gives no replay protection (there is no signature and no timestamp): anyone who
captures one request can resend it. Use HTTPS, make handlers idempotent, and rotate the credential
if it leaks.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/opsgenie-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/opsgenie-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/opsgenie-webhooks/examples/fastapi/).

## Alert Actions

The payload has an `action` field, an `alert` object and usually a `source` object, plus
`integrationId` and `integrationName`.

| `action` | Sent When | Extra `alert` fields |
| --- | --- | --- |
| `Create` | An alert is created | `teams`, `recipients` |
| `Acknowledge` | An alert is acknowledged |  |
| `UnAcknowledge` | An acknowledgement is removed |  |
| `AddNote` | A note is added | `note` |
| `AddTags` | Tags are added | `addedTags` |
| `RemoveTags` | Tags are removed | `removedTags` |
| `Close` | An alert is closed |  |
| `AssignOwnership` | An owner is assigned | `owner` |
| `TakeOwnership` | A user takes ownership |  |
| `Delete` | An alert is deleted |  |
| `Escalate` | An alert is escalated | top-level `escalationName`, `escalationNotify`; `alert` has only `alertId` |
| `UpdatePriority` | Priority changes | `priority`, `oldPriority` |
| `UpdateDescription` | Description changes | `description` |
| `UpdateMessage` | Message changes | `message` |
| `AddTeam` | A team is added | `team` |
| `AddRecipient` | A recipient is added | `recipient` |
| *your own name* | A custom action runs | Sent as `action`, e.g. `TestAction` |

## Environment Variables

```bash
OPSGENIE_WEBHOOK_HEADER=Authorization   # name of the custom header you added in Opsgenie
OPSGENIE_WEBHOOK_SECRET=your-secret     # its exact, whole value (including any "Bearer " prefix)

```

## Local Development

Opsgenie can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 opsgenie --path /webhooks/opsgenie

```

Use the printed URL as the webhook URL in Opsgenie.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/opsgenie-webhooks/references/overview.md) - What Opsgenie webhooks are, actions and payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/opsgenie-webhooks/references/setup.md) - Add the Webhook integration and the secret header
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/opsgenie-webhooks/references/verification.md) - Header check details, limits, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/opsgenie-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests