Gareth Wilson Gareth Wilson

Guide to Honeybadger Webhooks: Features and Best Practices

Published


Honeybadger webhooks tell your systems when something changes in a monitored project: an error occurs or comes back after being resolved, an uptime check goes down, a scheduled job misses its check-in, or a deploy is recorded. If you use Honeybadger for error and uptime monitoring, webhooks are how those alerts reach your own incident tooling, ticket queues and dashboards.

This guide covers how Honeybadger's Webhook integration is set up and authenticated, the 13 events and their payloads, what Honeybadger leaves undocumented, and the best practices for running a receiver in production.

What are Honeybadger webhooks?

Each Honeybadger project can have a Webhook integration that posts a JSON payload to a URL you choose. Every payload has an event field naming what happened and a human-readable message, plus objects that depend on the event.

Honeybadger does not sign webhook payloads. Instead, every outbound request from Honeybadger's servers (webhooks, source map fetches and uptime checks) carries a Honeybadger-Token header: a secret token derived from the project's API key. Your endpoint authenticates the request by comparing that header with the token it has stored.

Honeybadger webhook features

FeatureDetails
ConfigurationProject Settings > Alerts & Integrations > Webhook, for users with admin rights on the project
AuthenticationHoneybadger-Token: <token>, sent raw with no prefix
Where the token livesThe API Key tab of the project settings
Token rotationThe token changes when the project API key is reset
Optional headerAuthorization: Bearer <value>, if you fill in the integration's Bearer token field
Body signatureNone
RoutingThe body's event field
Events13
Event IDNone documented
Retries and timeoutNot documented
IP allowlistNot available. Honeybadger uses a dynamic range for outbound requests

Common events

EventSent whenTop-level properties
occurredAn error occursevent, message, project, fault, notice
resolvedAn error is marked as resolvedevent, message, project, fault
unresolvedA resolved error occurs againevent, message, project, fault
assignedAn error is assigned to a userevent, message, actor, fault, assignee
commentedA comment is added to an errorevent, message, actor, fault, comment
rate_exceededAn error rate threshold is exceededevent, message, project, fault
volume_spikeA project's error volume spikes above its learned baselineevent, message, project, volume_spike
deployedA deployment is recordedevent, message, project, deploy
downAn uptime check failsevent, message, project, site, outage
upAn uptime check succeeds after being downevent, message, project, site, outage
cert_will_expireAn SSL certificate is about to expireevent, message, project, site
check_in_missingAn expected check-in is missingevent, message, project, check_in
check_in_reportingA check-in reports successfullyevent, message, project, check_in

Here is Honeybadger's check_in_missing example, trimmed:

{
  "event": "check_in_missing",
  "message": "[Voyager Test] MISSING: Voyager hasn't checked in for 4 years",
  "project": {
    "id": 67747,
    "name": "Voyager Test",
    "token": "abcd1234",
    "environments": [{ "id": 94721, "name": "development", "notifications": true }],
    "unresolved_fault_count": 3,
    "fault_count": 6
  },
  "check_in": {
    "id": "XYZLOL",
    "name": "Voyager",
    "state": "missing",
    "schedule_type": "simple",
    "reported_at": "2019-12-23T21:39:37.124397Z",
    "expected_at": "2023-10-30T19:42:28.853152Z",
    "missed_count": 33385,
    "report_period": "1 hour",
    "details_url": "https://app.honeybadger.io/projects/67747/check_ins"
  }
}

The project object is large in practice. It includes the project's environments, owner, users and counts, as well as a token field, so treat stored payloads as sensitive. The fault object on error events carries id, klass, environment, resolved, notices_count and last_notice_at, and down and up events include an outage with down_at and up_at.

Setting up Honeybadger webhooks

  1. Open the project in Honeybadger, go to Project Settings, and open the API Key tab. Copy the token shown there and store it server-side, for example as HONEYBADGER_TOKEN.
  2. Go to the Alerts & Integrations tab and choose the Webhook integration, below the list of personal alert integrations.
  3. Enter your URL, including the protocol, for example https://example.com/webhooks/honeybadger.
  4. Leave Bearer token blank unless something in front of your endpoint rejects requests without an Authorization header.
  5. Save.

If you receive Honeybadger webhooks through Hookdeck, enter the token from the API Key tab in the Hookdeck source. Hookdeck checks the Honeybadger-Token header, not the optional Authorization bearer token.

Honeybadger cannot reach localhost. For local development, hookdeck listen 3000 honeybadger --path /webhooks/honeybadger with the Hookdeck CLI gives you a public URL that forwards to your machine.

Securing Honeybadger webhooks

Read Honeybadger-Token, compare it with your stored token in constant time, and reject the request if it is missing or different. The header holds the raw token, so there is nothing to strip. Fail closed when the token is not configured.

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

function verifyHoneybadgerToken(headerValue, token) {
  if (typeof headerValue !== 'string' || !token) return false;
  const received = Buffer.from(headerValue, 'utf8');
  const expected = Buffer.from(token, 'utf8');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

const app = express();

app.post('/webhooks/honeybadger', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyHoneybadgerToken(req.get('honeybadger-token'), process.env.HONEYBADGER_TOKEN)) {
    return res.sendStatus(401);
  }

  const payload = JSON.parse(req.body.toString('utf8'));
  // Route on payload.event, then hand off to a queue.
  res.sendStatus(200);
});

The same check in Python:

import hmac
import os


def verify_honeybadger_token(header_value, token) -> bool:
    if not header_value or not token:
        return False
    return hmac.compare_digest(header_value.encode("utf-8"), token.encode("utf-8"))


# verify_honeybadger_token(request.headers.get("honeybadger-token"), os.environ["HONEYBADGER_TOKEN"])

If one endpoint serves several Honeybadger projects, keep a token per project, because each project's token is different.

Make Honeybadger webhooks production-ready. Hookdeck Event Gateway checks the Honeybadger-Token header on every request, keeps each error, uptime and check-in event in a durable queue, and lets you replay any of them into your incident tooling.

Honeybadger webhook limitations and pain points

Resetting the API key breaks every webhook

The Problem: All webhooks start failing authentication at once, and nothing changed in your code.

Why It Happens: Honeybadger-Token is derived from the project API key. Reset the key and the token changes, but your endpoint still holds the old one.

Workarounds:

  • Make "update the webhook token" part of your API key rotation runbook.
  • Alert on a run of authentication failures from Honeybadger, since a whole project failing together usually means a rotated key.

How Hookdeck Can Help: With a Hookdeck source, the token lives in one place. Update it there and every downstream service keeps working, and the requests rejected with VERIFICATION_FAILED in the meantime are visible in Hookdeck's request log.

A static token instead of a signature

The Problem: Anyone who obtains the token can send a payload that passes the check, and nothing protects the body from being changed in transit.

Why It Happens: Honeybadger sends the same token on every request rather than signing each body. The same token also goes out with source map fetches and uptime checks.

Workarounds:

  • Use an HTTPS endpoint and never log the token header.
  • Before acting on high-impact events, such as auto-resolving tickets, confirm the fault through the Honeybadger API.

How Hookdeck Can Help: Hookdeck checks the header before anything is queued for your service, so forged requests never reach your handlers, and you can keep the token out of your application entirely by verifying Hookdeck's own signature at your destination instead.

No documented retries

The Problem: If your endpoint is down when a down or check_in_missing alert fires, you do not know whether Honeybadger will try again.

Why It Happens: Honeybadger's webhook documentation states the method and body but no timeout, success criterion or retry policy.

Workarounds:

  • Keep the endpoint fast and highly available, and return 200 as soon as the token is checked.
  • For uptime and check-in alerts, keep Honeybadger's built-in email or chat alerts switched on as a second channel.

How Hookdeck Can Help: Hookdeck accepts each request as soon as it arrives and stores it, then delivers it to your service with automatic retries, so a deploy or outage on your side does not lose an alert.

No event ID for deduplication

The Problem: If the same alert arrives twice, your handler can open two tickets.

Why It Happens: Payloads carry event and the related objects, but no unique delivery or event identifier.

Workarounds:

  • Build a key from event and the object's own identity, such as fault.id with fault.notices_count, or site.id with outage.down_at.
  • Make ticket creation an upsert keyed on the fault or site.

How Hookdeck Can Help: Hookdeck's deduplication can drop repeated requests based on the fields you choose.

Best practices

Check Honeybadger-Token, not the bearer token

The Honeybadger-Token header is sent on every request. The bearer token is an optional extra for services that insist on an Authorization header. Verify the one Honeybadger always sends.

Route on event and accept new ones

Switch on the body's event. Log and acknowledge any event you do not recognise rather than returning an error, so a new event type never shows up as a failed delivery.

Acknowledge fast and process asynchronously

Return 200 once the token matches and the payload is stored, then create tickets or page people in the background. See why to process webhooks asynchronously.

Pair down with up, and check_in_missing with check_in_reporting

Use the matching recovery event to close what the failure event opened. Key incidents on site.id or check_in.id so the pair always finds each other. See our guide to webhook idempotency.

Conclusion

Honeybadger webhooks are unsigned JSON posts with an event field, authenticated by the Honeybadger-Token header that comes from the project's API key. Compare that header in constant time, update it whenever the API key is reset, route on event, and build your own deduplication key, because Honeybadger documents neither an event ID nor a retry policy.

Hookdeck Event Gateway checks the Honeybadger-Token header, stores every alert, and retries delivery to your service, so an outage on your side does not drop an error or downtime notification.

Get started with Hookdeck for free and handle Honeybadger 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.