Integration recipe
Send new Toolbench leads to n8n
Register your n8n Webhook URL with POST /webhooks for lead.created, verify Toolbench-Signature in a Code node, then branch on the event payload.
Webhook node, signature check in Code, importable workflow JSON.
Register the webhook
Turn write on for your API token, then subscribe your n8n production Webhook URL for lead.created. Store the signing_secret (full whsec_… string) in n8n credentials or an env var — it is shown once.
curl -s -X POST "https://app.toolbench.com/api/v1/webhooks" \
-H "Authorization: Bearer $TB_TOKEN" \
-H "Content-Type: application/json" \
-d '{:,:[]}'Import the workflow
In n8n: Workflows → Import from file, then paste your Webhook path and set TB_WEBHOOK_SECRET to the signing secret. Download the starter JSON.
Verify in a Code node
Place this immediately after the Webhook node:
// n8n Code node — first item is the Webhook payload
const crypto = require("crypto");
const secret = $env.TB_WEBHOOK_SECRET; // full whsec_… from POST /webhooks
const header = $input.first().headers["toolbench-signature"] ?? "";
const rawBody = JSON.stringify($input.first().json.body ?? $input.first().json);
const parts = Object.fromEntries(
header.split(",").map((p) => {
const i = p.indexOf("=");
return [p.slice(0, i), p.slice(i + 1)];
}),
);
const t = Number(parts.t);
const v1 = parts.v1;
if (!t || !v1 || Math.abs(Date.now() / 1000 - t) > 300) {
throw new Error("Invalid or stale Toolbench-Signature");
}
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`, "utf8")
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))) {
throw new Error("Signature mismatch");
}
return $input.all();Branch on the event
After verification, read $json.body.type (or your raw-body parse) — for lead.created, map data.lead into your CRM, Slack, or a second workflow.