A Worker that sends and receives
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Send: POST /send { to, subject, text }
if (url.pathname === "/send") {
const { to, subject, text } = await request.json();
return fetch("https://api.usemektup.com/v1/emails", {
method: "POST",
headers: { Authorization: `Bearer ${env.MEKTUP_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ from: "hello@yourapp.com", to, subject, text }),
});
}
// Receive: Mektup posts new mail here
if (url.pathname === "/inbound") {
const raw = await request.text();
if (!(await verify(raw, request.headers.get("X-Mektup-Signature"), env.MEKTUP_WEBHOOK_SECRET))) {
return new Response("bad signature", { status: 401 });
}
const { message } = JSON.parse(raw);
await env.MAIL_QUEUE.send(message); // or store it in D1, KV, R2
return new Response(null, { status: 200 });
}
return new Response("not found", { status: 404 });
},
};
async function verify(raw, header, secret) {
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret),
{ name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
const mac = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(raw));
const hex = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
return header === `sha256=${hex}`;
}Store MEKTUP_API_KEY and MEKTUP_WEBHOOK_SECRET as secrets, deploy, and set the mailbox webhook to https://your-worker.workers.dev/inbound.