ContactLintAPIMCPPricingFree toolsCompareGet a key

Home / Integrations / n8n

Validate Emails in n8n Workflows With ContactLint

Published 2026-10-18

This guide adds a ContactLint email check to an n8n workflow with the HTTP Request node. A small Code node turns each result into one action: reject, suggest or accept. A Switch node routes on that action, and the workflow fails open if ContactLint does not answer.

Where the check goes in n8n

Put the check right after your trigger and before any node that saves the contact, adds it to a CRM, or sends a welcome email. The trigger is usually a Webhook node that receives form posts, or a trigger from your form or CRM app.

n8n runs on a server, so your secret key stays out of the browser. That makes this a real server-side check. If your form waits for the webhook's response, you can return a "Did you mean ...?" message to the person. If it does not wait, use the result to tag or hold the record instead.

Setup: credential and HTTP Request node

  1. Get a free secret key at /signup.
  2. In n8n, create a new credential of type Header Auth. Set Name to Authorization and Value to Bearer YOUR_SECRET_KEY. This keeps the key out of the workflow itself.
  3. Add an HTTP Request node after the trigger and set:
    • Method: POST
    • URL: https://contactlint.com/v1/email/validate
    • Authentication: Generic Credential Type, then Header Auth, then your credential
    • Send Body: on, Body Content Type: JSON, Specify Body: Using Fields Below
    • Body field name email, value {{ $json.body.email }} for a Webhook trigger, or the field from your trigger
    • Options, Timeout: 3000 (milliseconds)
  4. Open the node's Settings and set On Error to continue. A timeout, 401 or 402 then passes an item without a result instead of stopping the workflow. That is what makes it fail open.
  5. Add a Code node after it, mode Run Once for All Items, language JavaScript:
return $input.all().map(item => {
  const r = item.json.result;
  let action = 'accept';
  let message = '';
  if (r && r.verdict === 'invalid') {
    action = 'reject';
    message = 'That email address cannot receive mail.';
  } else if (r && r.did_you_mean) {
    action = 'suggest';
    message = 'Did you mean ' + r.did_you_mean + '?';
  } else if (r && r.disposable) {
    action = 'reject';
    message = 'Please use a permanent email address.';
  }
  // No result means a timeout or API error: action stays 'accept' (fail open)
  return { json: { ...item.json, action, message, suggestion: (r && r.did_you_mean) || '' } };
});
  1. Add a Switch node in Rules mode with three outputs: {{ $json.action }} equals reject, suggest and accept.

The HTTP Request node replaces the item with the API response. In later nodes, read the original address with an expression such as {{ $('Webhook').item.json.body.email }}.

Handling each verdict and did_you_mean

Testing it

Click to listen for a test event on the Webhook node, then send test posts to its test URL:

curl -X POST https://your-n8n-host/webhook-test/signup \
  -H "content-type: application/json" \
  -d '{"email":"jane@gmial.com"}'

Limits

Questions

Why use a Code node instead of putting all the rules in the Switch node?

The order matters: invalid first, then did_you_mean, then disposable. The Code node applies that order in one place and turns errors into accept. The Switch node then only compares one simple action field.

Where should I store the ContactLint key in n8n?

Store it in a Header Auth credential, not in the workflow. Workflows are often exported or shared, and credentials are kept separately.

Can I check many addresses at once?

Yes. Use POST /v1/email/validate/batch with an emails array of up to 50 addresses. Each address uses one check, and the batch counts as one request against the 60 per minute limit.

Other integrations

Get a free API key. 100 checks per month, no card required. Or try a live check first.