n8n 2.41.6 task runner and a Sume webhook verifier that never throws
n8n 2.41.6 keeps its task runner alive on unhandled rejections. Write the Sume webhook check so a bad signature returns false; answer 2xx only after storing.

Write the signature check for a Sume webhook as a function that returns true or false and never throws, and let the workflow decide what a false means. The n8n releases page lists 2.41.6 as stable and says it keeps the task runner alive on an unhandled rejection, which is a good safety net but a poor reason to let a malformed header raise an exception.
A webhook receiver sees hostile and broken input as a matter of course: missing headers, empty bodies, a timestamp that is not a number. The verifier should absorb all of it.
What the release page lists
Relevant items only.
| Release | Item |
|---|---|
| 2.41.6 stable | Task runner stays alive on unhandled rejection |
| 2.42.0 | Improved OpenTelemetry export |
| 2.42.0 | API workflow creation |
| 2.42.0 | Agent builder layout changes |
What Sume sends
A Sume job webhook is sent in mode: "webhook" with a public HTTPS webhook_url. It carries one of three terminal events: job.completed, job.failed or job.canceled. The body is signed with HMAC SHA 256 over <timestamp>.<raw_body>, and the headers are x-sume-webhook-timestamp and x-sume-webhook-signature: sume-v1=<hex>.
During a signing-secret rotation the signature header holds one sume-v1= entry per live secret, comma separated. Accept the delivery when any entry matches.
A verifier that returns false
The check below refuses an empty secret, rejects a bad timestamp and a stale one, and compares every entry. It never raises on header shape, so the only thing the surrounding workflow has to branch on is a boolean.
import crypto from "node:crypto";
export function verifySume(rawBody, timestamp, header, secret, tolerance = 300) {
if (!secret || typeof header !== "string") return false;
const ts = Number(timestamp);
if (!Number.isFinite(ts)) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - ts) > tolerance) return false;
const digest = crypto.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`).digest("hex");
const expected = Buffer.from(`sume-v1=${digest}`);
let ok = false;
for (const entry of header.split(",")) {
const got = Buffer.from(entry.trim());
if (got.length === expected.length && crypto.timingSafeEqual(got, expected)) ok = true;
}
return ok;
}Order of operations in the workflow
The shape of the receiving workflow matters more than the node types.
- Capture the raw body exactly as received. Re-serialized JSON will not match the signature.
- Run the verifier. On false, stop the branch and return a non-2xx so the sender knows delivery failed; do not process the payload.
- Write the event to storage keyed by
job_id, then answer2xx. Sume retries on network errors and non-2xx responses. - Only after storing, fan out to Slack, storage or the next step.
Retries are bounded
Sume tries a delivery up to 10 times in total, with a fixed delay between attempts of 30 seconds by default and a 10 second timeout per attempt. A workflow that does slow work before answering burns that budget and gets the same event again.
Use job_id as the idempotency key on your side, so a repeated event is a no-op. Ten failed attempts leave a failed delivery and a job that still reached its terminal state, so keep status_url polling available, or ask Sume to redeliver with POST /v1/jobs/{job_id}/webhook/redeliver.
Tracing with 2.42
If you turn on the improved OpenTelemetry export that 2.42.0 lists, put the Sume job_id on the span that handles the event. It is the one identifier that ties a delivery, a poll and a usage row together.
Sources
Related posts
More in Integrations
- n8n 2.42 MCP Registry: add Sume's hosted MCP endpoint
n8n 2.42.0 (pre-release) drops the feature flag for its MCP Registry. The Sume side is one URL: OAuth read-only by default, or an API key for paid tools.
- n8n "failed to refresh MCP registry": test Sume directly
If n8n says it failed to refresh the MCP registry, test your Sume connection separately with mcp_health and tools_list so you know which side is failing.
- n8n queue mode and Sume webhooks: the 10-second budget
Sume gives each webhook delivery attempt 10 seconds, up to 10 attempts 30 seconds apart. If n8n runs in queue mode, acknowledge first, then do the work.
- n8n task runner rejections: poll a Sume job without losing it
n8n 2.41.6 keeps the task runner alive after an unhandled promise rejection. Make your Sume poll catch its own errors and stop only on a terminal status.
Written by Sume