OpenSES

Webhook Verification

Verify that incoming OpenSES webhook payloads are authentic.

OpenSES signs the raw request body with HMAC-SHA256 and sends the result in X-OpenSES-Signature.

Next.js App Router

import { verifyWebhook } from '@openses/openses';

export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get('x-openses-signature') ?? '';

  const { data } = verifyWebhook({
    payload: rawBody,
    signature,
    secret: process.env.OPENSES_WEBHOOK_SECRET!,
  });

  console.log(data.type);
  console.log(data.data.email_id);

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

Express

import express from 'express';
import { verifyWebhook } from '@openses/openses';

const app = express();

app.post(
  '/webhooks/openses',
  express.raw({ type: 'application/json' }),
  (request, response) => {
    const { data } = verifyWebhook({
      payload: request.body.toString('utf8'),
      signature: request.header('x-openses-signature') ?? '',
      secret: process.env.OPENSES_WEBHOOK_SECRET!,
    });

    console.log(data.type);
    response.sendStatus(200);
  }
);

Do not call express.json() before verification on this route. Parsing and re-serializing JSON changes the signed bytes.

Payload shape

type WebhookPayload = {
  type: `email.${string}`;
  created_at: string;
  data: {
    email_id: string;
    from: string;
    to: string[];
    subject: string;
    status: string;
    event: {
      type: string;
      created_at: string;
      metadata?: Record<string, unknown>;
    };
  };
};