/v1/domains
Screens a bare domain for existence, registration age, parking, and brand impersonation.
- METHOD
- POST
- BODY CAP
- 4 KiB
- CHECKS
- 5
- FINDING CODES
- 5
POST /v1/domains screens one domain name — not a full URL. Use it when you have a
company or website domain from a form field (not a link someone clicked) and want to know
whether it’s real, new, dormant, or impersonating something well known.
Request#
{ "domain": "paypa1.com" }
| Field | Type | Required | Notes |
|---|---|---|---|
domain |
string | yes | 1–253 characters. Not format-validated at the boundary — a garbage string still flows through to each check’s own guard, which either finds nothing to check or reports it. |
There are no optional fields yet.
Request body cap: 4 KB — far more than a single domain needs, with headroom for future optional fields.
Send the registrable (apex) domain, not a full hostname. RDAP registries index
registration records at the apex level — POST /v1/domains {"domain":"www.cloudflare.com"}
returns domain.not_registered, even though cloudflare.com itself is registered and
resolves cleanly. This isn’t a bug in the check; RDAP genuinely has no record of a domain
literally named www.cloudflare.com. Strip any subdomain before calling this gate.
Checks this gate runs#
domain.dns and domain.rdap make network calls (DNS-over-HTTPS and RDAP respectively);
domain.parked and domain.typosquat are pure computation. All run on every call.
| Check | What it does |
|---|---|
domain.dns |
DNS existence and nameserver lookup — see domain.not_found. Also feeds domain.parked. |
domain.rdap |
Registry lookup for registration status and age — see domain.not_registered, domain.newly_registered. |
domain.parked |
Nameserver match against known domain-parking services — see domain.parked. |
domain.typosquat |
Typo similarity to well-known brand domains — see domain.typosquat. |
domain.mx |
Mail-exchanger lookup for the domain — contributes data.mx only, no finding. |
domain.mx is data-only: a domain without MX records isn’t itself a problem for this
gate, so it emits nothing and just adds data.mx — an array of {host, preference}
sorted by preference — when the domain has usable MX records. data.mx is absent (never
null or empty) when the lookup fails, the domain has no MX records, or the domain
publishes an RFC 7505 null MX. Same shape and same rule as /v1/emails’s data.mx.
RDAP coverage varies by TLD — some registries don’t run a public RDAP server. A TLD with no coverage degrades to “we don’t know” (no finding either way, but also no confirmation), never a false claim of registration status.
Example#
curl -s -X POST https://api.stormdoor.dev/v1/domains \
-H "Authorization: Bearer sk_live_..." \
-H "content-type: application/json" \
-d '{"domain":"paypa1.com"}'
{
"ok": true,
"verdict": "suspect",
"findings": [
{
"code": "domain.typosquat",
"severity": "medium",
"message": "Domain \"paypa1.com\" looks like a typo of \"paypal.com\" (edit distance 1)"
}
],
"data": {
"dns": {
"resolves": true,
"nameservers": ["ns1.markmonitor.com", "ns2.markmonitor.com"]
},
"mx": [{ "host": "bh.markmonitor.com", "preference": 1 }],
"rdap": { "createdAt": "2000-10-20T23:17:07.000Z", "ageDays": 9473 }
},
"checks_run": ["domain.dns", "domain.mx", "domain.parked", "domain.rdap", "domain.typosquat"],
"checks_skipped": [],
"meta": { "gate": "domains", "ruleset": "reference-9f0e0204" }
}
Real response — paypa1.com (a digit substituted for a letter) resolves and is old, so RDAP
and DNS both come back clean; only the typosquat check on the domain string itself fires.
data.rdap/data.dns are always the gate’s own resolved values, even when nothing about
them is a finding.
See response format for how checks_skipped reasons work, and don’t
proxy that array to your own end users.
Finding codes#
Each code links to its own page with what it looks at, why it matters and what to do.