Receive delivery webhooks in Next.js

Handle Publiq delivery events (delivered, opened, bounced) in a Route Handler, verifying the HMAC signature.

Read the raw body to verify the signature in the Publiq-Signature header before trusting the event:

import { NextResponse } from 'next/server';
import crypto from 'node:crypto';

const secret = process.env.PUBLIQ_WEBHOOK_SECRET!;

export async function POST(req: Request) {
  const raw = await req.text();
  const sig = req.headers.get('Publiq-Signature') ?? '';
  const expected = crypto.createHmac('sha256', secret).update(raw).digest('hex');
  if (sig !== expected) return new NextResponse('invalid signature', { status: 401 });

  const event = JSON.parse(raw);
  // event.type: 'message.delivered' | 'message.opened' | 'message.bounced' ...
  console.log(event.type, event.data.message_id);
  return NextResponse.json({ ok: true });
}

Register the webhook URL in the dashboard (or via SDK: Webhooks). The secret is shown only once on creation.

Best practices

  • Always call Publiq from the server — never expose the API key on the client.
  • Create the client instance once and reuse it across requests.
  • Prefer templateKey over inline HTML to keep content versioned.
  • Handle PubliqError (status/code); the SDK already retries transient errors with backoff.
  • Send from a verified domain — see Domains.

See also

Receive delivery webhooks in Next.js — Publiq Docs