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.
Read from the live engine
GET /v1/signals on api.monapi.io, so it describes the instance you would actually be calling. Your own instance serves its own catalogue at the same path.How a decision is built
signals → sum of weights (capped at 100) = score
score >= block threshold → block
score >= challenge threshold → challenge
otherwise → allow?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
feed:ipsum:0) that shifts when feeds change. The category is stable; the index is not.Categories
| Category | Weight | Meaning |
|---|---|---|
syntax | 50 | The address is not a valid mailbox address at all. |
disposable | 40 | Throwaway mailbox provider (10 minute mail and friends). |
phishing | 35 | Domain listed as actively used for phishing. |
malware | 40 | Domain listed as distributing malware. |
mx | 30 | Mail routing of the domain — can mail be delivered at all. |
role_account | 5 | Shared mailbox (info@, support@) rather than a person. |
typo | 10 | One edit away from a large mailbox provider. |
abuse | 15-40 | IP listed for attacks, spam or compromise by a public feed. |
anonymizer | 25 | Tor exit node or commercial VPN egress. |
datacenter | 15 | Hosting/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_mail | 10 | Free 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
| Signal | Category | Weight | Checks |
|---|---|---|---|
email:invalid_syntax | syntax | 50 | |
email_domain:<category> | <from the feed that listed the domain> | feed weight (disposable 40, malware 40, phishing 35) | |
domain:<category> | <from the feed that listed the domain> | feed weight (see above) | domain |
email:role_account | role_account | 5 | |
email:domain_typo | typo | 10 | |
email:no_mx | mx | 30 | |
email:implicit_mx | mx | 5 | |
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 · validatorThe 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 domainemail_domain:disposableemail_domain:phishingemail_domain:malwareThe 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 namesdomain:phishingdomain:malwaredomain:disposableSame 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_listThe 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_listThe 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 · dnsThe 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 · dnsNo 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>feed:firehol_level2:0feed:tor_exits:0feed:spamhaus_drop:1The 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.
| Profile | challenge | block | Weight overrides | Ignored |
|---|---|---|---|---|
checkout | 20 | 60 | anonymizer=40 | — |
default | 30 | 80 | — | — |
lead | 30 | 80 | free_mail=30, disposable=80 | — |
newsletter | 50 | 90 | — | free_mail |
Machine-readable
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.