> Check the X-Signature-256 header on RestoreCord webhook events with HMAC-SHA256 over the raw body, route events by name and design for best-effort delivery.

Source: https://restorecord.dev/webhook-signature-verification

# Verifying RestoreCord webhook signatures in Node.js

Signed webhook events let your systems react to verifications, firewall decisions and snapshots in real time. The signature check is what stops anyone else from posting fake ones.

[Get Enterprise events](https://restorecord.com/pricing)[Webhook events docs](https://restorecord.com/docs/webhooks)

Short answer

Read the raw request body, compute an HMAC-SHA256 with your webhook secret, strip any sha256= prefix from the X-Signature-256 header and compare the two in constant time. Reject the request if they differ, otherwise return 2xx and queue the event.

## Register your endpoint

Webhook events are an Enterprise feature and are separate from the webhook logs that post into a Discord channel. Set the receiving URL in server settings or through the API with a `WRITE_SERVERS` key, then generate the webhook secret from the dashboard:

http

```
PATCH /api/v3/servers/{serverId}
Content-Type: application/json

{ "webhookUrl": "https://example.com/restorecord/events" }
```

## What each request carries

| Part                   | Contents                                                              |
| ---------------------- | --------------------------------------------------------------------- |
| Content-Type header    | application/json                                                      |
| X-Webhook-Event header | The event name, for example member.verified                           |
| X-Signature-256 header | HMAC-SHA256 of the raw body, keyed with your secret                   |
| Body                   | event, timestamp, server (when server-scoped) and event-specific data |

## A receiver that verifies first

The one rule that matters: hash the bytes you received, not a re-serialised object. With Express, capture the raw body on this route only:

receiver.jsjs

```
const crypto = require("node:crypto");
const express = require("express");

const app = express();
const secret = process.env.RESTORECORD_WEBHOOK_SECRET;

function isSigned(raw, header) {
  if (!header) return false;
  const expected = crypto.createHmac("sha256", secret).update(raw).digest();
  const received = Buffer.from(header.replace(/^sha256=/, ""), "hex");
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

app.post("/restorecord/events", express.raw({ type: "application/json" }), (req, res) => {
  if (!isSigned(req.body, req.get("X-Signature-256"))) return res.sendStatus(401);
  const event = JSON.parse(req.body.toString("utf8"));
  queue.push(event); // your own job queue
  res.sendStatus(204);
});
```

Global JSON body parsers run before your handler and throw away the raw bytes. Mount the raw parser on the webhook route, or the signature will never match.

## Events worth routing

* member.verified: add the member to your CRM or welcome flow.
* member.denied: reason tells you why, such as vpn, alt, blacklist or account\_age.
* firewall.block, firewall.allow and firewall.password: feed a moderation dashboard.
* snapshot.create and snapshot.restore: keep an audit trail of backups.

## Design for best-effort delivery

RestoreCord sends each event once and does not retry non-2xx responses. Delivery never holds up the verification or snapshot that caused it. That means three things for you: acknowledge fast and do slow work from a queue, make handlers **idempotent** because a duplicate can still arrive, and accept nulls and unknown fields, since sensitive values like IP depend on server settings and plan.

Your receiver will also need a key to call back into the API. Scope it with [the API key guide](https://restorecord.dev/api-key-scopes), or return to the [developer overview](https://restorecord.dev/).

## Wire up signed events

1. 1  
### Stand up an HTTPS receiver  
Accept JSON POST requests and keep the raw request body available before any parsing.
2. 2  
### Register the URL and secret  
Set webhookUrl on the server and generate the webhook secret, storing it next to your other credentials.
3. 3  
### Verify, then enqueue  
Check X-Signature-256 in constant time, return 2xx and hand the event to a queue.
4. 4  
### Route on the event name  
Use X-Webhook-Event to dispatch handlers, and ignore unknown fields instead of failing.

## Webhook event questions

### 

### 

### 

## Let RestoreCord tell your systems first

Enterprise servers stream signed member, firewall and snapshot events to any HTTPS endpoint you run.

[Compare plans](https://restorecord.com/pricing)
