URL: /products/email-intelligence

---
title: Email Intelligence
description: "Email reputation API: score any email or domain for disposable addresses, mail provider, MX deliverability, SPF/DMARC, blacklists, domain age and TLD reputation in one POST /v1/email call."
---

Score any email before it signs up. One `POST /v1/email` returns a single calibrated risk score plus the underlying signals — disposable status, mail provider, MX deliverability, SPF/DMARC posture, DNSBL hits, domain age, and TLD reputation — for any email or domain.

Signup and checkout forms get flooded with throwaway and undeliverable addresses, and a naive regex passes every one of them. By the time a bounce or chargeback tells you the email was junk, the fake account or order already exists. Email Intelligence resolves the email to a domain-level reputation and hands you the booleans to allow, flag, or block — before you create the account.

## When to use it

Call `/v1/email` at the point of submission — signup, checkout, lead capture, waitlist — whenever an email is the thing you don't want to be fake. It's a server-side reputation lookup, so you call it with a secret key from your backend, read the result, and gate on it before you write the record.

It is **not** a verification email and **not** an SMTP probe. It checks the domain — disposable status, MX/A-record deliverability, DNSBL listings, and RDAP domain age — without sending mail or touching the recipient's mail server, so it never tips off the user or risks deliverability damage. Confirming a specific mailbox exists still requires your own double opt-in flow.

## How it works

<Steps>
  <Step title="POST the email or domain">
    Send `POST /v1/email` with a Bearer API key and a JSON body of `{"email": "..."}`. You can pass a full address (`user@domain.com`) or just the bare domain (`domain.com`). Reputation is keyed on the registrable domain, so mail hosted on a subdomain inherits the parent — `treasurer@i.gs.com` scores against `gs.com`.
  </Step>

  <Step title="FormShield resolves the reputation">
    FormShield combines mailbox signals (role address, free provider, local-part patterns) with domain signals — disposable status, mail-provider class, MX deliverability, SPF/DMARC posture, DNSBL listings, domain age, TLD reputation, and popularity — into one score. Domain-level checks are cached for 30 days, so repeat lookups on the same domain return instantly.
  </Step>

  <Step title="Gate on the signal">
    The response returns a normalized `risk` in `[0,1]`, a coarse `subverdict` (`clean` / `suspicious` / `malicious` / `unknown`), a recommended `action` (`allow` / `review` / `step_up` / `block`), and the individual signals. Gate on `action` or `risk` for a single decision, or read the booleans (`disposable`, `deliverable`, `blacklisted`) for precise control — no further round trips.
  </Step>
</Steps>

## Quickstart

Send a single request with the submitted address. Replace `fs_live_xxx` with your API key.

```bash
curl -X POST https://api.formshield.dev/v1/email \
  -H "Authorization: Bearer fs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"email": "newuser@mailinator.com"}'
```

A bare domain works too — drop the local part and pass `{"email": "mailinator.com"}` to get the same domain-level reputation.

The response is wrapped in the standard [envelope](/api-reference/introduction#response-envelope). The `signal` block holds the reputation; `data.cached` tells you whether it came from the 30-day cache.

```json
{
  "version": "1",
  "data": {
    "signal": {
      "risk": 0.785,
      "subverdict": "malicious",
      "action": "block",
      "evaluated": true,
      "flags": ["email_disposable"],
      "email": "newuser@mailinator.com",
      "domain": "mailinator.com",
      "registrable_domain": "mailinator.com",
      "role_account": false,
      "free_provider": false,
      "alias": false,
      "gibberish_score": 0.08,
      "disposable": true,
      "deliverable": true,
      "blacklisted": false,
      "blacklists": null,
      "domain_age_days": 8423,
      "new_domain": false,
      "mx_provider": null,
      "mx_tier": "unknown",
      "spf": "hardfail",
      "dmarc_policy": "reject",
      "tld_tier": "neutral",
      "domain_rank": null
    },
    "cached": false
  },
  "error": null,
  "metadata": {
    "request_id": "req_a1b2c3d4e5f6",
    "processing_time_ms": 412
  }
}
```

Gate on `signal.action` (or `signal.risk` / `signal.subverdict`) for a single allow / review / step-up / block decision, or read `signal.disposable`, `signal.deliverable`, and `signal.blacklisted` directly for precise control.

## Signals

The `signal` block carries one normalized `risk`, a coarse `subverdict`, and the underlying checks as individual fields. Read the booleans for precise control, or the `risk`/`subverdict` for a single verdict.

<ResponseField name="risk" type="float">
  Normalized risk in `[0,1]`. Higher is worse. Fold this into your own threshold, or use `subverdict` for a coarser read.
</ResponseField>

<ResponseField name="subverdict" type="string">
  Coarse verdict: `clean`, `suspicious`, `malicious`, or `unknown`.
</ResponseField>

<ResponseField name="action" type="string">
  Recommended signup-gate action derived from `risk`: `allow`, `review`, `step_up`, or `block`. Use it directly, or set your own thresholds on `risk`.
</ResponseField>

<ResponseField name="registrable_domain" type="string">
  The registrable domain (eTLD+1) the reputation is keyed on. Subdomains inherit it — `i.gs.com` resolves to `gs.com`.
</ResponseField>

<ResponseField name="flags" type="string array">
  The named tells that fired, e.g. `email_disposable`, `email_undeliverable`, `email_domain_young`, `email_domain_very_young`.
</ResponseField>

<ResponseField name="disposable" type="boolean">
  `true` when the domain (or its parent domain) is a known throwaway / temporary mailbox provider — the addresses people use to dodge one account per person. Surfaced as the `email_disposable` flag.
</ResponseField>

<ResponseField name="deliverable" type="boolean">
  Whether the domain can receive mail. Reflects MX records, falling back to an A record as an implicit MX per RFC 5321. A domain with neither comes back `deliverable: false` with an `email_undeliverable` flag.
</ResponseField>

<ResponseField name="blacklisted" type="boolean">
  Set from live DNSBL queries against Spamhaus DBL, SURBL, URIBL, and Barracuda. Error / test responses (`127.255.255.x`) are filtered so rate-limited lookups don't false-positive.
</ResponseField>

<ResponseField name="blacklists" type="string array">
  Exactly which DNSBL zones the domain hit, or `null` when it hit none.
</ResponseField>

<ResponseField name="domain_age_days" type="integer">
  Registration age resolved via RDAP. Freshly registered domains are a strong fraud tell — recently registered domains raise the `risk` score and set `new_domain`.
</ResponseField>

<ResponseField name="new_domain" type="boolean">
  `true` when the domain is recently registered. Combined with a legitimate mail provider, this is a classic cold-email / throwaway-domain pattern.
</ResponseField>

<ResponseField name="mx_provider" type="string">
  The detected mail-hosting provider behind the domain's MX records (e.g. `Google Workspace`, `Microsoft 365`, `Fastmail`, `Namecheap Private Email`), or `null` when the MX matches no known provider.
</ResponseField>

<ResponseField name="mx_tier" type="string">
  Coarse reputation tier of the mail provider: `high`, `medium`, `low`, or `unknown`. Enterprise mail and security gateways score high; cheap registrar-bundled mailboxes score low.
</ResponseField>

<ResponseField name="spf" type="string">
  SPF policy published at the domain: `hardfail` (`-all`), `softfail` (`~all`), `neutral`, or `none` (no SPF record).
</ResponseField>

<ResponseField name="dmarc_policy" type="string">
  Published DMARC policy: `reject`, `quarantine`, or `none`. Enforced DMARC (`reject` / `quarantine`) is a positive trust signal; `null` when no DMARC record is present.
</ResponseField>

<ResponseField name="tld_tier" type="string">
  TLD reputation: `trusted` (verified-eligibility TLDs like `.gov` / `.edu` / `.bank`), `neutral`, or `elevated` (cheap, high-abuse new gTLDs).
</ResponseField>

<ResponseField name="role_account" type="boolean">
  `true` for role / no-reply local-parts (`info@`, `admin@`, `noreply@`, …) rather than a person's mailbox.
</ResponseField>

<ResponseField name="free_provider" type="boolean">
  `true` when the domain is a free consumer webmail provider (Gmail, Outlook, Yahoo, iCloud, …). Informational, not a penalty.
</ResponseField>

<ResponseField name="alias" type="boolean">
  `true` when the address uses plus/sub-addressing (`user+tag@domain`).
</ResponseField>

<ResponseField name="domain_rank" type="integer">
  Popularity rank bucket (higher = more popular / established), or `null` when the domain isn't in the known-domain list.
</ResponseField>

<Note>
  `evaluated` is `true` when the lookup ran and produced a signal. `data.cached` reports whether the result came from the 30-day per-domain cache (`true`) or was computed fresh on this request (`false`).
</Note>

## Common questions

<ParamField path="How do I validate an email with an API at signup?" type="answer">
  Call `POST /v1/email` with the submitted address and a Bearer API key before you create the account. Read `disposable`, `deliverable`, and `blacklisted` from the returned signal to block throwaway or undeliverable addresses, or use the `risk` / `subverdict` fields for a single allow / flag / block decision. It accepts a full email or a bare domain, and results are cached per-domain for 30 days.
</ParamField>

<ParamField path="Does FormShield send a verification email or do SMTP checks?" type="answer">
  No. `/v1/email` is a reputation lookup, not a verification email. It checks the domain — disposable status, MX/A-record deliverability, DNSBL listings, and RDAP domain age — without sending mail or probing the recipient's SMTP server, so it never tips off the user or risks deliverability damage. Confirming a specific mailbox exists still requires your own double opt-in flow.
</ParamField>

<ParamField path="What does it cost per lookup?" type="answer">
  Each `/v1/email` lookup is 3 credits. Cached domains (a repeat lookup within the 30-day window) return from cache as `cached: true`, and the first lookup of any domain warms that cache for everyone hitting it next.
</ParamField>

## Next steps

<CardGroup cols={2}>
  <Card title="API reference" icon="code" href="/api-reference/introduction">
    The response envelope, authentication, and error codes shared by every endpoint.
  </Card>
  <Card title="Server reporting" icon="server" href="/guides/server-reporting">
    Capture crawlers and AI bots from your origin with `/v1/report`.
  </Card>
</CardGroup>
