> ## Documentation Index
> Fetch the complete documentation index at: https://docs.keystoneos.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Middleware

> Verify webhook signatures and parse events server-side.

## Import

```typescript theme={null}
import { webhookMiddleware, verifyWebhookSignature, verifyAndParseWebhook } from '@keystoneos/node';
```

## Express Middleware

The easiest way to handle webhooks in Express:

```typescript theme={null}
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.awaiting_deposits':
        console.log(`${data.settlement_id}: ${data.state} - trigger deposits`);
        break;
      case 'settlement.state.finalized':
        console.log(`Finalized: ${data.settlement_id}`);
        break;
      case 'settlement.state.rejected':
        console.log(`Rejected: ${data.settlement_id}`);
        break;
    }

    res.sendStatus(200);
  },
);
```

<Note>
  You must use `express.raw()` (not `express.json()`) so the middleware receives the raw body for signature verification.
</Note>

### Parameters

| Property | Type     | Required | Description                  |
| -------- | -------- | -------- | ---------------------------- |
| `secret` | `string` | Yes      | Your 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

```typescript theme={null}
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 });
});
```

| Parameter   | Type                  | Required | Description                        |
| ----------- | --------------------- | -------- | ---------------------------------- |
| `payload`   | `string \| Buffer`    | Yes      | Raw request body.                  |
| `signature` | `string \| undefined` | Yes      | The `X-Keystone-Signature` header. |
| `secret`    | `string`              | Yes      | Your webhook signing secret.       |

Returns the parsed `WebhookEvent` or `null` if verification fails.

### verifyWebhookSignature

Signature verification only, without JSON parsing:

```typescript theme={null}
import { verifyWebhookSignature } from '@keystoneos/node';

const isValid = verifyWebhookSignature(rawBody, signature, secret);
```

Returns `true` if the signature is valid.

## WebhookEvent Type

```typescript theme={null}
interface WebhookEvent {
  event: string;
  data: Record<string, unknown>;
}
```

## Common Events

| Event                                | Description                                        |
| ------------------------------------ | -------------------------------------------------- |
| `settlement.state.*`                 | Any settlement state event (use wildcard pattern). |
| `settlement.state.awaiting_deposits` | Compliance passed; escrow ready for deposits.      |
| `settlement.state.finalized`         | Settlement completed successfully.                 |
| `settlement.state.rejected`          | Compliance screening failed; settlement rejected.  |
| `test.ping`                          | Test event from webhook endpoint testing.          |

See the [webhook event catalog](/guides/webhook-events) for the full list and which states emit events.
