Signals and reasons
Understand why a decision was returned and when individual signals matter.
The action tells your signup flow what to do. primary_reason says why, and the signals are the facts behind it.
Primary reason
primary_reason is the one thing that most drove the action. It is OK when nothing applied.
This shortened response says the address was blocked because its domain is a known disposable provider:
{
"action": "block",
"primary_reason": "DISPOSABLE",
"signals": {
"disposable": true
}
}Use it for a short explanation, or to record why a signup was stopped. It is always one of these:
| Value | What it means |
|---|---|
DOMAIN_NEWLY_REGISTERED | Your new-domain rule. |
INFRASTRUCTURE_ZONE | Your free and dynamic-DNS rule. |
DOMAIN_UNDELIVERABLE | Your undeliverable-domain rule. |
DOMAIN_TYPO_SUGGESTED | The domain looks like a misspelling of a known provider. |
DISPOSABLE | A known disposable or temporary email provider. |
NO_MX | The domain has no mail servers. |
SUSPICIOUS | The address looks like generated junk or a test address. |
ROLE_BASED | A shared address such as info@, admin@, or support@. |
PRIVACY_RELAY | A relay that forwards to another inbox. block only if you turned the relay rule on. |
PUBLIC_PROVIDER | A public provider such as Gmail or Yahoo. |
OK | Nothing. The address looked ordinary. |
The table is in priority order. Compare the value, not the wording: the descriptions can change, the values will not.
primary_reason is the single most useful thing to say about the address, which is not always the rule that produced action. Typo domains get squatted, so a mistyped address often lands on a domain that is genuinely disposable. gmial.com returns action: "block" with primary_reason: "DOMAIN_TYPO_SUGGESTED" and suggested_domain: "gmail.com", because "did you mean gmail.com?" is what the person needs to read. signals.disposable is still true, so the cause is never lost. Read the signals when you need the cause rather than the explanation.
Exact domain rules are the exception. They set action without a value of their own, so an allow rule on a disposable domain still reads DISPOSABLE, and a block rule on an ordinary domain reads OK. action is what your rule changed.
Signals
Signals are the facts found during the check. Every field is always present. Use them when your product needs an exception the action alone cannot express.
| Signal | Example | What it tells you |
|---|---|---|
disposable | name@tempmail.com | The domain is a known temporary email provider. |
public_provider | name@gmail.com | The address uses a public provider. This is not bad by itself. |
role_based | support@company.com | The address looks shared by a team or function. |
suspicious | A strong junk-like pattern | The address needs more confidence before valuable access. |
privacy_relay | x8f2k9@duck.com | A hidden address forwarding to a real inbox. One flag for every relay service, so it does not say which. Allowed unless you turn on the relay rule. |
dynamic_dns | me@bob.duckdns.org | The domain is on a zone where anyone can claim a free subdomain. Blocked unless you turn off the free and dynamic-DNS rule. |
has_mx | true | Mail server records were found for the domain. |
suggested_domain | gmail.com | The entered domain may contain a typo. |
normalized_email | foobar@gmail.com | The canonical form after provider alias rules (see below). |
mailbox_alias | true | Your account saw this mailbox before under a different spelling. Resending the identical address is false. Off by default. |
has_spf | true | The domain publishes an SPF record. Read the caveats below. |
has_dmarc | false | The domain publishes a DMARC record. Read the caveats below. |
has_mx, has_spf, has_dmarc, and mailbox_alias can be null, which means not known rather than no. suggested_domain and normalized_email are null when they do not apply.
Correcting a typo
A typo outranks the cause only when the suggestion is confident: exactly one edit away, and not a character glued onto an intact provider name. gmial.com qualifies. xgmail.com and abmail.com do not, so those keep DISPOSABLE as the reason and carry the suggestion in signals.suggested_domain instead.
If you would rather not depend on that distinction, branch on the signal directly and the wording is right in every case:
if (result.signals.suggested_domain) {
showDidYouMean(result.signals.suggested_domain);
} else {
explainUsing(result.primary_reason);
}
This applies whatever the action is. A typo on a domain that is not disposable comes back as allow, and the same branch offers the correction before you accept the address.
Email normalization and mailbox_alias
Some providers treat many spellings as one inbox. f.o.o.bar+trial2@gmail.com and foobar@googlemail.com both reach foobar@gmail.com. normalized_email gives you that single form.
- Gmail, Proton, Outlook, Fastmail, iCloud, Zoho, Yandex, mailbox.org, and Runbox have alias rules we apply.
- Yandex and Runbox also fold their alternate domains, so
alice@ya.ruandalice@yandex.ruare one address, as aredemo@rbox.meanddemo@runbox.com. - Every other domain is only lowercased and trimmed.
- Two different mailboxes are never merged into one.
mailbox_alias is true when your account has seen the same mailbox before under a different spelling. Off by default; turn it on in Dashboard → Protection → Email memory.
- Resending the identical address returns
false. That is a retry, and your own records already answer it. A new spelling of a mailbox you have seen is the part only we can answer. - It proves a previous check of an alias, not an existing account and not a completed signup. Never refuse on the flag alone. Refuse when your own users table confirms that mailbox already signed up, which is the second trial you are trying to stop.
- It never changes
action. What to do about a repeat is your call. - All your API keys share one memory, so rotating a key loses nothing. Test addresses are remembered too; use Dashboard → Try a check to avoid that.
- Turn it off or delete a single address under Dashboard → Protection → Email memory. Details in Privacy and retention.
Sender authentication: has_spf and has_dmarc
Whether the domain publishes SPF and DMARC records.
nullmeans not known yet, not "no". The first check of a new domain returnsnulland a later one has the answer.- They describe whether a domain can be spoofed as a sender, which is a different question from whether a signup is real.
- Real businesses and disposable providers publish SPF at about the same rate, so
has_spfon its own tells you little. - Do not reject on missing records. Plenty of legitimate small businesses have never set up DMARC.
- Both are data only and never change
action. They are most useful as corroboration alongside other signals.