Integration

Send emails from SvelteKit with Postkit

Send transactional emails from SvelteKit server endpoints and form actions. Works with any adapter.

1. Set your API key

# .env
POSTKIT_API_KEY=pk_live_...

2. Send an email

typescript
// src/routes/api/send/+server.ts
import { POSTKIT_API_KEY } from "$env/static/private";
import { json } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";

export const POST: RequestHandler = async ({ request }) => {
  const { to, name } = await request.json();

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

  return json(await res.json());
};

3. Handle webhooks

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

typescript
// src/routes/webhooks/postkit/+server.ts
import { POSTKIT_WEBHOOK_SECRET } from "$env/static/private";
import { json, error } from "@sveltejs/kit";
import crypto from "crypto";
import type { RequestHandler } from "./$types";

export const POST: RequestHandler = async ({ request }) => {
  const body = await request.text();
  const msgId = request.headers.get("webhook-id") ?? "";
  const timestamp = request.headers.get("webhook-timestamp") ?? "";
  const signature = request.headers.get("webhook-signature") ?? "";

  const secret = Buffer.from(
    POSTKIT_WEBHOOK_SECRET.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 error(401, "Invalid signature");

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

Start sending in under 5 minutes

Free plan. No credit card required.

Get Started Free