Raccoon Docs

Webhooks

Send issue, threshold and silence events to Slack, an incident tool or your own endpoint, signed so you can verify them.

A webhook posts JSON to a URL you choose when something happens in an application. Add one under Settings → Issues → Webhooks. Admins and owners can manage webhooks.

Create a webhook#

  1. Give it a name and the URL to post to (http or https).
  2. Choose its events. All of them are selected by default.
  3. Save it. Raccoon shows the signing secret once, so copy it then.

Send test posts a test event right away and shows the response. Each webhook's page logs every delivery with its status code, payload and response, filtered by Successful or Failed. The webhook list shows a warning when the last delivery failed.

Events#

Event Sent when
issue.created An issue opens. A new performance issue also sends threshold.exceeded.
issue.reopened A resolved issue happens again.
issue.resolved An issue is resolved, by anyone, in bulk, through the MCP server or on deploy.
issue.assigned An issue is assigned to someone.
threshold.exceeded A route, job, command or scheduled task goes over its threshold.
environment.silent An environment with a silence alert stops sending events.
environment.recovered That environment sends events again.

Payload#

Every delivery is a POST with a JSON body:

{
    "id": "0c3f7c1e-2f3a-4c1b-9d2e-5b6a7c8d9e0f",
    "event": "issue.created",
    "created_at": "2026-11-03T09:41:00Z",
    "data": {
        "application": { "id": "9e5c1a52-8f0e-4b8e-a1c4-2f6d3b7e9a10", "name": "Shop" },
        "environment": { "id": "9e5c1a52-93b1-4d27-b8f5-6a0c4e2d1f37", "name": "Production" },
        "issue": {
            "id": 81,
            "ref": 12,
            "type": "exception",
            "issueable_type": null,
            "group": "5d1c...",
            "title": "TypeError",
            "subtitle": "Cannot read properties of undefined (reading 'total')",
            "status": "open",
            "priority": 0,
            "occurrences": 1,
            "first_seen_at": "2026-11-03T09:40:58Z",
            "last_seen_at": "2026-11-03T09:40:58Z",
            "url": "https://raccoon.watch/environments/9e5c1a52-93b1-4d27-b8f5-6a0c4e2d1f37/issues/12"
        }
    }
}
  • issue.assigned adds assignee with id, name and email.
  • threshold.exceeded adds threshold with type, name, threshold_ms and p95_ms.
  • environment.silent and environment.recovered have application, environment (with silence_alert_minutes), silent_since, recovered_at (null while silent) and url, and no issue.

The request also has the headers X-Raccoon-Event, X-Raccoon-Signature and User-Agent: Raccoon-Webhooks/1.0.

Verify the signature#

X-Raccoon-Signature is sha256= followed by the hex HMAC-SHA256 of the raw request body, keyed with the webhook's secret. Compute it over the body exactly as received, before parsing the JSON, and compare in constant time:

import { createHmac, timingSafeEqual } from 'node:crypto';

const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
const valid = expected.length === signature.length && timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
$valid = hash_equals('sha256='.hash_hmac('sha256', $request->getContent(), $secret), $request->header('X-Raccoon-Signature'));

Rotate secret on the webhook's page makes a new secret. The old one stops working immediately.

Retries#

Raccoon waits up to 10 seconds for a response. A delivery fails when there's no response or the status is 400 or above. A failed delivery is retried three more times, after 10 seconds, 1 minute and 5 minutes. Disabling or deleting the webhook cancels pending retries.