Catch typo’d addresses and fake names at signup
Verification asks whether mail to an address will bounce. Screening asks the next question, once the domain’s MX resolves: did this person type their own address, and is the name they gave a real one? One call, and the answer is allow or review, never a block.
The endpoint
curl -X POST https://api.sortance.com/v1/screen \
-H "Authorization: Bearer $SORTANCE_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"jhon.smtih@gmail.com","name":"John Smith"}'- Auth: the same
Authorization: Bearer sk_live_...header as/v1/verify. A key comes from the quickstart. - Limits: the same body limit and the same rate limit as
/v1/verify, 600 requests a minute per key, shared between the two. - Meter: its own monthly allowance, described below.
Request
| Field | Type | Notes |
|---|---|---|
email | string | Required. Up to 320 characters, with the same rules as /v1/verify. |
name | string | Optional. The name the person typed into your form, up to 200 characters after trimming. Empty or whitespace is treated as absent; anything other than a string is a 400 invalid_request. Without a name, the name checks are skipped and no name reaches the model; the address checks and the domain check still run. |
Response
{
"email": "jhon.smtih@gmail.com",
"domain": { "syntax": "pass", "mx": "pass", "disposable": false },
"screening": {
"typo_suggestion": "john.smith@gmail.com",
"typo_source": "name_match",
"name_match": "typo",
"fake_name": "unlikely",
"domain_check": "ok",
"placeholder": false,
"recommendation": "review",
"reasons": ["local_part_typo_of_name"],
"degraded": false
},
"cost_usd": 0.0000076,
"latency_ms": 212,
"trace_id": "sc_..."
}An illustrative example: the person called themselves John Smith and typed jhon.smtih@. The domain passed the gate, so the model was asked and found the name plausible; the typo alone is enough for review, and the suggestion is what you would show them. gmail.com is a major consumer mailbox provider, so domain_check is ok without a model call and adds nothing to the cost.
| Field | Type | Meaning |
|---|---|---|
email | string | The address you sent, echoed back. Sortance never stores it; the screening record keeps only a keyed hash. |
domain.syntax | pass | fail | Whether the address is syntactically valid. |
domain.mx | pass | fail | unknown | Whether the domain's MX resolves. unknown means the lookup did not settle. |
domain.disposable | boolean | Whether the domain is a known disposable-mail service. |
screening.typo_suggestion | string | null | A corrected full address you can offer back on the form (“Did you mean john.smith@gmail.com?”), or null. It never echoes the name on its own. |
screening.typo_source | name_match | domain | null | What the suggestion corrects: the part before the @ against the name, or the domain against a common one. |
screening.name_match | match | typo | partial | mismatch | not_checked | How the part before the @ relates to the name. not_checked when no name was sent. |
screening.fake_name | likely | unlikely | uncertain | not_checked | Whether the name reads as fictional, a placeholder or a keyboard mash. not_checked when the model was not asked or did not answer. |
screening.domain_check | ok | typo_like | lookalike | placeholder | throwaway_like | uncertain | not_checked | What the domain looks like, judged from its name alone. See the domain check below. It never produces a typo_suggestion. |
screening.placeholder | boolean | The address looks like something typed to get past a form rather than a real mailbox. |
screening.recommendation | allow | review | Never deny. See below for what triggers review. |
screening.reasons | string[] | Every reason that applied, from the closed vocabulary below. Nothing you typed can appear here. |
screening.degraded | boolean | True when a model request was made and failed. The gate and code checks still answered; whichever label that request feeds (fake_name or domain_check) is not_checked. |
cost_usd | number | What the model requests cost, summed when both were made. 0 when the model was not called, and a cached domain answer adds 0. |
latency_ms | number | Wall-clock for the request. |
trace_id | string | sc_-prefixed. Quote it when asking about a screening. |
No probability or score appears in the response, and none is stored. Each model answer becomes one of the labels above and the number behind it is dropped.
Reasons
reasons only ever contains values from this list. The recommendation is review when any reason marked review is present, and allow otherwise. name_mismatch and name_email_inconsistent are informational: plenty of real people sign up with an address that has nothing to do with their name, so they never cause a review on their own.
| Reason | From | Triggers review | Means |
|---|---|---|---|
syntax_invalid | Gate | Yes | The address is not a valid address. |
address_blocklisted | Gate | Yes | The address itself is on your account's own blocklist (/v1/addresses/blocklist). Sortance keeps a keyed hash and a masked form, never the address. |
domain_blocklisted | Gate | Yes | The domain, or a parent of it, is on your account's own blocklist (/v1/domains/blocklist). |
domain_no_mx | Gate | Yes | The domain has no mail server, so mail to it cannot be delivered. |
disposable_domain | Gate | Yes | The domain is a known disposable-mail service. |
domain_allowlisted | Gate | No | The domain is on your account's own allowlist, so Sortance's disposable lists were not applied to it. Informational. |
address_allowlisted | Gate | No | The address itself is on your account's own allowlist (/v1/addresses/allowlist), so Sortance's disposable lists and your domain blocks were not applied to it. Syntax and MX still were. Informational. |
domain_typo | Code check | Yes | The domain looks like a misspelling of a common one, such as gmial.com for gmail.com. |
local_part_typo_of_name | Code check | Yes | The part before the @ is a near-miss of the name they typed, such as jhon.smtih for John Smith. |
placeholder_address | Code check | Yes | The address looks like something typed to get past a form rather than a real mailbox. |
name_mismatch | Code check | No | The part before the @ has nothing in common with the name. Informational. |
domain_suspicious | Model | Yes | Judged from the domain name alone, it looks like a misspelt mail provider, a brand lookalike such as paypa1.com, a placeholder such as asdfgh.com, or a throwaway inbox. No correction is suggested. |
fake_name_likely | Model | Yes | The name reads as fictional, a placeholder or a keyboard mash, such as Mickey Mouse or asdf asdf. |
name_email_inconsistent | Model | No | The address clearly names a different person. Informational. |
Order of operations
- The gate. The same local gate
/v1/verifyruns first: syntax, MX and disposable domains, plus your account’s own domain and address rules. Its signals are thedomainobject. A failure here addssyntax_invalid,address_blocklisted,domain_blocklisted,domain_no_mxordisposable_domain. A domain or address on your allowlist adds the informationaldomain_allowlistedoraddress_allowlistedinstead ofdisposable_domain. The model’s domain check still runs on it, as on any other domain. - Code checks, always. Plain code compares the address against common domains and against the name, and looks for placeholders. These are free and run even when the gate failed: a misspelt domain such as
gmial.comis often the reason there is no MX, and that is when the suggestion matters most. - The model, only after the MX resolves. Up to two separate requests to TypeSafe’s Jev model, described in the next two sections: a name check and a domain check. When both apply they run in parallel under one shared deadline.
The name check. Jev is asked whether the name is a real person’s and whether the address plausibly belongs to them, but only when all of these hold: the gate passed (valid syntax, an MX that actually resolved, not disposable), you sent anamewith at least one letter in it, and screening by model is on for your account. A DNS lookup that failed is not a resolved MX (domain.mxreadsunknown), so the model is not asked then either. Otherwisefake_nameisnot_checkedand that request costs nothing.
The domain check. The same conditions, except that no name is needed: the gate passed with an MX that resolved, and screening by model is on for your account. It is skipped when the domain is a major consumer mailbox provider (Gmail, Outlook, Yahoo and the like), which answersokwithout a call, and when the free code check already found a domain typo, in which casedomain_checkstaysnot_checkedbecausedomain_typoand its suggestion already cover it.
The domain check
The free code check catches misspellings of the mail providers on its list, such as gmial.com. The domain check covers what a list cannot: a misspelling of a provider that is not on it, a brand lookalike such as paypa1.com, a placeholder such as asdfgh.com, or a throwaway-style domain that is not on the disposable list. Jev cannot look anything up. It judges the string, choosing one of five descriptions, and that choice becomes screening.domain_check:
| Value | Means |
|---|---|
ok | The domain reads as a plausible real organisation's or mail provider's domain, including uncommon, new and non-English names. Also the answer, without a model call, for a major consumer mailbox provider such as gmail.com. |
typo_like | Looks like a misspelling of a well-known email provider that the free code check does not have on its list. |
lookalike | Imitates a well-known brand with swapped, added or look-alike characters, such as paypa1.com. |
placeholder | A placeholder, test, fake or keyboard-mash domain, such as asdfgh.com. |
throwaway_like | Looks like a temporary or throwaway inbox service that is not on the disposable list. |
uncertain | The model answered, but not confidently enough to label the domain either way. Adds no reason. |
not_checked | The domain was not judged: the conditions above did not all hold, the code typo check already fired, or the call failed. |
typo_like,lookalike,placeholderandthrowaway_likeadd the reasondomain_suspicious, which sends the signup toreview. Never a block.- A suspicious label needs a confident answer. A less confident one is reported as
uncertain, which adds no reason, and so is a less confidentlegitimate. - It never suggests a correction. Jev only picks a description, so this check never sets
typo_suggestion; a corrected domain only ever comes from the free code check. - Only the domain is sent. The request carries the domain name and the mail-provider label Sortance already derived for it, and nothing else: not the part before the @, not the full address, not the name. It is a separate request from the name check for exactly that reason.
- Cached per domain for 30 days. An answer is kept against the domain, as the label alone, and reused for every account. A cached answer means no model call and adds nothing to
cost_usd. A failed call is never cached.
The model deadline and degraded
The model calls have a total budget of 600 ms, including at most one retry when the model is overloaded and enough of the budget is left. When both the name check and the domain check apply, they run in parallel under that same 600 ms, not one budget each, and each succeeds or fails on its own. If a request times out, is overloaded, is rejected or answers in a shape Sortance does not accept, the request still returns 200: the label that request feeds (fake_name or domain_check) is not_checked, degraded is true, and everything else, including the other model request if it answered, is still there. A screening step that fails your signup form is a worse outcome than a missing label.
degraded means a model request was made and did not answer. When none was made, because the gate failed, the MX did not resolve, the account has it turned off, the domain was answered from the cache or the consumer-provider list, there was no name for the name check, or the model is not yet enabled on this deployment, degraded is false.
Its own meter
- A separate monthly allowance of 10,000 screenings by default, metered on the user who owns the key, across every key they hold. One request is one screening, whether the model was asked once, twice or not at all.
cost_usdis what the model requests cost Sortance: each is priced on its input tokens, and the two are added when both run. A domain answered from the cache costs 0.- Screenings do not count against your verification allowance, and verifications do not count against screenings.
- When it is spent,
/v1/screenreturns402with codescreening_quota_exceeded./v1/verifycarries on. - GET /v1/usage reports screenings used this month and the screening quota alongside the verification figures. See pricing.
Privacy
Screening can make two kinds of request to TypeSafe’s Jev model, and they carry different data:
- The name check. When you send a
nameand the address passes the gate, the address, the name and the results of the free code checks (including any suggested corrected address) are sent so Jev can judge whether the name is real. Nothing is sent for it when you omit the name. - The domain check. When the address passes the gate and the domain is not a major consumer mailbox provider or already in the cache, the domain name and its mail-provider label are sent, and nothing else: no address, no part before the @, no name. The answer is cached against the domain for 30 days.
That is the only data that leaves Sortance for screening. TypeSafe is listed as a sub-processor on the trust page.
Sortance stores neither the name nor the address. Each screening keeps a keyed hash of the address, whether a name was supplied, the labels and reasons, the model version, the cost and the time. No column holds the name, and nothing that could recover it (no hash, length or script). Only the labels describing how it compared to the address and whether it looked real are kept. Neither the name nor the address is written to a log.
The model step is on by default. If your contracts require it, it can be turned off for your account on request. That turns off both requests, the name check and the domain check (domain_check reads not_checked); the gate and the code checks keep working without them.
From an agent
The MCP server exposes the same call as the screen_contact tool, with email required and name optional. It returns the same response, counts against the same screening allowance, and its trace_id starts with sc_.