Skip to content

n8n

Start an n8n workflow with each new response, then send it anywhere n8n reaches. Formerie sends a webhook, and n8n’s Webhook node catches it.

We ran these steps on self-hosted n8n 2.45.7, with real signed deliveries from a local copy of Formerie. We haven’t run them on n8n Cloud.

Connect your form

  1. In n8n, create a workflow and add a Webhook node.
  2. Set HTTP Method to POST, and choose a Path, such as formerie-responses.
  3. Copy the Production URL. The Test URL works only while n8n is listening in the editor, so it won’t catch your form’s later responses.
  4. In Formerie, open your form, then Delivery, and add a webhook with that address. Keep the signing secret.
  5. In n8n, publish the workflow, which turns the production address on.
  6. In Formerie, choose Send test. Find the run under the workflow’s Executions.

While you build, you can catch a test in the editor: select Listen for Test Event, and use the Test URL in a second webhook for a moment.

Use the answers

The body is under body, and headers are under headers:

Text
{{ $json.body.data.fields.email }}
{{ $json.body.data.submission.language }}
{{ $json.headers['webhook-id'] }}

The first is the field called email on your form. A nested field follows its groups, such as fields.contact.email.

Check the signature

A workflow’s address is secret, but anyone who has it could send fake responses. To refuse them, check the signature. It takes three more nodes and a setting:

  1. In the Webhook node, set Respond to Using ‘Respond to Webhook’ Node, and turn on the Raw Body option under Add Option.
  2. If you host n8n, allow the crypto module by setting NODE_FUNCTION_ALLOW_BUILTIN=crypto on your instance.
  3. Add a Code node after it, in JavaScript, with this, and your own secret:
JavaScript
const crypto = require('crypto');

const secret = 'whsec_your_secret';
const item = $input.first();
const headers = item.json.headers;
const rawBody = Buffer.from(item.binary.data.data, 'base64').toString('utf8');

const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = crypto.createHmac('sha256', key).update(`${id}.${timestamp}.${rawBody}`).digest('base64');
const valid = fresh && (headers['webhook-signature'] ?? '').split(' ').some((part) => part === `v1,${expected}`);

return [{ json: { valid, event: valid ? JSON.parse(rawBody) : null } }];
  1. Add an If node with the condition {{ $json.valid }} is true.
  2. On its true branch, carry on with your workflow and end with a Respond to Webhook node answering 200. On its false branch, add one answering 401.

After this, your answers are at $json.event.data.fields. A forged request got a 401 in our run. A real one got 200, and kept getting it through a change of secret until the old one ended, when it got 401 until the node had the new secret.

Keep the secret in an n8n credential if your setup allows it, rather than in the node.

If it doesn’t work

  • Nothing arrives after publishing: the webhook in Formerie may still have the Test URL. Replace it with the Production URL.
  • n8n can’t be reached: Formerie needs a public https address on the standard port. A self-hosted n8n needs a reverse proxy or tunnel in front of it.
  • The Code node stops on require: the crypto module isn’t allowed yet. See step 2.
  • The Code node can’t read binary: the Raw Body option is off.

Next: send responses to Google Sheets or HubSpot.