Send from Express and know what happened after the 202. One accepted ID, one delivery trail.
Queue transactional mail without waiting on delivery, then use signed webhooks and product events to keep the rest of the lifecycle visible.
✓Server-side only. Mount the webhook route with a raw body parser.
One request, complete trace
Request
POST /api/v1/emails
Authorization: Bearer aem_…
Idempotency-Key: welcome-8f21
{ "to": "[email protected]", "template": "welcome" }
202 Accepted
{ "id": "msg_7ca31", "status": "queued" }
[ 01 / 03 ]Integration shape
Separate accepted requests from delivery outcomes.
Use an Express route or service module
A route or service can return the accepted response immediately; add your own queue only when local work needs durable retries.
Publish product events
Publish events after your application commits the change, so lifecycle flows reflect product truth rather than a browser action.
Verify the webhook
Mount a raw body parser on the webhook route only, so the signature still verifies.
[ 02 / 03 ]Server quickstart
Return an accepted email ID from an Express route.
The request is idempotent and server-side; delivery continues independently after the API responds.
Return the accepted message ID to application code and trace delivery separately.
app.post("/welcome", async (req, res) => {
const result = await fetch(EMAIL_URL, { method: "POST",
headers: authHeaders(`welcome-${req.body.userId}`),
body: JSON.stringify({ to: req.body.email, template: "welcome" }) });
res.status(result.status).json(await result.json());
});Accepted response
{ "id": "msg_7ca31", "status": "queued" }[ 03 / 03 ]Related
Keep building with Express
Questions people ask us
Close the loop on Express email delivery.
Queue a real message, return its ID, and verify the webhook that reports the outcome.