Reading a result
What valid, reason, normalized and details mean.
Every verification returns the same envelope. The top level is identical for
each country and document type; anything specific lives under details.
The envelope
| Field | Always present | Meaning |
|---|---|---|
type | Yes | Which document was checked: phone, national_id, tax_id or iban |
valid | Yes | Whether the value passed every check for its country and type |
input | Yes | The value exactly as you submitted it |
normalized | When valid | The value stripped of formatting — E.164 for phone numbers |
country | When resolved | Code, name and calling code |
formatted | Phone | National and international renderings |
reason | When invalid | Why the value was rejected |
details | Varies | Fields specific to the country and type |
Store normalized, not input. It is the form that compares and deduplicates
cleanly.
valid is not an error
Status codes describe the request
A value that fails its checks is a successful request: 200, with valid
set to false and a reason. Only a malformed request, a bad credential, an
empty balance or a throttle produce a non-2xx status.
const res = await fetch(url, options);
// The call worked; the number may still be nonsense.
if (!res.ok) throw new Error("request failed");
const result = await res.json();
if (!result.valid) {
showMessage(explain(result.reason));
}Reason codes
Reasons say why one value was rejected. They are add-only and never renamed, so matching on them is safe — but treat an unrecognised code as a generic rejection rather than crashing.
| Code | Meaning |
|---|---|
empty_input | Nothing was provided |
invalid_characters | Contains characters that do not belong in this format |
too_short | Too short for this format |
too_long | Too long for this format |
wrong_country | Belongs to a different country than the one resolved |
invalid_prefix | The prefix does not exist in the numbering plan |
unallocated_prefix | The prefix exists but is not in use |
unknown_area_code | The area code is not recognised |
invalid_year | The encoded year is not plausible |
checksum_failed | The check digits do not match the rest of the value |
unsupported_country | That country is not supported yet |
unsupported_type | Unknown document type |
Details by type
details follows type:
- Phone —
line_type(mobile,fixed_line,toll_free,premium_rate,shared_cost,unknown) and thecarrierthe range is allocated to, including whether it is an MVNO and the network it rides on. - Emirates ID — the parts the number encodes, including birth year.
- Tax registration number — structural breakdown of the TRN.
- IBAN — bank and branch identifiers, and the account number.
Carrier data reflects the range a number was allocated to. It does not follow portability, so it answers "which network was this range issued to" rather than "who serves this subscriber today".
The exact fields for each type are in the API reference, generated from the schema.
Checking cheaply
The United Arab Emirates is the only supported country today, so you can validate against its document formats — length, pattern and expected characters — client-side. Reject obviously malformed input in your own UI before making a call.