Entylink
Tutorial

Verify Entylink webhook signatures in Node.js

Implement secure webhook verification in Node.js using Entylink's `X-Entylink-Signature` header, the raw request body, and the signing secret returned when the webhook is created.

verify entylink webhook signature node jswebhook signature verification tutorialhmac webhook verification node jscompany monitoring webhook security
Guide details
Read time
10 min
Estimated for an engineer implementing the flow
Level
Advanced
Assumes comfort with APIs and backend basics
Intent
Implementation
Real product or compliance workflows
Who this is for

Start from the real implementation problem

Target readers
Backend engineers receiving Entylink webhooksPlatform teams securing change-event ingestionDevelopers replacing insecure unsigned webhook handlers
01Read the raw body before parsing JSON
02Recompute the HMAC SHA-256 signature
03Reject invalid webhook deliveries safely
Prerequisites

Keep the setup explicit

01

A webhook created through `/v1/webhooks`

02

The `secret` value returned at creation time

03

A route handler that can read the raw request body

Step by step
Step 01

Store the signing secret when the webhook is created

The webhook creation response includes a one-time `secret`. Store it immediately in your own secrets manager or configuration store because you need it to verify future deliveries.

Step 02

Read the raw request body, not a re-serialized object

The signature is calculated over the exact JSON body string. If you parse and re-stringify the payload first, verification can fail.

Next.js route handlerExample
import { createHmac, timingSafeEqual } from "crypto";

export async function POST(request: Request) {
  const signature = request.headers.get("x-entylink-signature") || "";
  const body = await request.text();

  const expected = `sha256=${createHmac(
    "sha256",
    process.env.ENTYLINK_WEBHOOK_SECRET!,
  ).update(body).digest("hex")}`;

  const isValid =
    signature.length === expected.length &&
    timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!isValid) {
    return new Response("invalid signature", { status: 401 });
  }

  const payload = JSON.parse(body);
  return Response.json({ ok: true, event: payload.event });
}
Step 03

Use the event header to route logic cleanly

Entylink includes `X-Entylink-Event` so you can branch cleanly by event type such as `filing.new`, `officer.appointed`, or `company.status_changed`.

Step 04

Make the handler idempotent

Your webhook consumer should tolerate duplicate deliveries or replay logic. Persist a delivery identifier or event fingerprint before triggering downstream work.