Gareth Wilson Gareth Wilson

Guide to Opsgenie Webhooks: Features and Best Practices

Published


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

FeatureDetails
ConfigurationSettings > Integrations > Add integration > Webhook
Team scopeOptional Assignee team on the integration
MappingsOne "post to url" per alert action. Tag-added and custom-action mappings can be repeated with different tags or action names
MethodPOST with a JSON body
Payloadaction, an alert subset, source, integrationId, integrationName
Description and detailsOptional, truncated to 1,000 characters, only for Create and custom actions
AuthenticationCustom headers on the integration
SignatureNone
Retries and timeoutNot documented
LifecycleEnd of sale June 4, 2025. End of support April 5, 2027

Common events

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

actionFires whenExtra fields
CreateAn alert is createdalert.teams, alert.recipients
AcknowledgeAn alert is acknowledged
UnAcknowledgeAn acknowledgement is removed
AddNoteA note is addedalert.note
AddTagsTags are addedalert.addedTags
RemoveTagsTags are removedalert.removedTags
AddTeamA team is addedalert.team
AddRecipientA recipient is addedalert.recipient
AssignOwnershipAn owner is assignedalert.owner
TakeOwnershipA user takes ownership
EscalateAn alert is escalatedescalationId, escalationName, escalationNotify, escalationTime, repeatCount
UpdatePriorityThe priority changesalert.priority, alert.oldPriority
UpdateMessageThe message changesalert.message
UpdateDescriptionThe description changesalert.description
CloseAn alert is closed
DeleteAn 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:

{
  "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, 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: 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.

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:

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 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 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 on a schedule you control. 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 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 and our guide to 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 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 for free and handle Opsgenie webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.