# Reason codes

> Every reason code isBusinessEmail returns in reasons[]: syntax, list matches, MX fingerprints, SPF/DMARC/DKIM, Microsoft tenant, domain age and more.

Source: https://isbusinessemail.com/docs/reason-codes

`reasons` is an array of machine-readable codes explaining a verdict, strongest first. Use them for logging, analytics, support answers and your own rules.

```json
"reasons": ["mx_google_workspace", "dmarc_reject", "txt_saas_tokens:3"]
```

- Some codes carry a **count suffix** after a colon: `txt_saas_tokens:3` means three SaaS verification tokens were found. Split on `:` before matching.
- New codes may be added within `/v1`. **Treat unknown codes as informational**, never as errors.
- The **Effect** column is a guide to direction, not a weight. Weights are tuned on our evaluation set and change between engine versions.

Effects: **→ category** sets the category outright · **+** raises business confidence · **−** lowers it · **flag** sets a flag without changing the category · **info** is context only.

## Input and syntax

| Code | Meaning | Effect |
|---|---|---|
| `invalid_syntax` | The address or domain isn't syntactically valid (bad characters, missing `@`, empty labels, length limits) | → `invalid` |
| `invalid_domain` | The domain doesn't exist in DNS, or isn't a registrable domain (e.g. only a public suffix) | → `invalid` |
| `no_mx` | No MX records and no address record to fall back to, so the domain can't receive mail | → `invalid` |
| `null_mx` | The domain publishes a null MX (`MX 0 .`, RFC 7505): it explicitly accepts no mail | → `invalid` |
| `role_account` | The local part is a role, not a person: `info`, `sales`, `support`, `admin`, `noreply`… | flag; `review` under `b2b`, `block` under `strict` |
| `shared_inbox_name` | The local part looks like a shared inbox, one mailbox several people answer from (`support`, `billing`, `sales-eu`…); see [`shared_inbox`](https://isbusinessemail.com/docs/response-fields#shared-inboxes-and-groups) | flag (low confidence) |
| `group_name` | The local part looks like a group that forwards to many members (`team`, `all`, `berlin-team`…); see [`group`](https://isbusinessemail.com/docs/response-fields#shared-inboxes-and-groups) | flag (low confidence) |
| `google_group` | The address is at `googlegroups.com`, so it is a Google Group | flag |
| `subaddress` | The address has a `+tag`; `normalized_email` has it removed | flag |
| `typo_suspected` | The domain is within a small edit distance of a popular provider (e.g. `gmial.com`); see `did_you_mean` | flag, − |
| `lookalike_domain` | The domain imitates a popular provider with confusable characters or punycode | flag, − |

## List matches

| Code | Meaning | Effect |
|---|---|---|
| `custom_domain` | Not on any shared, disposable or relay list: the organization's own domain | + (baseline for `business`) |
| `shared_provider` | On our shared-domain provider list (gmail.com, outlook.com, yahoo.com, regional webmail, ISP domains…) | → `personal` |
| `disposable_provider` | On our disposable list (throwaway inbox services) | → `disposable` |
| `relay_service` | A privacy relay (Apple Hide My Email, Firefox Relay, DuckDuckGo, SimpleLogin, addy.io) | → `relay` |
| `education_domain` | An education suffix (`.edu`, `.ac.*`, `.edu.*`) or a known university domain | → `education` |
| `government_domain` | A government suffix (`.gov`, `.gov.*`, `.gouv.fr`, `.gc.ca`, `.mil`) or a known public-sector domain | → `government` |
| `business_allowlist` | On our business allowlist: a verified company domain whose DNS might otherwise look unusual | → `business` |
| `blocked_domain` | On our blocklist, built from spam and abuse reports | `is_blocked`; `block` under every policy |
| `blocked_email` | The exact address is on our blocklist (stored as a hash). Returned to secret keys only. | `is_blocked`; `block` under every policy |
| `admin_override` | A person on our team reviewed this domain and set its category | → the reviewed category |

## MX fingerprints

Who receives mail for the domain, from its MX hosts.

| Code | Meaning | Effect |
|---|---|---|
| `mx_google_workspace` | MX is Google Workspace (`aspmx.l.google.com`, `smtp.google.com`) | + strong; Workspace evidence |
| `mx_google_consumer` | MX is consumer Gmail (`gmail-smtp-in.l.google.com`) | → `personal` |
| `mx_microsoft_365` | MX is Exchange Online (`*.mail.protection.outlook.com`) | + strong; Microsoft 365 evidence |
| `mx_outlook_consumer` | MX is consumer Outlook.com (`*.olc.protection.outlook.com`) | → `personal` |
| `mx_yahoo_consumer` | MX is consumer Yahoo Mail (`*.yahoodns.net`) | → `personal` |
| `mx_enterprise_gateway` | MX is a corporate email-security gateway (Proofpoint, Mimecast, Cisco, Barracuda, Trend Micro, Sophos, Hornetsecurity, Broadcom/Symantec) | + strong |
| `mx_hosting_provider` | MX is a web-hosting company's mail service | + moderate |
| `mx_consumer_host` | A custom domain hosted on a consumer-oriented mail service (e.g. iCloud+ custom domains, Fastmail). Still `business`, with lower confidence. | + weak |
| `mx_forwarding_service` | MX is a mail-forwarding service: the domain has no mailboxes of its own | − |
| `mx_shared_disposable` | The MX host also serves three or more known disposable domains | → `disposable` |
| `mx_shared_provider` | The MX host also serves several known shared (free-mail) domains: an alias or regional domain of a webmail provider | → `personal` |
| `disposable_name` | The domain name itself advertises a throwaway inbox (`temp`, `trash`, `spam`, `fake`, `burner`…) and there is no evidence of a real organization | → `disposable` |
| `mail_service_name` | The domain name suggests a mailbox service (`…mail`, `inbox…`) and there is no evidence of a real organization (Workspace/M365/gateway MX, SaaS tokens, an Entra tenant) | → `unknown` (review) |
| `mx_other_suite` | MX is another business suite (Zoho, Proton, Yandex 360, Microsoft 365 resellers…); see `workspace.other_suite` | + |

## Authentication records

| Code | Meaning | Effect |
|---|---|---|
| `spf_present` | The domain publishes an SPF record | + |
| `spf_business_saas` | SPF includes business senders: HubSpot, Salesforce, Zendesk, Intercom, SendGrid, Mailgun, Mailchimp, Amazon SES… | + |
| `dmarc_reject` | DMARC policy `p=reject` | + |
| `dmarc_quarantine` | DMARC policy `p=quarantine` | + |
| `dmarc_none` | DMARC record with `p=none` (monitoring only) | + weak |
| `no_spf_no_dmarc` | Neither SPF nor DMARC published, which is common for throwaway and parked domains | − |
| `dkim_google` | A Google DKIM key at `google._domainkey`, which reveals Workspace even behind a mail gateway | + ; Workspace evidence |
| `txt_saas_tokens:N` | Domain-verification TXT tokens for business SaaS (Atlassian, DocuSign, Slack, Zoom, Adobe, Stripe, Microsoft `MS=`, Facebook, Apple…). Suffix `:n` = how many. | + |

## Microsoft tenant

| Code | Meaning | Effect |
|---|---|---|
| `ms_tenant` | The domain belongs to a Microsoft Entra ID tenant (found through Microsoft's public OpenID discovery); `tenant_id` is filled | + ; Microsoft 365 evidence |
| `ms_federated` | The tenant federates sign-in to another identity provider (Okta, ADFS, Ping…); see `identity_provider` | + |

## Domain enrichment

Set with `deep=true`, or when the domain was already enriched in the background.

| Code | Meaning | Effect |
|---|---|---|
| `new_domain` | Registered less than 30 days ago (RDAP); sets `is_new_domain` | − strong |
| `young_domain` | Registered less than 180 days ago (RDAP) | − |
| `parked_domain` | The homepage is a parking or "for sale" page | − strong |
| `no_website` | No homepage answered within the time limit | − |
| `has_website` | A homepage answered | + weak |

## Crowd and operations

| Code | Meaning | Effect |
|---|---|---|
| `crowd_shared_suspected` | Many distinct people across many unrelated customer accounts use this domain, which looks like a shared provider. Sent to human review; it never flips the category by itself. | info, − |
| `dns_timeout` | DNS lookups timed out; the verdict comes from lists only and `degraded` is `true` | info, − |
| `dns_servfail` | The domain's own DNS servers failed (SERVFAIL/REFUSED), so it can't receive mail right now | → `invalid` |

## Using reasons in your code

```javascript
const codes = result.reasons.map((r) => r.split(':')[0]);

if (codes.includes('typo_suspected') && result.did_you_mean) {
  showHint(`Did you mean ${result.did_you_mean}?`);
}
if (codes.includes('ms_tenant')) {
  preselectConnector('microsoft');
}
```

See also: [Response fields](https://isbusinessemail.com/docs/response-fields) · [Workspace detection](https://isbusinessemail.com/docs/workspace-detection) · [Categories and policies](https://isbusinessemail.com/docs/categories-and-policies)
