Integration

Send emails from Express with Postkit

Send transactional emails from Express.js middleware and route handlers. A single POST endpoint is all you need.

1. Set your API key

# .env
POSTKIT_API_KEY=pk_live_...

2. Send an email

typescript
// routes/email.js
import express from "express";
const router = express.Router();

router.post("/send", async (req, res) => {
  const { to, name } = req.body;

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

  const data = await response.json();
  res.json(data);
});

export default router;

3. Handle webhooks

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

typescript
// routes/webhooks.js
import express from "express";
import crypto from "crypto";

const router = express.Router();

router.post(
  "/postkit",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const body = req.body.toString();
    const signature = req.headers["webhook-signature"] ?? "";
    const timestamp = req.headers["webhook-timestamp"] ?? "";
    const msgId = req.headers["webhook-id"] ?? "";

    const secret = Buffer.from(
      process.env.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) return res.status(401).json({ error: "Invalid signature" });

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

export default router;

Start sending in under 5 minutes

Free plan. No credit card required.

Get Started Free