npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kontent-ai/webhook-helper

v4.0.0

Published

This utility helps with webhook notifications from Kontent.ai

Readme

npm version Build

Kontent.ai Webhook helper

This package aims to help you with Webhooks received from Kontent.ai projects. It provides:

  • Runtime validation and parsing of webhook payloads with Zod
  • Type-safe notification handling with TypeScript discriminated unions
  • Signature verification

Installation

Install package:

npm install @kontent-ai/webhook-helper

pnpm add @kontent-ai/webhook-helper

Quick Start

Create a reusable function to parse and validate webhook requests:

import { parseSignedWebhookResponse, SIGNATURE_HEADER, ParseResult, WebhookResponse } from '@kontent-ai/webhook-helper';

const parseWebhookRequest = async (
  request: Request,
  secret: string,
): Promise<ParseResult<WebhookResponse>> => {
  const signature = request.headers.get(SIGNATURE_HEADER) ?? "";
  const payload = await request.text();

  return parseSignedWebhookResponse({
    payload,
    secret,
    signature,
  });
};

const handleWebhook = async (request: Request) => {
  const result = await parseWebhookRequest(request, 'your-webhook-secret');

  if (!result.success) {
    return new Response(result.error.message, { status: 401 });
  }

  result.data.notifications.forEach(notification => {
    if (notification.object_type === 'content_item') {
      console.log('Content item:', notification.data.system.name);
    }
  });

  return new Response('OK', { status: 200 });
};

Parsing & Validating Webhooks

The library provides two main parsing functions:

Parse with signature verification

import { parseSignedWebhookResponse } from '@kontent-ai/webhook-helper';

const result = parseSignedWebhookResponse({payload, secret, signature});

if (!result.success) {
  console.error('Validation failed:', result.error);
  return;
}

const webhookData = result.data;

Parse without signature verification

import { parseWebhookResponse } from '@kontent-ai/webhook-helper';

const result = parseWebhookResponse(body);

if (!result.success) {
  console.error('Parsing failed:', result.error);
  return;
}

const webhookData = result.data;

Type-Safe Notification Handling

Each notification has an object_type discriminator that enables type narrowing:

const result = parseWebhookResponse(body);

if (result.success) {
  result.data.notifications.forEach(notification => {
    switch (notification.object_type) {
      case 'content_item':
        console.log('Workflow step:', notification.data.system.workflow_step);
        console.log('Action:', notification.message.action);
        break;
      case 'asset':
        console.log('Asset ID:', notification.data.system.id);
        console.log('Action:', notification.message.action);
        break;
      case 'content_type':
        console.log('Type codename:', notification.data.system.codename);
        break;
      case 'language':
        console.log('Language codename:', notification.data.system.codename);
        break;
      case 'taxonomy':
        console.log('Taxonomy codename:', notification.data.system.codename);
        break;
      case 'unknown':
        console.warn('Unknown notification type:', notification.original_notification);
        break;
    }
  });
}

Notification Structure & object_type

To enable the type-safe discrimination shown above, this library "uplifts" the object_type property to the top level of the notification object. This is a convenience helper that creates a discriminated union, allowing TypeScript to automatically narrow the types.

This means the parsed notification object is a slight superset of the original Kontent.ai webhook payload.

If you need to strictly match the original notification shape (without the top-level object_type), you can use the Omit utility type:

import { AssetNotification } from '@kontent-ai/webhook-helper';

// Removes the helper 'object_type' property to match the raw payload structure
type RawAssetNotification = Omit<AssetNotification, "object_type">;

Handling Unknown Webhooks

The library is forward-compatible with future webhook types. When a notification doesn't match any known schema, it's typed as UnknownNotification:

type UnknownNotification = {
  object_type: "unknown";
  original_notification: Record<PropertyKey, unknown>;
};

This allows you to:

  • Access raw webhook data via original_notification
  • Handle future webhook types without breaking your application
  • Use exhaustive type checking in switch statements
result.data.notifications.forEach(notification => {
  if (notification.object_type === 'unknown') {
    console.log('Unrecognized webhook:', notification.original_notification);
  }
});

Signature Verification

If you need to verify signatures separately from parsing, use isSignatureValid():

import { isSignatureValid, replaceLinebreaks, SIGNATURE_HEADER } from '@kontent-ai/webhook-helper';

const verifyWebhookSignature = async (request: Request, secret: string): Promise<boolean> => {
  const signature = request.headers.get(SIGNATURE_HEADER);
  const payload = await request.text();

  return isSignatureValid({payload, secret, signature});
}

if (!await verifyWebhookSignature(request, 'your-webhook-secret')) {
  throw new Error('Invalid signature');
}

The payload must be exactly the same (including whitespaces) as the original webhook body. If you've parsed the payload into an object, you can reconstruct it:

const payload = JSON.stringify(jsonPayload, null, 2);

The replaceLinebreaks() function normalizes line endings to prevent signature mismatches caused by Windows line breaks:

import { replaceLinebreaks } from '@kontent-ai/webhook-helper';

const normalizedPayload = replaceLinebreaks(payload);