Integration

Send emails from Nuxt with Postkit

Send transactional emails from Nuxt 3 server routes using Postkit's REST API. Nitro handles the server-side fetch.

1. Set your API key

# .env
POSTKIT_API_KEY=pk_live_...

2. Send an email

typescript
// server/api/send.post.ts
export default defineEventHandler(async (event) => {
  const { to, name } = await readBody(event);
  const config = useRuntimeConfig();

  const data = await $fetch("https://api.postkit.eu/v1/emails", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${config.postkitApiKey}`,
      "Content-Type": "application/json",
    },
    body: {
      from: "hello@yourapp.eu",
      to,
      subject: "Welcome aboard!",
      html: `<h1>Welcome, ${name}!</h1>`,
    },
  });

  return data;
});

3. Handle webhooks

Postkit sends delivery events (sent, delivered, bounced, opened, clicked) via HMAC-SHA256 signed webhooks following the Standard Webhooks specification.

typescript
// server/api/webhooks/postkit.post.ts
import crypto from "crypto";

export default defineEventHandler(async (event) => {
  const body = await readRawBody(event) ?? "";
  const headers = getHeaders(event);
  const msgId = headers["webhook-id"] ?? "";
  const timestamp = headers["webhook-timestamp"] ?? "";
  const signature = headers["webhook-signature"] ?? "";
  const config = useRuntimeConfig();

  const secret = Buffer.from(
    config.postkitWebhookSecret.replace("whsec_", ""),
    "base64"
  );
  const content = `${msgId}.${timestamp}.${body}`;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(content)
    .digest("base64");

  const valid = signature.split(" ").some((sig) => {
    const val = sig.replace(/^v1,/, "");
    return crypto.timingSafeEqual(
      Buffer.from(val, "base64"),
      Buffer.from(expected, "base64")
    );
  });

  if (!valid) throw createError({ statusCode: 401, message: "Invalid signature" });

  const data = JSON.parse(body);
  console.log("Postkit event:", data.type);
  return { received: true };
});

Start sending in under 5 minutes

Free plan. No credit card required.

Get Started Free