Documentation
Spam protection
WebForms scores every submission for spam before delivering it. Deterministic signals such as a honeypot and a time trap add up to a score, an AI classifier reads the message itself on every plan, and anything over the form's threshold is quarantined: stored for review, never delivered.
Last updated
How scoring works
Every layer below produces a partial score between 0 and 1; they add up. Once the total reaches the threshold (0.6), the submission is quarantined: stored and visible in your inbox, but never delivered to a destination, and not counted toward your monthly quota. A few checks (a filled honeypot, a submission faster than the time trap allows, a failed captcha) reject outright, before anything is stored, regardless of threshold.
Either way, the sender always sees a normal success response. Returning an error to a rejected submission is how a bot learns what got it caught. Silence is the point.
Signals
- Honeypot field A field named _gotcha that a real visitor never sees or fills, hidden with CSS. A bot filling every field trips it instantly. Rejects outright.
- Time trap Set _start_time to a timestamp when the form renders. A submission completed in under ~2 seconds (configurable) is treated as scripted.
- Disposable email Checks the submitted email's domain against a list of throwaway-email providers. Adds to the spam score; it does not reject on its own.
- Blocked domains / keywords Per-form lists you maintain: a submission from a blocked sending domain is quarantined, and content containing chosen keywords adds to the score.
- Link flood Many URLs packed into one field is a strong spam signal on its own.
- Origin mismatch If you've set an allowed-origins list for the form, a submission whose Origin header doesn't match adds to the score rather than being silently accepted.
- Empty or tiny content A submission with almost nothing in it (common with scripted probing) scores slightly higher.
- Sender reputation Submissions are tracked by sender identity (email/domain, never IP) across your workspace. A sender previously confirmed as spam by the AI classifier scores worse next time, for free, without another model call.
- AI classifier A second opinion from a classifier that reads the message itself, on every plan including free. It runs on every submission the deterministic score has not already condemned, including ones that score zero, because content-only spam trips no other signal. Its verdicts feed sender reputation, so a repeat sender is caught without another call. Switch it off per form if no third party should see submission content.
- Captcha Turnstile, hCaptcha or reCAPTCHA, verified server-side against the token in _captcha (or the provider's own field name).
How well does the AI classifier work?
We measured it on 22 September 2026, before switching it on for every plan, by running it over the 808 submissions WebForms had stored at that point. Median response time was 258ms (95th percentile 326ms), and no call failed. It agreed with 50 of the 51 submissions the deterministic filter had already quarantined, and it flagged 11 submissions that had been delivered as spam with at least 90% confidence.
None of those submissions had a human spam label, so these are agreement figures, not precision and recall. Quarantine exists for the cases it gets wrong: a real message it holds back is still stored in your inbox, where you can read it and reply.
Setting up a honeypot
Add a field named _gotcha (or your form's configured honeypot field name) and hide it with CSS, not type="hidden", which some bots skip.
<input type="text" name="_gotcha" style="position:absolute;left:-9999px" tabindex="-1" autocomplete="off" />
Setting up the time trap
Stamp a hidden field with the current time when the form renders, so the server can measure how long it took to submit:
<input type="hidden" name="_start_time" id="start-time" />
<script>
document.getElementById("start-time").value = Date.now();
</script>Configuring per form
Every signal is a toggle or a threshold in Form → Settings → Spam, and the same shape is writable from the write API:
curl -X PATCH https://api.webforms.to/v1/forms/frm_xxx \
-H "Authorization: Bearer wf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"spamConfig": {
"honeypot": true,
"timeTrapMs": 2000,
"captcha": "turnstile",
"blockDisposableEmail": true,
"blockedKeywords": ["viagra", "crypto airdrop"],
"allowedOrigins": ["example.com"]
}
}'Reviewing quarantine
Quarantined submissions live alongside genuine ones in the dashboard, filterable by status, so nothing caught by a false-positive is actually lost: you can open it, read it in full and contact the sender yourself. The Submissions API excludes them by default; pass status=quarantined to include them.
Get your endpoint
Free for 300 submissions a month. No card required.
Start free