Signal reference

Every check returns a list of signals. Each one is a single fact the engine found, with the weight it contributed to the score. This page is what those ids mean — and, just as usefully, when they are wrong.

How a decision is built

signals → sum of weights (capped at 100) = score

score >= block threshold      → block
score >= challenge threshold  → challenge
otherwise                     → allow
Thresholds and weights come from the active policy profile (?profile=). A profile can reweight a whole category or ignore it; the signals you get back already reflect that, so the weights in a response are the weights that actually scored.

Two rules for consuming signals

Match on the prefix and the category, not the full id
Feed signal ids carry an index (feed:ipsum:0) that shifts when feeds change. The category is stable; the index is not.
An empty signal list means “nothing known”
Not “clean”. A score of 0 with confidence 0.2 means no source had anything to say about this address.

Categories

CategoryWeightMeaning
syntax50The address is not a valid mailbox address at all.
disposable40Throwaway mailbox provider (10 minute mail and friends).
phishing35Domain listed as actively used for phishing.
malware40Domain listed as distributing malware.
mx30Mail routing of the domain — can mail be delivered at all.
role_account5Shared mailbox (info@, support@) rather than a person.
typo10One edit away from a large mailbox provider.
abuse15-40IP listed for attacks, spam or compromise by a public feed.
anonymizer25Tor exit node or commercial VPN egress.
datacenter15Hosting/cloud range — no residential user behind it. Never emitted for the mail servers of an email check: every mail server worth its name lives in a datacenter, so the hit carries no information there.
free_mail10Free mailbox provider (gmail.com, gmx.de, mail.ru). Not abuse, an attribute of the address: the sender has no domain of their own. Weight is low on purpose — a B2B lead form cares, a newsletter signup does not, and the profile decides. Paid privacy providers (posteo.de, mailbox.org) are deliberately absent from the source list; proton.me is missing although its free tier would qualify.

Signals

SignalCategoryWeightChecks
email:invalid_syntaxsyntax50email
email_domain:<category><from the feed that listed the domain>feed weight (disposable 40, malware 40, phishing 35)email
domain:<category><from the feed that listed the domain>feed weight (see above)domain
email:role_accountrole_account5email
email:domain_typotypo10email
email:no_mxmx30email
email:implicit_mxmx5email
feed:<feed_name>:<n>abuse | anonymizer | datacenter (from feeds.yaml)the feed's weight in feeds.yaml (15-40)ip, domain, email

What each signal means

email:invalid_syntaxsyntax · weight 50 · high · validator

The string is not a syntactically valid email address (RFC 5322 via the email-validator library). Deliverability is reported as undeliverable without any DNS lookup.

What to do with it. Reject at the form level and show a correction hint. Nothing else in the response is meaningful — no domain was parsed.

When it is wrong. Unusual but legal local parts (quoted strings, unicode) are accepted by the validator, so a rejection here is rarely wrong.

email_domain:<category><from the feed that listed the domain> · weight feed weight (disposable 40, malware 40, phishing 35) · high if weight >= 30, else low · comma-separated feed names that listed the domain
Examples: email_domain:disposableemail_domain:phishingemail_domain:malware

The email's domain appears on one or more domain blocklists. One signal per category, never one per feed; the weight is the highest weight among the feeds of that category that listed it, and `source` names all of them.

What to do with it. disposable is the usual reason to challenge a signup; malware and phishing on a sender domain are strong block reasons. The feed names in `source` are what you cite when a user disputes the decision.

When it is wrong. Disposable lists are broad and include some legitimate privacy forwarders (e.g. relay services). Phishing lists can carry a compromised domain for a while after cleanup.

domain:<category><from the feed that listed the domain> · weight feed weight (see above) · high if weight >= 30, else low · comma-separated feed names
Examples: domain:phishingdomain:malwaredomain:disposable

Same lookup as email_domain, emitted by the domain check. The different prefix exists so a caller can tell which check produced the signal when results are stored side by side.

What to do with it. Use for link/referrer/website checks. Identical semantics to email_domain of the same category.

When it is wrong. Same as email_domain.

email:role_accountrole_account · weight 5 · low · static_list

The local part is a shared mailbox name (info, support, kontakt, sales, …). Not abuse — an attribute of the address.

What to do with it. Useful for B2B lead quality, not for blocking. Weight is deliberately small; raise it via a profile weight override if role addresses are worthless to you, or ignore the category entirely for contact forms.

When it is wrong. Legitimate in B2B: a purchasing department writing from einkauf@ is normal.

email:domain_typotypo · weight 10 · low · static_list

The domain is one edit (insert, delete, substitute, adjacent transposition) away from a popular mailbox provider — gamil.com, gmial.com, web.d. Only checked when no blocklist already flagged the domain. The suggestion is in enrichment.did_you_mean.

What to do with it. Do not block on this. Show "did you mean gmail.com?" in the form — this is the signal that recovers real customers who mistyped.

When it is wrong. A real, small domain that happens to be one edit from a large provider (e.g. gmx.at vs gmx.de) will be flagged.

email:no_mxmx · weight 30 · high · dns

The domain has neither MX records nor an A record fallback. Mail to this address cannot be delivered by anyone. Deliverability is set to undeliverable.

What to do with it. The strongest cheap signal for a fake address. Worth a challenge on its own at the default threshold; combine with anything else and it blocks.

When it is wrong. Transient DNS failures look identical to a missing MX. A resolver timeout on a busy domain will fire this once and not again — do not persist the verdict, re-check.

email:implicit_mxmx · weight 5 · low · dns

No MX record, but an A record exists. RFC 5321 says mail may be delivered to the A record host, so this is deliverable in theory but unusual for a domain that accepts mail in practice.

What to do with it. A weak hint, not a reason to act alone. Common for small self-hosted domains.

When it is wrong. Legitimate single-host mail setups.

feed:<feed_name>:<n>abuse | anonymizer | datacenter (from feeds.yaml) · weight the feed's weight in feeds.yaml (15-40) · high if weight >= 30, else medium · <feed_name>
Examples: feed:firehol_level2:0feed:tor_exits:0feed:spamhaus_drop:1

The IP falls inside a range listed by that feed. `<n>` is the index of the match when several ranges cover the same address — it is a disambiguator, not a rank. On the domain check the IP is a resolved A record; on the email check it is an IP of one of the first five MX hosts.

What to do with it. Match on the feed name and the category, never on the full id — the index shifts when feeds change. Weights stack across feeds: two feeds listing the same IP add up, which is how a well-known bad host reaches block range without any single feed being decisive. They do not stack across hosts: when a domain resolves to several addresses, or has several mail servers, one feed contributes once. A provider running five mail servers is not five times as suspicious.

When it is wrong. Large NAT ranges and shared hosting mean a listed IP is not necessarily the visitor. anonymizer is a policy question, not an abuse finding: VPN users are ordinary customers in Europe. On the email check, the IPs belong to the domain's mail servers and not to whoever is filling in your form — a shared provider can be listed for something another customer did.

Profiles on this instance

Select one per request with ?profile=. Same signals, different verdict.

ProfilechallengeblockWeight overridesIgnored
checkout2060anonymizer=40
default3080
lead3080free_mail=30, disposable=80
newsletter5090free_mail

Machine-readable

The catalogue is served as JSON by GET /v1/signals, and a single id resolves with ?signal=feed:tor_exits:0. Generating code or building an agent? Read it from your own instance rather than this page — feeds and weights are configuration. The client packages do exactly that.
Signal reference · monapi