Agent skill

Opsgenie Webhooks Skill

Receive and authenticate Opsgenie webhooks. Use when setting up an Opsgenie Webhook integration endpoint, checking the secret custom header, debugging 401s, or handling alert actions like Create, Acknowledge, Close, AddNote and Escalate.

Install this skill

npx skills add hookdeck/webhook-skills --skill 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)

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/, examples/nextjs/, examples/fastapi/.

Alert Actions

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

actionSent WhenExtra alert fields
CreateAn alert is createdteams, recipients
AcknowledgeAn alert is acknowledged
UnAcknowledgeAn acknowledgement is removed
AddNoteA note is addednote
AddTagsTags are addedaddedTags
RemoveTagsTags are removedremovedTags
CloseAn alert is closed
AssignOwnershipAn owner is assignedowner
TakeOwnershipA user takes ownership
DeleteAn alert is deleted
EscalateAn alert is escalatedtop-level escalationName, escalationNotify; alert has only alertId
UpdatePriorityPriority changespriority, oldPriority
UpdateDescriptionDescription changesdescription
UpdateMessageMessage changesmessage
AddTeamA team is addedteam
AddRecipientA recipient is addedrecipient
your own nameA custom action runsSent as action, e.g. TestAction

Environment Variables

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):

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

Use the printed URL as the webhook URL in Opsgenie.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 4, 2026

View on GitHub →