Skip to content

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.

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.

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 Fires when
subscriber.created A subscriber is created
subscriber.confirmed A subscriber confirms double opt-in
campaign.sent A campaign finishes sending

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.