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 };
});