One-line JavaScript

Form protection

Validate email fields before browser form submission, block configured high-risk outcomes, and meter every integration independently.

Overview

Form protection is designed for SaaS signups, newsletters, lead capture, free trials, waitlists, and checkout forms. Each integration has a public site key, an exact or wildcard origin allowlist, an independent rate limit, usage analytics, and its own revocation boundary. It never exposes a developer API key.

Invalid and disposable blocking

Syntax, domain, MX, and disposable-domain checks run before submission.

Bot friction

A hidden honeypot catches automated form fillers without adding a visual challenge.

Dynamic form support

Forms added later by React, Vue, modals, and page transitions are protected automatically.

GET
https://api.mailtooth.com/form-protection/embed.js
POST
https://api.mailtooth.com/form-protection/verify

Quickstart

  1. Open Form protection and choose Protect a form.
  2. Name the integration and add each origin that hosts the form, such as https://example.com. Origins contain a scheme, host, and optional port, never a path.
  3. Select verification depth and a blocking policy. Quick is the lowest-latency default and costs 0.5 credit per conclusive check.
  4. Copy the generated script tag before the closing body tag.
HTML
<script src="https://api.mailtooth.com/form-protection/embed.js" data-mailtooth-key="YOUR_PUBLIC_SITE_KEY" defer></script>

By default, every form containing an input[type="email"] is protected. Set custom selectors in the dashboard when only specific forms should be intercepted.

What the script does

  1. Finds matching forms on load and whenever the DOM changes.
  2. Adds an off-screen, non-tabbable honeypot field.
  3. Verifies on email-field blur and caches the decision for that exact value.
  4. Pauses submission until a decision is available and preserves each submit button's previous disabled state.
  5. Uses native setCustomValidity and reportValidity for accessible validation.
  6. Resubmits with requestSubmit, preserving the clicked submit button and existing submit handlers.

Script configuration

AttributeDefaultDescription
data-mailtooth-keyRequiredPublic site key shown in the dashboard.
data-mailtooth-formformCSS selector for forms to protect.
data-mailtooth-emailinput[type="email"]Email-field selector resolved inside each matching form.
data-mailtooth-fail-opentrueSet to false to block while verification is unavailable.
data-mailtooth-timeout12000Timeout in milliseconds; the minimum is 1000.
data-mailtooth-endpointDerived from script URLOptional override for a controlled proxy or staging API.
Custom targeting and fail-closed mode
<script src="https://api.mailtooth.com/form-protection/embed.js" data-mailtooth-key="YOUR_PUBLIC_SITE_KEY" data-mailtooth-form="#signup" data-mailtooth-email="input[name=email]" data-mailtooth-fail-open="false" defer></script>

To render errors in your own element, add data-mailtooth-error="element-id" to the email input. The script updates that element and marks it as an ARIA live region.

Blocking policy

Invalid syntax, missing domains or MX records, disposable domains, and conclusive undeliverable mailbox results are always blocked. An integration can additionally block the following outcomes.

OptionWhen it appliesRecommendation
RiskyDeepcheck found a mailbox that may accept mail but carries delivery risk.Enable for abuse-sensitive trials and monitor conversion.
UnknownA required DNS, provider, or SMTP check was inconclusive. Unknown checks are refunded.Leave disabled for revenue forms; enable for strict gated access.
Role basedThe local part is shared, such as info, admin, or sales.Enable when an individual work identity is required.

Quick costs 0.5 credit, Standard costs 1 credit, and Deepcheck costs 2 credits. Unknown outcomes are refunded through the same ledger as API verification.

Browser events

Events bubble from the protected form. Use them for analytics or presentation changes, not as server-side authorization signals.

EventMeaning
mailtooth:verifiedThe address passed the configured policy. detail contains allowed, reason, and message.
mailtooth:blockedThe address was blocked. detail contains allowed, reason, and message.
mailtooth:errorVerification was unavailable. detail.allowed reflects fail-open or fail-closed behavior.
Event listener
document.querySelector("#signup").addEventListener("mailtooth:blocked", (event) => {
  analytics.track("signup_email_blocked", { reason: event.detail.reason });
});

SPAs and dynamic forms

No reinitialization is needed after client-side navigation or modal rendering. The embed observes added elements and enhances each form once. Remove duplicate script tags so a form is not managed by multiple site keys.

Existing synchronous and asynchronous submit listeners still run after verification. The normal browser submission path is retained, including the original submitter when supported.

Security and privacy

  • The site key is public by design. It can only request policy decisions and cannot call developer, billing, team, or bulk APIs.
  • Browser requests must match the stored origin allowlist. Use separate integrations for production, staging, and local sites.
  • Only the email, public site key, and honeypot value are sent to MailTooth. Raw verification evidence is not returned to the page.
  • Revoke a copied or retired integration immediately. Its earlier analytics remain available.
Keep layered server-side controlsA script tag protects browser interactions, but a bot can call your form action without running JavaScript. Keep server-side schema validation, rate limiting, CSRF protection where applicable, abuse monitoring, and double opt-in for sensitive workflows. Never use a client-only decision to authorize an account, payment, or privileged action.

Failure behavior

The default is fail open: if verification times out or is unavailable, submission continues so an outage does not lose a signup or purchase. Set data-mailtooth-fail-open="false" for a strict flow. Fail-closed mode shows a retry message and prevents submission.

In-flight checks are canceled when the address changes. A per-integration rate limit returns HTTP 429 when exceeded; the script then applies the selected failure behavior.

Troubleshooting

SymptomCauseFix
403 responseThe page origin is not allowed.Add the exact scheme, hostname, and port. Do not add a path.
401 responseThe integration is revoked or the site key is wrong.Copy the current snippet or create a replacement integration.
429 responseThe per-minute integration limit was exceeded.Increase the limit or reduce repeated attempts.
Form is not interceptedThe form or email selector does not match.Inspect the markup and update both selectors in the dashboard.
CSP blocks the script or requestThe API origin is missing from Content Security Policy.Allow https://api.mailtooth.com in script-src and connect-src.
Submission proceeds during an outageFail-open is the default.Set data-mailtooth-fail-open to false if the strict tradeoff is acceptable.
Next step

Create an origin-scoped integration.

Copy the generated tag and test it from an allowed origin.

Open form protection