Skip to main content

Import

import { webhookMiddleware, verifyWebhookSignature, verifyAndParseWebhook } from '@keystoneos/node';

Express Middleware

The easiest way to handle webhooks in Express:
import express from 'express';
import { webhookMiddleware } from '@keystoneos/node';

app.post(
  '/webhooks/keystone',
  express.raw({ type: 'application/json' }),
  webhookMiddleware({ secret: process.env.WEBHOOK_SECRET }),
  (req, res) => {
    const { event, data } = req.keystoneEvent;

    switch (event) {
      case 'settlement.state.compliance_cleared':
        console.log(`${data.settlement_id}: ${data.from_state} -> ${data.to_state}`);
        break;
      case 'settlement.state.finalized':
        console.log(`Finalized: ${data.settlement_id}`);
        break;
      case 'settlement.state.rolled_back':
        console.log(`Rolled back: ${data.settlement_id}`);
        break;
    }

    res.sendStatus(200);
  },
);
You must use express.raw() (not express.json()) so the middleware receives the raw body for signature verification.

Parameters

PropertyTypeRequiredDescription
secretstringYesYour webhook signing secret.

Behavior

  • Reads X-Keystone-Signature header
  • Verifies HMAC-SHA256 signature using timing-safe comparison
  • Parses the JSON body
  • Attaches parsed event to req.keystoneEvent
  • Returns 401 if signature is invalid or missing

Manual Verification

For non-Express frameworks (Hono, Fastify, serverless), use the lower-level functions:

verifyAndParseWebhook

import { verifyAndParseWebhook } from '@keystoneos/node';

// Hono example
app.post('/webhooks/keystone', async (c) => {
  const body = await c.req.text();
  const signature = c.req.header('x-keystone-signature');

  const event = verifyAndParseWebhook(body, signature, process.env.WEBHOOK_SECRET);
  if (!event) {
    return c.json({ error: 'Invalid signature' }, 401);
  }

  // Handle event...
  return c.json({ ok: true });
});
ParameterTypeRequiredDescription
payloadstring | BufferYesRaw request body.
signaturestring | undefinedYesThe X-Keystone-Signature header.
secretstringYesYour webhook signing secret.
Returns the parsed WebhookEvent or null if verification fails.

verifyWebhookSignature

Signature verification only, without JSON parsing:
import { verifyWebhookSignature } from '@keystoneos/node';

const isValid = verifyWebhookSignature(rawBody, signature, secret);
Returns true if the signature is valid.

WebhookEvent Type

interface WebhookEvent {
  event: string;
  data: Record<string, unknown>;
}

Common Events

EventDescription
settlement.state.*Any settlement state change (use wildcard pattern).
settlement.state.finalizedSettlement completed successfully.
settlement.state.rolled_backSettlement rolled back.
compliance.clearedAll parties passed compliance.
compliance.flaggedA party was flagged for manual review.
test.pingTest event from webhook endpoint testing.