Send email with Express
Send from a handler, and receive delivery events on a second route.
You need a verified sending domain and an API key from Settings › API Keys. If neither exists yet, the quickstart covers both in a couple of minutes.
Install
npm i sending-sdk expressSend
const express = require("express");
const { Sending } = require("sending-sdk");
const app = express();
const sending = new Sending({ apiKey: process.env.SENDING_API_KEY });
app.post("/signup", express.json(), async (req, res) => {
const { id } = await sending.emails.send({
from: "Acme <[email protected]>",
to: req.body.email,
subject: "Welcome",
html: "<p>You're in.</p>",
idempotencyKey: `welcome-${req.body.userId}`,
});
res.json({ id });
});
// Webhooks need the RAW body to verify the signature.
app.post("/hooks/sending", express.raw({ type: "application/json" }), (req, res) => {
const raw = req.body.toString("utf8");
// verify(raw, req.get("Sending-Signature"), process.env.WEBHOOK_SECRET)
res.sendStatus(200);
});A successful call answers 202 with the message id: accepted and queued, not yet delivered. Delivery, opens, bounces and complaints arrive later, on webhooks or in the dashboard.
What changes in Express
Two routes, two body parsers. Sending uses JSON everywhere, but signature verification has to run on the exact bytes we sent.
The thing that catches people
express.json() on the webhook route breaks verification: it reparses and re-serialises the payload, so key order and spacing change and the HMAC no longer matches. Use express.raw() there.
Next
- All the fields: cc and bcc, reply-to, attachments, templates, scheduling.
- Webhooks: delivery, bounce and complaint events, signed with HMAC.
- Domains: SPF, DKIM and MAIL FROM, and why sending is refused until they are in place.
- MCP: the same operations as tools, when the one writing the code is an agent.