Outbound webhooks
Outbound webhooks let Plume notify an external service over HTTP when something happens. Unlike the Go hook system, webhooks need no code — you configure an endpoint in the UI (or via the API) and Plume POSTs JSON to it.
Configuring an endpoint
Section titled “Configuring an endpoint”Add a webhook endpoint in the app, or manage endpoints through the REST API at /api/webhooks. Each endpoint has a URL, the events it subscribes to, and a signing secret used to authenticate deliveries.
How delivery works
Section titled “How delivery works”When a subscribed event fires, Plume sends a POST with a JSON body to your URL. Two headers describe the delivery:
X-Plume-Event— the event name (for example,campaign.sent).X-Plume-Signature— an HMAC-SHA256 signature of the raw request body, keyed with the endpoint’s signing secret.
Event catalog
Section titled “Event catalog”| Event | Fires when |
|---|---|
subscriber.created |
A subscriber is created |
subscriber.confirmed |
A subscriber confirms double opt-in |
campaign.sent |
A campaign finishes sending |
Verifying the signature
Section titled “Verifying the signature”Compute the HMAC-SHA256 of the raw request body using your endpoint’s signing secret, then compare it to the X-Plume-Signature header. Use a constant-time comparison.
import crypto from "node:crypto";
function verify(rawBody, signature, secret) { const expected = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature), );}
app.post("/plume-webhook", (req, res) => { if (!verify(req.rawBody, req.get("X-Plume-Signature"), SECRET)) { return res.sendStatus(401); } const event = req.get("X-Plume-Event"); // handle event... res.sendStatus(200);});Always verify the signature before trusting a payload — it’s the only thing proving the request came from your Plume install.