All docs
3 min read

Custom rules

Sometimes the spam you actually get is specific. The same SEO agency emails the same pitch every Tuesday from the same domain. A bot reuses the same comment. A link pattern shows up across hundreds of submissions.

Custom rules let you encode that pattern once and have every future matching submission filed as spam automatically.

Three kinds of rules

Each form has three rule lists, edited on the form's settings page under Custom rules:

Rule type What it matches Example
Regex A regex pattern against the full payload (all fields concatenated) `/(buy
Keyword Case-insensitive substring match against any field value outsourcing
Blocked email Exact match against the email field, or any field containing an email spammer@example.com, *@bad-domain.com

A submission that matches any rule on any list is filed as spam with reason custom_rule_matched:<rule>.

Regex syntax

Patterns use PCRE syntax (PHP regex). Wrap in delimiters and add flags as needed:

/(?i)\b(crypto|nft|web3)\s+(advisor|consultant|agency)\b/
/https?:\/\/[a-z0-9-]+\.(xyz|top|click)\b/i
/[\p{Cyrillic}]{10,}/u   # Long Cyrillic strings (often spam in EN-only forms)

The pattern is matched against a string of all field values joined with newlines. So a regex hit on the message body or the name field both count.

Keyword syntax

Keywords are plain strings, case-insensitive substring match. No wildcards, no regex — if you need either, use the regex list instead.

Good keyword candidates:

  • Brand names of recurring spammers
  • Specific phrases from copy-paste pitches ("I noticed your website")
  • Domains and TLDs you'd never expect from a real user

Blocked-email syntax

Three forms accepted:

  • someone@example.com — exact match on a single address.
  • *@example.com — any address at that domain.
  • *@*.example.com — any subdomain of that domain.

Match is case-insensitive. The check runs against any field that contains an @ and parses as an email address, not just the field literally named email.

Performance considerations

Rules run synchronously, before the submission is persisted. They're cheap, but not free.

  • Regex is the most expensive. A pathological regex on a 50KB payload will block the request thread for seconds. Keep patterns simple, anchor them, and avoid .* at both ends.
  • Keyword is O(n × m) where n is field length and m is keyword count. Stays fast up to a few hundred keywords.
  • Blocked email is a hash lookup. Effectively free even at thousands of entries.

If you're approaching the per-form rule cap (100 regex, 500 keyword, 5000 email), ask yourself whether AI moderation would do the same job with less maintenance.

Common patterns

# Match anything that looks like a crypto pitch
/(?i)\b(crypto|defi|web3|nft|blockchain)\s+(deal|advisor|opportunity)\b/

# Match any URL with a sketchy TLD
/https?:\/\/[^\s]+\.(xyz|top|click|bid|loan)\b/i

# Match obvious SEO outreach
/(?i)\b(guest\s+post|link\s+exchange|backlink|seo\s+services)\b/

# Match phone numbers in the message field (most legit forms have separate phone fields)
/(\+?\d[\d\s\-]{8,}\d)/

# Long runs of non-Latin script in an English-only form
/[\p{Cyrillic}\p{Han}]{15,}/u

Combining with AI moderation

Custom rules and AI moderation stack. Rules run first (cheap, deterministic). If no rule matches, the submission falls through to AI moderation (slower, fuzzier).

The dashboard shows hit counts per rule over the last 30 days. If a rule has zero hits in a month, delete it — every rule you keep adds latency to every submission.

What's next