# Guide to Opsgenie Webhooks: Features and Best Practices

Opsgenie webhooks send alert activity to the systems around your on-call process: an alert is created, someone acknowledges it, a note is added, the priority changes, it escalates or it closes. If you sync alerts to a ticketing tool, a status page or an incident timeline, the Webhook integration pushes each of those actions as it happens.

This guide covers how the Opsgenie Webhook integration maps alert actions to requests, what the payloads contain, how to authenticate requests Opsgenie doesn't sign, and the best practices for a production receiver. It starts with something you need to plan for: Opsgenie is being shut down.

## Opsgenie's end of support

Atlassian is moving Opsgenie's capabilities into Jira Service Management. Opsgenie reached end of sale on June 4, 2025, and its end of support date is April 5, 2027. Atlassian says existing customers can keep using Opsgenie until then, after which it shuts down, any data that hasn't been moved is deleted, and remaining Opsgenie-based integrations stop working. Opsgenie also shuts down automatically 120 days after a customer's migration date if a site admin doesn't turn it off first.

If you're building a new Opsgenie webhook receiver, build it knowing it has a fixed lifetime. Keep the alert-handling logic separate from the Opsgenie-specific parsing, so you can put a Jira Service Management integration in front of the same logic when you migrate.

## What are Opsgenie webhooks?

The Webhook integration sends an HTTP `POST` with a JSON body to a URL you choose when an alert action happens. You decide which actions post, using "post to url" mappings such as "If alert is created in Opsgenie, post to url". The payload carries the action plus a subset of the alert's fields, and you can choose to include the alert description and details.

Opsgenie doesn't sign webhook requests. The integration's only authentication setting is custom headers, which are added to every call. Put a secret in one and have your receiver compare it.

## Opsgenie webhook features

| Feature | Details |
| --- | --- |
| Configuration | **Settings > Integrations > Add integration > Webhook** |
| Team scope | Optional **Assignee team** on the integration |
| Mappings | One "post to url" per alert action. Tag-added and custom-action mappings can be repeated with different tags or action names |
| Method | `POST` with a JSON body |
| Payload | `action`, an `alert` subset, `source`, `integrationId`, `integrationName` |
| Description and details | Optional, truncated to 1,000 characters, only for `Create` and custom actions |
| Authentication | Custom headers on the integration |
| Signature | None |
| Retries and timeout | Not documented |
| Lifecycle | End of sale June 4, 2025. End of support April 5, 2027 |

## Common events

The `action` field names what happened. Opsgenie documents 16 standard actions:

| `action` | Fires when | Extra fields |
| --- | --- | --- |
| `Create` | An alert is created | `alert.teams`, `alert.recipients` |
| `Acknowledge` | An alert is acknowledged |  |
| `UnAcknowledge` | An acknowledgement is removed |  |
| `AddNote` | A note is added | `alert.note` |
| `AddTags` | Tags are added | `alert.addedTags` |
| `RemoveTags` | Tags are removed | `alert.removedTags` |
| `AddTeam` | A team is added | `alert.team` |
| `AddRecipient` | A recipient is added | `alert.recipient` |
| `AssignOwnership` | An owner is assigned | `alert.owner` |
| `TakeOwnership` | A user takes ownership |  |
| `Escalate` | An alert is escalated | `escalationId`, `escalationName`, `escalationNotify`, `escalationTime`, `repeatCount` |
| `UpdatePriority` | The priority changes | `alert.priority`, `alert.oldPriority` |
| `UpdateMessage` | The message changes | `alert.message` |
| `UpdateDescription` | The description changes | `alert.description` |
| `Close` | An alert is closed |  |
| `Delete` | An alert is deleted |  |

Custom alert actions send their own name as `action`, for example `TestAction`, so your router will see values outside this list if your team uses them.

Here is Opsgenie's example for a created alert:

```json
{
  "source": { "name": "web", "type": "API" },
  "alert": {
    "alertId": "052652ac-5d1c-464a-812a-7dd18bbfba8c",
    "alias": "aliastest",
    "tinyId": "23",
    "message": " test alert",
    "tags": ["tag1", "tag2"],
    "teams": ["team1", "team2"],
    "recipients": ["recipient1", "recipient2"],
    "username": "fili@ifountain.com",
    "userId": "daed1180-0ce8-438b-8f8e-57e1a5920a2d",
    "source": "fili@ifountain.com",
    "entity": "",
    "createdAt": 1420452191104,
    "updatedAt": 1420452193166002000
  },
  "action": "Create",
  "integrationId": "37c8f316-17c6-49d7-899b-9c7e540c048d",
  "integrationName": "Integration1"
}

```

Note the units: `createdAt` is in milliseconds, but `updatedAt` has nanosecond precision. A 19-digit nanosecond value is larger than JavaScript can represent exactly as a number, so don't use it as an exact key without care. `Escalate` is shaped differently from the rest: its `alert` object holds only `alertId`, and the escalation details sit at the top level.

> **See Opsgenie webhook payloads in action.** Inspect and replay sample Opsgenie webhook payloads in [Hookdeck Inspect](https://inspect.hookdeck.com/webhooks/opsgenie), with no account or setup required.

## Setting up Opsgenie webhooks

Generate a long random secret first (`openssl rand -hex 32` works) and store it in your app's environment.

1. In Opsgenie, go to **Settings > Integrations** and select **Add integration**.
2. Search for and select **Webhook**.
3. Name the integration and, optionally, choose an **Assignee team** to limit it to that team's alerts. Select **Continue**.
4. Enter your HTTPS endpoint as the Webhook URL.
5. Add a custom header for authentication, for example `X-Webhook-Secret` with your secret, or `Authorization` with `Bearer <secret>`.
6. Tick the boxes to send the alert description and details if you need them. They're only sent for `Create` and custom actions.
7. Under the post-to-URL section, add a mapping for each alert action you want, for example "If alert is created in Opsgenie, post to url".
8. Select **Turn on integration**. Mappings only work once the integration is on.

Each standard action can have only one "post to url" mapping per integration. Opsgenie raises an error if you add a second, except for "a tag is added to the alert" and "a custom action is executed on alert", which can have several mappings as long as the tags or custom action names differ.

For local development, use the [Hookdeck CLI](/docs/cli): `hookdeck listen 3000 opsgenie --path /webhooks/opsgenie` gives you a public HTTPS URL that forwards to your local server, plus a web UI for inspecting and replaying deliveries, with no account required.

**With Hookdeck.** Create an Opsgenie source in Hookdeck, enter the **Header Name** and **Header Value** exactly as you set them on the integration, including any `Bearer ` prefix, and use the source URL as the Webhook URL.

## Securing Opsgenie webhooks

Compare your custom header with the stored value. Look it up case-insensitively, compare the whole value in constant time, and check lengths first, because `crypto.timingSafeEqual` throws on buffers of different lengths.

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

const app = express();

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) {
  const secret = process.env.OPSGENIE_WEBHOOK_SECRET;
  if (!secret) return false; // fail closed when unconfigured
  const name = (process.env.OPSGENIE_WEBHOOK_HEADER || 'X-Webhook-Secret').toLowerCase();
  const received = headers[name];
  return typeof received === 'string' && safeEqual(received, secret);
}

app.post('/webhooks/opsgenie', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyOpsgenieRequest(req.headers)) {
    return res.sendStatus(401);
  }

  let payload;
  try {
    payload = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  res.sendStatus(200);

  const alertId = payload.alert && payload.alert.alertId;
  switch (payload.action) {
    case 'Create':
      // open a ticket keyed on alertId
      break;
    case 'Acknowledge':
    case 'Close':
      // update the ticket's state
      break;
    default:
      // other standard or custom actions
      break;
  }
});

```

The same check in Python:

```python
import hmac
import os

def verify_opsgenie_request(headers) -> bool:
    secret = os.environ.get("OPSGENIE_WEBHOOK_SECRET")
    if not secret:
        return False
    name = os.environ.get("OPSGENIE_WEBHOOK_HEADER", "X-Webhook-Secret")
    received = headers.get(name)  # case-insensitive in Starlette and Flask
    if not received:
        return False
    return hmac.compare_digest(received.encode("utf-8"), secret.encode("utf-8"))

```

A static header authenticates the sender, not the body, and it gives no replay protection: there's no signature or timestamp, so a captured request can be resent. Serve the endpoint over HTTPS, keep the secret out of logs, and rotate it on the integration if it leaks. Opsgenie also publishes a list of outbound static IPs that you can allow at a firewall as a second layer.

> **Make Opsgenie webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) checks your custom header on every request, stores each alert action durably, and retries delivery to your service, so a receiver outage doesn't leave your ticketing tool out of step with on-call.

## Opsgenie webhook limitations and pain points

### A product with an end date

**The Problem:** Any Opsgenie webhook integration you build stops working when Opsgenie shuts down.

**Why It Happens:** Opsgenie's end of support date is April 5, 2027, and Atlassian is moving customers to Jira Service Management. Integrations left in Opsgenie stop working when it's turned off, which can happen earlier for a customer who has migrated.

**Workarounds:**

* Keep alert-handling logic independent of Opsgenie's payload format.
* Plan the replacement integration in Jira Service Management alongside your migration.

**How Hookdeck Can Help:** Hookdeck sits between the sender and your service. When you move to a new alerting source, you can point it at a new Hookdeck source and use [transformations](/docs/transformations) to reshape its payloads, keeping one stable format for your handler during the switch.

### No documented retries

**The Problem:** If your endpoint is down when an alert is acknowledged or closed, you may never get that update.

**Why It Happens:** The Webhook integration page documents no retry policy, timeout or expected response.

**Workarounds:**

* Keep the receiver fast and available, and return `200` quickly.
* Reconcile open alerts from the Opsgenie API after an outage.

**How Hookdeck Can Help:** Hookdeck accepts each request as it arrives, stores it, and delivers to your service with [automatic retries](/docs/retries) on a schedule you control. [Issues](/docs/issues) alert you when deliveries fail, and you can replay any stored alert action.

### No signature on the request

**The Problem:** A forged request with the right header value looks identical to a real alert action.

**Why It Happens:** Custom headers are the only authentication the integration offers.

**Workarounds:**

* Use a long random header value and compare it in constant time.
* Look up the alert through the Opsgenie API before taking a destructive action.

**How Hookdeck Can Help:** Hookdeck's Opsgenie source compares the header you name against the exact value you enter. A request without a matching header still gets a 200 response, but Hookdeck marks it as failed verification and creates no event, so it never reaches your destination. Every request is stored with its headers and body.

### Ordering and missing context

**The Problem:** Actions for one alert can arrive close together, and most of them don't carry the description or details.

**Why It Happens:** Each action is its own request. Description and details are only sent for `Create` and custom actions, and are cut to 1,000 characters.

**Workarounds:**

* Key every record on `alert.alertId` and apply actions as state changes.
* Fetch the full alert from the API when you need the description or details.

**How Hookdeck Can Help:** [Filters](/docs/filters) can route actions by `action` to different destinations, for example sending `Create` and `Close` to your ticketing integration and everything else to an audit log.

## Best practices

### Map only the actions you need

Each mapping sends traffic your receiver has to handle. Start with `Create`, `Acknowledge` and `Close`, and add others as you use them.

### Key on `alertId`

Use `alert.alertId` to tie actions together. `tinyId` and `alias` are handy for display, but `alertId` is the stable identifier.

### Handle custom and unknown actions

Custom actions arrive with their own names. Acknowledge anything you don't recognise with a `200` and log it, rather than failing.

### Acknowledge fast and make handlers idempotent

Return `200` once the header checks out, process asynchronously, and build a deduplication key from `alertId`, `action` and `updatedAt` stored as a string. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks) and our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

## Conclusion

The Opsgenie Webhook integration posts unsigned JSON for each mapped alert action, with the action name in `action` and a subset of the alert in `alert`. Authenticate it with a custom header compared in constant time over HTTPS, key your records on `alertId`, and watch the mixed timestamp units. Above all, plan for April 5, 2027, when Opsgenie's support ends and the integration stops.

[Hookdeck Event Gateway](/event-gateway) checks the header, stores every alert action and retries delivery to your service, and gives you one place to reshape payloads when you move to a new alerting source.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Opsgenie webhooks reliably in minutes.