Home / Integrations / Webflow
Validate Emails in Webflow Forms With ContactLint
Published 2026-10-18
This guide adds the ContactLint widget to a Webflow site with one custom code embed. People who type jane@gmial.com see "Did you mean jane@gmail.com?" under the field and can fix it in one click. You can also hold back submission for addresses that cannot receive mail.
Where the check goes in Webflow
Webflow hosts your forms and processes submissions itself, so none of your own server code runs when someone submits. That leaves the browser, and the browser is public. Use the ContactLint widget with a cl_pub_ publishable key. Never put your secret key in Webflow custom code.
A browser check can be skipped by anyone who turns off JavaScript or posts to the form directly. If you need a check that cannot be skipped, send submissions to your own backend or to an automation tool such as n8n, Make or Zapier, and run the server-side check there. For most marketing and signup forms, the widget alone catches the common typos before they reach your list.
Step 1: create a publishable key
Get a free secret key at /signup. Then create a publishable key that only answers requests from your Webflow domains. List every origin the form runs on: your custom domain with and without www if you use both, and your Webflow staging domain. A key accepts up to 5 origins.
curl -X POST https://contactlint.com/v1/widget/keys \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "content-type: application/json" \
-d '{"origins":["https://www.yoursite.com","https://yoursite.com","https://yoursite.webflow.io"],"daily_cap":200}'The response contains a publishable_key that starts with cl_pub_. Run this command from your own computer, not from Webflow.
Step 2: add the custom code
- Open your site settings and go to the Custom code tab. Custom code may depend on your Webflow plan, so check your plan.
- Paste this into Footer code, replacing
cl_pub_...with your key:
<script src="https://contactlint.com/widget.js" data-key="cl_pub_..." data-selector="input[type=email]" defer></script>
<script>
document.addEventListener('contactlint:result', function (e) {
var r = e.detail;
var bad = r.verdict === 'invalid' || (r.disposable && !r.did_you_mean);
e.target.setCustomValidity(bad ? 'Please use a permanent, working email address.' : '');
});
document.addEventListener('input', function (e) {
if (e.target.type === 'email') e.target.setCustomValidity('');
});
</script>- Save and publish. Custom code runs on the published site, not inside the Designer.
The first script tag loads the widget. Webflow Email fields render as input type="email", so data-selector finds them without extra attributes. The second script is optional. It uses the contactlint:result event to hold back submission for undeliverable or disposable addresses. It also clears that block whenever the person edits the field, so a failed check never leaves the form stuck.
To check only one form, give it an ID in the element settings and use data-selector="#your-id input[type=email]". To place the hint somewhere specific, add a custom attribute data-contactlint-hint with value email-hint to the email field, and give an empty Text Block the ID email-hint.
Handling each verdict and did_you_mean
- risky with reason
possible_typo: the widget shows "Did you mean ...?" fromdid_you_mean. One click fixes the field. The script does not block these, because gmial.com is both a typo and on the disposable list, and the suggestion is the better answer. - invalid: the widget shows a short note that the domain cannot receive mail. With the optional script, the browser blocks submission and shows your message.
- risky with reason
disposable: the widget shows a note. The optional script blocks submission. Remover.disposablefrom the script if throwaway addresses are fine for this form. - risky with reason
role_address: allowed. info@ and sales@ are real, shared inboxes. - valid or unknown: allowed with no hint.
Testing it
- Open the published page or your staging domain, not the Designer.
- Type
jane@gmial.comand click out of the field. You should see "Did you mean jane@gmail.com?". Click it and the field changes to jane@gmail.com. - Type
x@mailinator.comand click out. You should see the disposable note. With the optional script, pressing submit shows your validation message. - Type
jane@example.invalid. You should see the note that the domain cannot receive mail. - Type
info@yourcompany.comwith a real domain. The form should submit.
If nothing appears, open your browser's developer tools. Check that widget.js loads and that the page's exact origin is in the key's origin list. www and non-www count as different origins.
Limits
- ContactLint does not connect to mail servers. It cannot confirm that a mailbox exists or detect catch-all domains.
- The widget fails open. If ContactLint does not answer, no hint appears and the form submits normally.
- A browser check can be bypassed. For a check that cannot be skipped, validate on a server.
- The widget checks only when the field loses focus with a new value, so one visitor filling the form once uses one check. You get 100 free checks per month, then credit packs from $9 for 5,000.
- Publishable keys allow 10 checks per minute per visitor network, 30 per minute per key, and your daily cap from 1 to 10,000. The key is visible in page source, so set the cap to what you are willing to spend in a day.
- Typed addresses are sent to contactlint.com. Mention this in your privacy notice.
See the widget guide and site builder setup for more options.
Questions
Why does the widget not show in the Webflow Designer?
Custom code runs only on the published site. Test on your published domain or your webflow.io staging domain, and make sure that origin is listed on the publishable key.
Can I run a server-side check on Webflow form submissions?
Not inside Webflow itself, because your own code does not run on submit. Send submissions to your own backend or an automation tool such as n8n, Make or Zapier, and call the ContactLint API there with your secret key.
Will the widget block my form if ContactLint is down?
No. The widget never blocks a form by itself. The optional script only blocks after a result says the address is invalid or disposable, and it clears the block when the person edits the field.
Other integrations
- Next.js
- Express
- Django
- FastAPI
- Laravel
- Ruby on Rails
- Supabase
- Firebase Authentication
- WordPress
- Zapier
- Make
- n8n
Get a free API key. 100 checks per month, no card required. Or try a live check first.