Consume Embers webhooks in n8n
A community recipe: point a signed Embers endpoint at an n8n Webhook node, verify the HMAC in a Code node, then run your own workflow.
n8n can receive Embers leads through the same signed HTTPS webhook that Zapier, Make, Clay, and hand-written receivers use. This is a community recipe: Embers does not build, test, or support an n8n node.
Before you start
- An n8n instance, cloud or self-hosted, reachable over public HTTPS on port 443.
- On a self-hosted instance, the Code node allowed to load the built-in
cryptomodule (NODE_FUNCTION_ALLOW_BUILTIN=crypto). - Somewhere to keep the
whsec_signing secret. The example reads it from theEMBERS_WEBHOOK_SECRETenvironment variable.
Set up the workflow
-
Add a Webhook node
Create a workflow that starts with a Webhook node. Set HTTP Method to
POSTand leave Respond on Immediately, so n8n answers200before your workflow runs. Under Options, turn on Raw Body: the node then keeps the exact bytes Embers sent as binary data nameddata, which the signature check needs. -
Register the URL in Embers
Copy the node’s Production URL, not the Test URL, and activate the workflow. In Embers, open Integrations in the sidebar, then the Webhooks row (
/integrations/webhooks), choose Add endpoint, and paste it. Settings, then Developer, then Webhooks works too. Store thewhsec_secret from Save the signing secret where the Code node can read it. -
Subscribe to the events you want
Keep New ICP lead (
lead.created) for one event per newly qualified lead. New engagement (lead.engagement.added) is the highest-volume event and is not selected on a new endpoint. Lead status changed and Report ready are there when you need them. -
Verify the signature in a Code node
Add a Code node straight after the Webhook node, in Run Once for All Items mode, with this JavaScript. It throws on anything that fails, which stops the execution, and passes on the parsed event when the check succeeds.
const crypto = require('crypto'); const TOLERANCE_SECONDS = 300; const secret = $env.EMBERS_WEBHOOK_SECRET; const headers = $input.first().json.headers; const rawBody = await this.helpers.getBinaryDataBuffer(0, 'data'); const id = headers['webhook-id']; const timestamp = headers['webhook-timestamp']; const age = Math.abs(Date.now() / 1000 - Number(timestamp)); if (!id || !timestamp || !(age <= TOLERANCE_SECONDS)) { throw new Error('Missing Embers headers or stale timestamp'); } const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64'); const expected = crypto .createHmac('sha256', key) .update(`${id}.${timestamp}.`) .update(rawBody) .digest(); const verified = String(headers['webhook-signature'] || '') .split(' ') .some((entry) => { const received = Buffer.from(entry.split(',')[1] || '', 'base64'); return received.length === expected.length && crypto.timingSafeEqual(received, expected); }); if (!verified) { throw new Error('Embers signature did not match'); } return [{ json: JSON.parse(rawBody.toString('utf8')) }];If your instance blocks
$envin the Code node, read the secret from wherever it keeps secrets and change that line. Verify webhook signatures explains each part of the check. -
Deduplicate, then do the work
Delivery is at least once and retries can arrive out of order. Store each
id(the same value as thewebhook-idheader) and skip events you have already processed. Join people ondata.lead.linkedin_urn, falling back todata.lead.idwhen the URN is blank on an older record. Skipwebhook.testevents, which carrydata.is_test: true, once you are live.
To build the rest of the workflow, send a test from Embers (Send test in the endpoint’s … menu) and open that execution in n8n’s executions list: it holds a sample shaped like the real event.
What Embers sends
The payload is the same one every receiver gets. A lead event fires when the person qualifies: a score above zero, an ICP match or a manual approval, and a company that is not blocklisted. data.lead.contact appears on a paid Solo plan or above and carries only a stored email and phone, never a fresh lookup. The webhook payload reference lists every field.
Because the Webhook node answers before the Code node runs, a failed check or a workflow error never pauses the endpoint, and Embers does not retry it either. Watch n8n’s own executions list for those.
Troubleshooting
The endpoint paused with HTTP 404. You registered the Test URL, or the workflow is not active. n8n answers 404 for a webhook nothing is listening on, and a 404 pauses the endpoint at once. Activate the workflow, save the Production URL on the endpoint to clear the pause, and replay. See a webhook endpoint is paused.
Every execution fails the signature check. Raw Body is off, so the Code node has no data binary to hash, or the secret was rotated. Turn the option on, or copy the new secret.
require('crypto') fails. A self-hosted instance has not allowed the built-in module. Set NODE_FUNCTION_ALLOW_BUILTIN=crypto and restart n8n.
Deliveries stopped after a few days. n8n was unreachable or answering 5xx for the whole retry window of about 6.3 days, so the endpoint paused. Fix it, resume, and replay.
Related
Frequently asked questions
- Is there an Embers node for n8n?
- No. n8n receives the standard signed webhook through its own Webhook node. Embers does not publish or support an n8n node.
- Why did my endpoint pause as soon as I saved it?
- The first delivery reached an n8n URL nothing was listening on, usually the Test URL or an inactive workflow, and n8n answered 404. A 404 pauses the endpoint after one attempt.
Was this article helpful?
What went wrong?
Thank you. We read every answer.
Your first 2,000 engagers free
Every day Embers reads the public activity around your market, ranks the people who fit, and hands you a short list with the reason each one surfaced. You decide who to contact.
Start free trial- No card to start
- No LinkedIn login
- Cancel anytime