Documentation

Everything you need to get protected

⚑ Quick start: three ways

Pick whichever is quickest. The first two need nothing installed on your website.

  1. Check leads you already have. In your dashboard, upload a CSV export from your CRM β€” every row comes back with a verdict, risk score and reasons (details).
  2. Connect new leads β€” no code. Have your form tool, CRM, Zapier or Make send a copy of each new lead to your private lead-intake URL. Nothing changes on your website (how to connect).
  3. Block fakes on your website in real time (optional). Install the WordPress plugin, or paste one line before the closing </body> tag (platform guides below).

Test any of them with test@mailinator.com β€” it's flagged within seconds in your dashboard, with the reason shown. On a website, the snippet finds every form on the page automatically (including ones added later by page builders).

πŸ”Œ Connect your leads (no code)

Every form tool, CRM and automation platform can send a copy of each new lead to a web address. Send it to your lead-intake URL β€” it's in your dashboard's Connect your leads card and looks like this:

https://leadshield.ai/api/validate?api_key=YOUR_API_KEY

Nothing changes on your website and nothing is blocked: LeadShield scores each copy and the verdict appears under Recent activity within seconds, in your weekly report, and in your webhooks (Pro and Agency). Keep the URL private β€” it contains your API key β€” and if you regenerate the key, update every connected tool.

GoHighLevel

  1. Automation β†’ Workflows β†’ Create workflow β†’ Start from scratch.
  2. Trigger: Contact Created (or Facebook Lead Form Submitted).
  3. Action: Webhook β†’ method POST β†’ URL: your lead-intake URL.
  4. Save and publish, then send a test lead β€” it appears in your dashboard.

GoHighLevel: tag fake leads automatically PRO

The steps above send every new contact to LeadShield. To have GoHighLevel act on the verdict, so fake leads don't get your client's SMS and email follow-ups, send the verdict back:

  1. In GoHighLevel, create a second workflow with the trigger Inbound Webhook and copy its URL. (On some GoHighLevel plans this is a premium trigger.)
  2. In your LeadShield dashboard β†’ Webhooks, paste that URL, choose blocked only, save, and press Send test so GoHighLevel receives a sample to map.
  3. In that workflow, add Find Contact (or Create/Update Contact) using the email from lead.email, then Add Contact Tag β†’ leadshield-flagged. Branch on result if you want separate tags for invalid and suspicious.
  4. In each follow-up workflow, right after its trigger, add a Wait of 2 minutes and an If/Else: if the contact has the tag leadshield-flagged, stop (or move it to a review list); otherwise carry on.

LeadShield normally returns a verdict within a few seconds, so a two-minute wait leaves plenty of room. Tag rather than delete at first, so your team can review what was flagged. The test sends a sample lead (test@mailinator.com); delete it from GoHighLevel if it appears.

Zapier

  1. Trigger: your lead source β€” Facebook Lead Ads, Typeform, your CRM's new contact…
  2. Action: Webhooks by Zapier β†’ POST (needs a paid Zapier plan) β†’ URL: your lead-intake URL β†’ payload type JSON.
  3. Data: email, name, phone, message β€” mapped from the trigger.
  4. To keep junk out of your CRM: add a Filter step "only continue if result is valid" before the CRM step.

Make

  1. Trigger module: your lead source (for example Facebook Lead Ads β†’ Watch Leads).
  2. HTTP β†’ Make a request: URL your lead-intake URL, method POST, body type Raw, content type JSON.
  3. Request content: {"email": "…", "name": "…", "phone": "…", "message": "…"}, mapped from the trigger.
  4. Tick Parse response and filter on result = valid before your CRM module.

Facebook lead ads

Facebook's own lead forms never touch your website, so no website code can screen them β€” connecting is the only way. Use GoHighLevel, Zapier or Make above, with Facebook Lead Ads as the trigger.

Form tools with webhooks

Typeform, Jotform, Webflow, Elementor Pro, Unbounce, Gravity Forms, WPForms, Fluent Forms, HubSpot workflows, Calendly and others: paste your lead-intake URL into the tool's webhook setting. LeadShield reads JSON, form-encoded and multipart bodies β€” including nested formats like Typeform's answers, Webflow's payload.data, HubSpot's properties and Jotform's rawRequest β€” and finds the email, name, company, phone and message fields itself. An email address is the only required field.

What's checked on a connected lead

  • The same as on your website: email checks, phone validation, AI content analysis (Pro and Agency), duplicates and your keyword blocklist.
  • IP reputation runs when the tool includes the visitor's IP address β€” send it as ip. Many tools (GoHighLevel, Gravity Forms, Jotform) can include it.
  • Browser-only signals β€” fill speed, mouse and keyboard activity, the hidden bot trap and device fingerprint β€” need the snippet. If your tool knows the visitor's user agent, send it as user_agent.
  • A lead forwarded by a tool is never penalised for coming from that tool's server: its server address and software aren't treated as the visitor's.
  • Each copy uses one validation from your monthly allowance.

The snippet, in depth

<script src="https://leadshield.ai/js/shield.js" data-api-key="YOUR_API_KEY" async></script>

What it does on every protected form: injects an invisible honeypot field, measures fill-time and human interaction (keystrokes, mouse, touch β€” so mobile users and autofill are never penalised), computes a privacy-safe device fingerprint hashed in the browser, and validates on submit. Fail-open guarantee: if our API is slow (>5s) or unreachable, the form submits normally β€” our outage can never lose you a lead.

Configuration attributes

AttributeDefaultEffect
data-api-keyβ€”Required. Your API key from the dashboard.
data-form-selectorformCSS selector limiting which forms are protected, e.g. #contact-form.
data-block-suspicioustrueSet "false" to hard-block only outright junk (score 70+) and let borderline leads through flagged.
data-badgefalseSet "true" to show a small "Protected by LeadShield" badge under the form.

Custom verdict handling

<script src="https://leadshield.ai/js/shield.js" async></script> <script> window.addEventListener('DOMContentLoaded', function() { LeadShield.init({ apiKey: 'YOUR_API_KEY', blockSuspicious: false, onValidation: function(result) { // result: { result, risk_score, reasons, suggested_domain } // Return true to allow the submission, false to block it. return result.risk_score < 80; } }); }); </script>

WordPress

⭐ Easiest: install our free plugin. In WordPress go to Plugins → Add New, search “LeadShield”, install and activate. Then Settings → LeadShield and paste your API key. That is the whole install — no code, no theme edits, and it survives theme updates.

The plugin also lets you choose whether borderline “suspicious” leads are blocked, limit protection to specific forms with a CSS selector, and switch on an optional “Protected by LeadShield” credit (off by default).

Prefer to paste the snippet yourself? One snippet protects Contact Form 7, WPForms, Gravity Forms, Elementor forms, Ninja Forms and most others automatically.

Easiest method (any theme):

  1. Install the free plugin "WPCode" (or any "insert headers and footers" plugin).
  2. Go to Code Snippets β†’ Header & Footer β†’ Footer box.
  3. Paste your snippet from the dashboard. Save.

Without a plugin: Appearance β†’ Theme File Editor β†’ footer.php β†’ paste just before </body>. (Use a child theme so updates don't remove it.)

Verify: submit any form with test@mailinator.com β†’ blocked β†’ visible in your dashboard.

WooCommerce

WooCommerce runs on WordPress, so the WordPress install above covers it β€” the snippet protects account-registration and contact forms storewide.

Recommended scope for stores: protect registration and enquiry forms, but leave the checkout form alone (payment friction is sacred). Limit scope with:

<script src="https://leadshield.ai/js/shield.js" data-api-key="YOUR_KEY" data-form-selector=".woocommerce-form-register, .wpcf7-form" async></script>

Shopify

  1. Shopify admin β†’ Online Store β†’ Themes β†’ β‹― β†’ Edit code.
  2. Open layout/theme.liquid.
  3. Paste your snippet just before </body>. Save.

This protects storefront forms: contact pages, newsletter signups and customer registration. For headless/Hydrogen stores, call the REST API from your backend instead.

Webflow Β· Wix Β· Squarespace

Webflow: Project settings β†’ Custom code β†’ Footer code β†’ paste snippet β†’ Save & publish.

Wix: Settings β†’ Custom code β†’ + Add custom code β†’ paste snippet β†’ set "Place code in: Body β€” end" β†’ apply to all pages.

Squarespace: Settings β†’ Advanced β†’ Code injection β†’ paste into the Footer box β†’ Save. (Business plan or higher required by Squarespace.)

Typeform, JotForm & embedded forms

Form builders that render inside an iframe (Typeform, JotForm, Google Forms embeds) can't be reached by a snippet on the host page β€” the browser isolates iframes. Two good options:

  1. Connect it (recommended): paste your lead-intake URL into the form's webhook setting, or route it through Zapier/Make β€” see Connect your leads. Filter out anything that isn't valid before it reaches your CRM.
  2. Native embeds: JotForm's plain HTML embed (not iframe mode) renders directly in your page, and the snippet protects it like any other form.

REST API reference

Authentication

Every request needs your API key in the X-API-Key header. Requests without a valid key get 401 Unauthorized. Manage or regenerate your key in the dashboard β€” regeneration kills the old key instantly.

Validate a lead

POST https://leadshield.ai/api/validate X-API-Key: YOUR_API_KEY Content-Type: application/json { "email": "lead@example.com", // required "full_name": "Jane Smith", // optional β€” improves AI analysis "company_name": "Acme Ltd", // optional "phone": "+44 20 7946 0000", // optional β€” enables phone validation "message": "We need 50 units of…", // optional β€” enables AI-spam text analysis "ip": "203.0.113.9", // optional β€” the VISITOR's IP, see below "form_fill_time": 14.2, // optional β€” seconds, if you track it "form_url": "https://yoursite.com/contact" }

Field names β€” send what your form already uses

No need to rename anything. Every field accepts the common aliases, case-insensitively:

FieldAlso accepted
emailemail_address, emailAddress, your-email
full_namename, fullname, fullName, contact_name, your-name β€” or first_name + last_name
company_namecompany, companyName, organisation, organization, business, org
phonetel, telephone, mobile, phone_number, contact_number
messagecomments, comment, enquiry, inquiry, description, notes, details, your-message
ipip_address, client_ip, visitor_ip

Only email is required. Send whatever else your form happens to collect β€” most forms only ask for a name, email and message, and that's perfectly normal. A lead is never penalised for a field your form doesn't ask for.

⚠️ Calling from your server, CRM or Zapier? Send the ip field. In a server-to-server call the connecting IP is your server, not the visitor's β€” so pass the visitor's real IP in the body, or the IP-reputation layer will check the wrong address. The JavaScript snippet handles this for you automatically.

Response

{ "success": true, "result": "invalid", // valid | suspicious | invalid "risk_score": 95, // 0–100 "reasons": ["Disposable email domain"], "suggested_domain": null, // "gmail.com" when a provider typo is detected "usage": { "current": 412, "limit": 10000 } }

Errors

StatusMeaningFix
400Missing email in bodySend JSON with an email field
401Missing/invalid API keyCheck the X-API-Key header for typos or spaces
403Subscription inactive or trial expiredPick a plan in dashboard β†’ billing
429Monthly validation limit reachedUpgrade, or wait for the monthly reset on the 1st

Health check

GET https://leadshield.ai/api/health β†’ { "status": "ok" }

Webhooks PRO

Set a webhook URL in your dashboard and we'll POST every verdict to it as it happens β€” no polling. Choose every lead (push clean ones straight into your CRM) or blocked only (route junk to a review queue). Use the Send test button to confirm your endpoint before going live.

POST https://your-app.com/hooks/leadshield X-LeadShield-Event: lead.validated X-LeadShield-Signature: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 { "event": "lead.validated", "sent_at": "2026-07-26T09:15:04.123456Z", "result": "invalid", "risk_score": 88, "reasons": ["Disposable email domain"], "lead": { "email": "spam@mailinator.com", "full_name": "Test Lead", "company_name": "", "phone": "", "ip_address": "203.0.113.10", "form_url": "https://example.com/contact" }, "usage": { "current": 412, "limit": 10000 } }

Verify it came from us. X-LeadShield-Signature is an HMAC-SHA256 of the raw request body, keyed with your API key β€” the same scheme Stripe uses. Compare it against your own computation and reject anything that doesn't match:

import hmac, hashlib expected = hmac.new(API_KEY.encode(), raw_body, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, request.headers["X-LeadShield-Signature"]): abort(400)

Reply with any 2xx status. We retry once on failure and then move on β€” webhooks are notifications, so a dead endpoint never delays or changes a validation.

Ad-platform exclusion exports PRO

Blocking junk at your form protects your CRM, but your ad platform still counts those clicks as conversions and keeps hunting for more of the same. Two downloads in your dashboard close that loop:

  • Google Ads IP exclusions β€” a plain list of the addresses behind your blocked leads. Paste into Campaign β†’ Settings β†’ Additional settings β†’ IP exclusions (Google's cap is 500, so we return the most recent).
  • Hashed email suppression β€” SHA-256 of each blocked address, exactly the format Google Customer Match and Meta Custom Audiences expect. Upload as an exclusion audience. Raw addresses never leave your account.

Both default to the last 90 days; add ?days=30 to narrow the window.

Bulk CSV list cleaning

No account yet? The free lead audit runs the same deterministic checks on up to 250 rows with no signup and no card β€” useful for a first look before you install anything.

Screen a list you already have β€” a CRM export, an old spreadsheet, an inherited database β€” without waiting for new form traffic. Dashboard β†’ Clean a list you already have β†’ choose your .csv β†’ the annotated file downloads with three columns added: verdict, risk score and reasons.

  • Format: a header row containing an email column (we also accept "e-mail", "email address", "work email"). A phone column is used when present. All your original columns are preserved.
  • Limits: 100 rows per upload on Free, 1,000 on Pro, 5,000 on Agency. Your first 14 days get the Pro allowance. Max file size 4MB.
  • Quota: rows count as validations against your monthly allowance, but are not added to Recent activity β€” a one-off list clean shouldn't distort your live-traffic statistics.
  • It shares one pool with your live forms. Cleaning 9,000 rows on a 10,000 plan leaves only 1,000 for real submissions, and once the quota is gone live screening pauses until the 1st. Your dashboard shows the split (live vs list cleaning), warns at 80% and 100%, emails you at both, and refuses any upload that would take you over.

⚠️ Bulk results are deliberately conservative. A CSV row has no browser session and no visitor IP, so bulk cleaning runs the deterministic layers only β€” email syntax, disposable domains, live MX, typo detection, phone format and in-file duplicates. Behavioural signals, IP reputation and AI contextual analysis need a live form submission. Anything flagged here is genuinely junk, but a row that looks clean can still be caught in real time.

Ad-click attribution

The snippet reads gclid, gbraid, wbraid, fbclid, msclkid and UTM parameters from the landing-page URL (or from a matching hidden form field, if your site already captures them) and sends them with the lead. That's what powers the wasted ad spend figure on your dashboard β€” it separates junk that cost you ad money from junk that arrived organically.

No cookies or browser storage are used, in line with our privacy promise. The trade-off is that a click ID is only visible when the form is reached during the same visit as the ad click β€” which covers the usual PPC landing-page flow. Sending server-side? Include the click ID in your /api/validate body and it is stored the same way.

Optional extra signals

Beyond the standard layers, five further signals can be switched on for your account. They are off by default β€” we run them in observation mode first, measure what they would have caught on your own traffic, and only enable one when the evidence supports it. Nothing changes on your account without that review.

SignalWhat it catches
Datacenter / hosting IPSubmissions from AWS, Google Cloud, DigitalOcean and similar β€” real customers browse from consumer connections, automated traffic often doesn't
Tor exit nodeSubmissions routed through the Tor anonymity network
Country mismatchA phone number registered in one country submitted from an IP in another
Submission velocityRepeated submissions from one address in minutes, or the same email twice in quick succession
Domain ageEmail domains registered within the last 30 days, a common disposable-infrastructure pattern

When a signal is enabled it behaves like every other layer: it adds to the 0–100 risk score and its reason appears on the lead, so you always see exactly why something was flagged. Ask support if you'd like any of them turned on for your account.

Risk thresholds & data retention AGENCY

Agency accounts can move where verdicts fall on the 0–100 score (defaults: invalid at 70, suspicious at 40) β€” lower the cut-off to block harder, raise it to let borderline leads through. You can also set how long screened lead details are kept before automatic deletion; 0 clears them at the next nightly sweep, so aggregate scores and counts survive but the personal data does not. Both live in dashboard β†’ Advanced controls.

Server-side examples

cURL

curl -X POST https://leadshield.ai/api/validate \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email": "test@example.com", "full_name": "Test User"}'

JavaScript / Node

const res = await fetch("https://leadshield.ai/api/validate", { method: "POST", headers: { "X-API-Key": process.env.LEADSHIELD_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ email: lead.email, full_name: lead.name, message: lead.message }) }); const verdict = await res.json(); if (verdict.result === "invalid") return; // drop it before the CRM

Python

import requests verdict = requests.post( "https://leadshield.ai/api/validate", headers={"X-API-Key": LEADSHIELD_KEY}, json={"email": lead["email"], "full_name": lead["name"]}, timeout=8, ).json() if verdict["result"] != "invalid": crm.create_lead(lead, score=verdict["risk_score"])

PHP

$ch = curl_init('https://leadshield.ai/api/validate'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['X-API-Key: ' . $key, 'Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode(['email' => $email]), ]); $verdict = json_decode(curl_exec($ch), true);

CRM & automation recipes

Zapier / Make (no code β€” works with any CRM or form tool)

  1. Trigger: your form tool's "new submission" (Typeform, JotForm, Facebook Lead Ads, anything).
  2. Action: Webhooks β†’ POST β†’ your lead-intake URL (https://leadshield.ai/api/validate?api_key=… β€” or the plain URL plus an X-API-Key header), JSON body with the mapped email/name/message fields. Step-by-step: Connect your leads.
  3. Filter: only continue if result is not invalid.
  4. Action: create the lead in your CRM, storing risk_score in a custom field.

HubSpot

Use a Workflow with a custom-code action (or the Zapier recipe above): on contact creation, POST the contact's email/name to /api/validate, write risk_score to a custom property, and branch β€” 70+ gets a "junk" lifecycle stage and never reaches a sales queue.

Salesforce

An Apex trigger (before insert on Lead) calling our REST endpoint does the same: store the score in a custom field and route assignment rules on it. Any Salesforce developer can wire this in under an hour with the reference above.

Pipedrive & others

Every CRM with an API or Zapier support works with the validate-before-create pattern: score first, create second, route by risk_score.

Verdicts & risk routing

ScoreVerdictRecommended handling
0–39validStraight to sales. Fast follow-up on these is where revenue lives.
40–69suspiciousDon't call β€” nurture. Email sequence first; let engagement prove intent. (Blocked by the snippet by default; set data-block-suspicious="false" to let them through flagged.)
70–100invalidBlocked at the form / never enters the CRM. Reasons are always attached.

Typo rescue: when a visitor mistypes a major provider (gmial.com), the response includes suggested_domain and the snippet shows "Did you mean @gmail.com?" β€” recovering a customer you'd otherwise lose.

Testing your install

  1. Submit your form with test@mailinator.com β†’ should be blocked ("Disposable email domain").
  2. Submit with yourname@gmial.com β†’ "Did you mean @gmail.com?" appears.
  3. Submit with your real email, typed normally β†’ passes instantly.
  4. Open your dashboard β€” all three attempts are listed with scores and reasons.

Troubleshooting

"API key required" or 401 errors

The key must be sent exactly as issued β€” check for leading/trailing spaces, and that it's in the data-api-key attribute (snippet) or X-API-Key header (API). Still stuck? Regenerate the key in your dashboard and update your site β€” old keys stop working immediately.

Nothing appears in the dashboard

1) View your page source and confirm the snippet is present before </body> (some cache/optimizer plugins strip scripts β€” whitelist shield.js). 2) Confirm your form has an email field β€” forms without one are ignored. 3) Check the browser console for a LeadShield error message.

A genuine lead was blocked

Check the reason in your dashboard first β€” it's usually a disposable domain or honeypot hit, which are near-certain junk. If the verdict was "suspicious" (40–69), set data-block-suspicious="false" so only outright junk is hard-blocked, or take full control with onValidation. Believe the engine got it flat wrong? Send us the example β€” we tune on real cases and reply fast.

Monthly limit reached (429)

Validation pauses (forms keep working, unvalidated) until the 1st β€” or resumes the moment you upgrade in dashboard β†’ Manage billing. We email you at 80% and 100% so it never surprises you.

Single-page apps / AJAX forms

The snippet watches the page for new forms automatically (MutationObserver), so SPAs and dynamically-injected forms are covered. If your form submits via your own JavaScript rather than a real submit event, call the REST API from your handler instead.

Billing & account

  • Plans: Free ($0 forever, 100 validations a month), Pro ($99/month, 10,000, AI and every integration) and Agency ($249/month, unlimited, all your clients in one account). Annual billing gives 2 months free.
  • Every account starts with 14 days of Pro, no card. Then it moves to the Free plan rather than switching off β€” everything you connected keeps working, and your data and API key are preserved.
  • Badge: website forms on the Free plan show a small “Protected by LeadShield” badge; Pro and Agency can remove it. In the WordPress plugin it's optional and off by default.
  • Upgrade: on Free or in your first 14 days, choose Pro or Agency from your dashboard. On a paid plan, dashboard β†’ Manage billing opens the secure Stripe portal for plan changes, card updates and cancellation; cancellation runs to the end of the paid period.
  • Card statement: charges appear as AUTONOMA β€” LeadShield's parent company.
  • Invoices: downloadable anytime in the billing portal.
  • Data retention: screened lead data is deleted automatically after 90 days (configurable on Agency, down to zero).

Still stuck?

Ask the assistant in the corner β€” it knows everything on this page. Or contact a human; we reply within one business day.

LeadShield AssistantTypically replies in seconds
×
Hi! I know these docs inside out β€” ask me anything about installing, the API, or troubleshooting.