Integration Guide
Everything you need to call the Validator REST API from your application. Three endpoints, one response envelope: each call is a base check plus the components you enable, billed per component to your prepaid wallet.
1 Prerequisites
- A Validator account — sign up here (no subscription required).
- An API key from your tenant portal.
- Prepaid wallet balance for the operations you plan to run. See pricing.
2 Authentication
Pass your API key in the Authorization
header as a Bearer token.
There are no cookies, no sessions, and no OAuth flows for API calls.
Authorization: Bearer vlt_your_api_key_here
API keys begin with vlt_. Keep them secret — treat them like passwords.
Services catalog
Retail operations and prices come from Admin → Services. Each base check and each component you enable bills its own operation; the operations charged are returned in billing.operations.
| Service | Operation | Price | Method | Path | How to select |
|---|---|---|---|---|---|
| Phone check | check_phone |
2 credits ($0.0010) | POST | /v1/check/phone |
— (send the identifier only) |
| Carrier check | check_phone_carrier |
49 credits ($0.0245) | POST | /v1/check/phone |
`carrierCheck: true` |
| Ported check | check_phone_ported |
8 credits ($0.0040) | POST | /v1/check/phone |
`portedCheck: true` |
| Email check | check_email |
1 credit ($0.0005) | POST | /v1/check/email |
— (send the identifier only) |
| Email verify | check_email_verify |
20 credits ($0.0100) | POST | /v1/check/email |
`emailVerify: true` |
| Email premium | check_email_premium |
77 credits ($0.0385) | POST | /v1/check/email |
`emailPremium: true` |
| DNC screening | check_phone_dnc |
79 credits ($0.0395) | POST | /v1/check/phone |
`dncCheck: true` |
| check_phone_dnc_overlay | check_phone_dnc_overlay |
79 credits ($0.0395) | POST | /v1/check/phone |
`dncCheck: true` |
| Live line | check_phone_active |
28 credits ($0.0140) | POST | /v1/check/phone |
`activeCheck: true` |
| Caller name (CNAM) | check_phone_caller_name |
12 credits ($0.0060) | POST | /v1/check/phone |
`callerName: true` |
| Phone premium | check_phone_premium |
390 credits ($0.1950) | POST | /v1/check/phone |
`phonePremium: true` |
| Session start | session_start |
1 credit ($0.0005) | POST | /v1/sessions |
— (send the identifier only) |
| Email resend | email_send |
1 credit ($0.0005) | POST | /v1/sessions/resend |
`channel=email` |
| SMS OTP | sms_otp |
100 credits ($0.0500) | POST | /v1/sessions/resend |
`channel=phone` |
3 Phone check
POST /v1/check/phone
runs the base phone check — line type, carrier, city, state, county, ZIP, rate centre,
LATA, timezone and OCN — for US and Canadian numbers. Enable any of the
phone components
with a boolean key to go deeper — the components you tick are the request.
Request
{
"phone": "+18134696697",
"dncCheck": true
}
Response (200)
{
"ok": true,
"phone": {
"status": "valid",
"confidence": 0.98,
"e164": "+18134696697",
"lineType": "mobile",
"carrierName": "T-Mobile USA",
"reference": {
"lineType": "mobile",
"carrierName": "T-Mobile USA",
"ocn": "6529",
"state": "FL",
"city": "Tampa",
"county": "Hillsborough",
"zip": "33601",
"rateCenter": "TAMPA",
"lata": "952",
"timezone": "America/New_York",
"companyType": "wireless",
"smsGateway": false
},
"dnc": {
"callable": true,
"onFederalDnc": false,
"onStateDnc": false,
"onLitigator": false,
"onComplainer": false,
"resolvedState": "FL",
"stateDncApplicable": true,
"recommendation": "call"
},
"checked": ["base", "dncCheck"],
"source": "vendor"
},
"billing": {
"charged": true,
"operations": ["check_phone", "check_phone_dnc"],
"totalMicroUsd": 40500,
"totalCredits": 81
}
}
Every endpoint returns this one envelope: ok, a
phone
and/or email
half, and billing. Fields that were not checked are
omitted — never false
or null.
The totals above are illustrative; read
GET /v1/pricing
for live prices. totalMicroUsd
is the exact amount charged (1,000,000 micro = $1.00);
totalCredits
is the same amount in credits (1 credit = $0.0005).
| Field | Type | Description |
|---|---|---|
| status | string | valid / invalid / risky / unknown — whether the number is usable |
| confidence | number |
0 to 1 — how sure we are of status
|
| reason | string | Why the status is what it is, when there is one |
| e164 | string | Normalized E.164 number |
| lineType | string | mobile / landline / voip / tollfree / unknown |
| carrierName | string |
Carrier name; normalized when carrierCheck ran
|
| reference | object | Base check data: lineType, carrierName, ocn, state, city, county, zip, rateCenter, lata, timezone, companyType, smsGateway |
| portability | object |
With portedCheck, carrierCheck
or phonePremium:
ported, portedDate, carrierChanged, carrierNative, carrierServing, provenance
(npac / lrn_derived / internal / unknown)
|
| dnc | object |
With dncCheck
or phonePremium: callable, onFederalDnc,
onStateDnc, onLitigator, onComplainer, resolvedState, stateDncApplicable,
recommendation, overlayUnavailable
|
| active | boolean |
With activeCheck
or phonePremium: the line is live and assigned
to a subscriber. Omitted when unknown.
|
| callerName | string |
With callerName or phonePremium
|
| checked | string[] |
Component keys that produced an answer, including base
|
| unavailable | object[] |
{ option, reason }
for each component that produced no answer.
Omitted when everything ran; these are not billed.
|
| source | string | vendor / cache / internal — where the answer came from |
Request cadence
Each API key may send at most 2 requests per
second across every /v1
endpoint. A request over
that cadence returns 429
with a Retry-After
header; it does not run and is not
billed. Pace batches evenly instead of sending them in bursts. Identical requests
sent at the same moment (same number or address, same base check and upgrades)
share one lookup and receive the same answer; each completed check is billed.
4 Email check
POST /v1/check/email
runs the base email check — syntax, domain, MX, disposable addresses, role addresses
and blocklist signals. Enable emailVerify
for mailbox-level verification or emailPremium
for verification plus catch-all and abuse scoring. Both replace the base check
rather than adding to it.
Request
{
"email": "[email protected]",
"emailVerify": true
}
Response (200)
{
"ok": true,
"email": {
"status": "valid",
"confidence": 0.97,
"reason": "mailbox_verified",
"hasMx": true,
"disposable": false,
"roleBased": false,
"dnsblHit": false,
"mailboxVerified": true,
"checked": ["base", "emailVerify"],
"source": "vendor"
},
"billing": {
"charged": true,
"operations": ["check_email_verify"],
"totalMicroUsd": 10000,
"totalCredits": 20
}
}
Note the single operation in billing.operations:
emailVerify
replaced the base check, so the base is not
billed alongside it.
| Field | Type | Description |
|---|---|---|
| status | string | valid / invalid / risky / unknown — whether the address is usable |
| confidence | number |
0 to 1 — how sure we are of status
|
| reason | string | Detail reason from the check |
| hasMx | boolean | true when MX records exist |
| disposable | boolean | true for known disposable domains |
| roleBased | boolean | true for role addresses (info@, admin@) |
| dnsblHit | boolean | true when a blocklist match was found |
| mailboxVerified | boolean |
With emailVerify or emailPremium
|
| catchAll | boolean | With emailPremium |
| abuseScore | number | With emailPremium |
| checked | string[] |
Component keys that produced an answer, including base
|
| unavailable | object[] |
{ option, reason }
for each component that produced no answer;
not billed
|
| source | string | vendor / cache / internal |
5 Both in one call
POST /v1/check
accepts a phone, an email, or both, with any mix of phone and email components in one body.
Each half is validated against its own family, runs concurrently, and is billed on its own
outcome. Only the halves you supplied an identifier for appear in the response. Enabling a
component for a family whose identifier is missing is a 400 — the request asked for a check
that could not run.
Request
{
"phone": "+18134696697",
"email": "[email protected]",
"carrierCheck": true,
"emailPremium": true
}
Response (200)
{
"ok": true,
"phone": {
"status": "valid",
"confidence": 0.99,
"e164": "+18134696697",
"lineType": "mobile",
"carrierName": "T-Mobile",
"reference": { "…": "…" },
"portability": {
"ported": true,
"portedDate": "2024-04-05",
"carrierChanged": true,
"carrierNative": "AERIAL COMMUNICATIONS, INC.",
"carrierServing": "T-MOBILE USA, INC.",
"provenance": "npac"
},
"checked": ["base", "carrierCheck"],
"source": "vendor"
},
"email": {
"status": "valid",
"confidence": 0.97,
"reason": "mailbox_verified",
"hasMx": true,
"disposable": false,
"roleBased": false,
"dnsblHit": false,
"mailboxVerified": true,
"catchAll": false,
"abuseScore": 0,
"checked": ["base", "emailPremium"],
"source": "vendor"
},
"billing": {
"charged": true,
"operations": ["check_phone_carrier", "check_email_premium"],
"totalMicroUsd": 79500,
"totalCredits": 159
}
}
When a component has no answer
HTTP stays 200 and ok
stays true. The component is listed
in unavailable
with a reason, its fields are absent, and it
is not billed. A replacement tier that cannot complete returns the base result and charges
the base only — partial bundle output is never emitted.
{
"ok": true,
"phone": {
"status": "valid",
"confidence": 0.95,
"e164": "+18134696697",
"lineType": "mobile",
"reference": { "…": "…" },
"checked": ["base"],
"unavailable": [{ "option": "activeCheck", "reason": "vendor_unavailable" }],
"source": "internal"
},
"billing": {
"charged": true,
"operations": ["check_phone"],
"totalMicroUsd": 1000,
"totalCredits": 2
}
}
6 Components
Each component is a boolean key on the request. A component
adds
when it is a separate lookup on top of the base check, and
replaces
the base when its own operation already does the base's work — so you are never charged for the
same reference lookup twice. A component another selected component already includes is one
check, billed once: carrierCheck
covers portedCheck,
phonePremium
covers every phone upgrade, and emailPremium
covers emailVerify.
Phone components
| Key | What it does | Base | Includes | Response fields |
|---|---|---|---|---|
| carrierCheck | Normalized carrier — A normalized name for the current serving carrier, with ported status. | Replaces | portedCheck | carrierName, portability |
| portedCheck | Ported check — Live line type, ported status and ported date. | Replaces | — | portability |
| dncCheck | DNC screening — Federal and state DNC, litigator and complainer signals. | Adds to | — | dnc |
| includeTcpaScreening | Modifier on dncCheck — with dncCheck, upgrades the screening to the TCPA overlay (litigator and complainer signals). Ignored on its own. | Modifies | — | dnc |
| activeCheck | Live line — Confirms the number is live and assigned to a subscriber — not that the handset is answering right now. | Adds to | — | active |
| callerName | Caller name — Caller name lookup; also included with Premium. | Adds to | — | callerName |
| phonePremium | Premium — Every phone upgrade: carrier, ported status, live line, DNC, TCPA and caller name. | Replaces | carrierCheck, portedCheck, dncCheck, activeCheck, callerName | carrierName, portability, active, dnc, callerName |
Email components
| Key | What it does | Base | Includes | Response fields |
|---|---|---|---|---|
| emailVerify | Verify — Mailbox-level verification. | Replaces | — | mailboxVerified |
| emailPremium | Premium — Mailbox verification, deliverability, catch-all and abuse scoring. | Replaces | emailVerify | mailboxVerified, catchAll, abuseScore |
Prices per component are on the
pricing page
and in GET /v1/pricing. The
price calculator
(or the API call builder in your portal once you are signed in)
shows the exact request and response for any selection.
7 Error codes
| HTTP | Meaning | Action |
|---|---|---|
| 400 |
Bad request — no identifier, a component enabled for a family whose identifier
is missing ({ error, missing, options }),
or an unknown option
({ error, phoneOptions, emailOptions })
|
Fix the request body and retry |
| 401 | Unauthorized — bad or missing Bearer token | Check your API key and Authorization header |
| 402 | Not enough credits in the wallet for the whole selection — checked before any lookup runs | Top up your wallet via the portal |
| 403 |
Forbidden — the account is suspended, or
test: true
was sent and test mode is not enabled for the account. Nothing ran.
|
Contact support, or ask for test mode |
| 429 | Too many requests — the daily test-mode limit was reached. Nothing ran. | Retry tomorrow (UTC), or send a live check |
| 500 | A half failed before it could answer. Nothing was charged — debits happen only after every half has answered. | Retry; contact support if it persists |
| 503 |
Unavailable — a component we sell cannot be served right now
({ error, unavailable: [...] }).
The request is correct; nothing ran and nothing is charged.
|
Retry with exponential back-off, or drop the named component |
| 504 | A half did not finish within the vendor budget. Nothing was charged. | Retry later |
All error responses are
{ "ok": false, "error": "…" }
with a human-readable description, plus the fields named above where they apply.
A conclusive "status": "invalid"
is a successful check
(HTTP 200, ok: true), not an error.
8 Billing
Each check is billed to your prepaid wallet per component, using the operations in the services catalog. A completed check is billed in full, whether the answer is valid or invalid — you asked whether the identifier is usable and got a correct answer. Requests rejected before any work (400, 401, 402, 403, 429, 503) and checks that failed or timed out before answering (500, 504) are not charged.
The cache and our internal checks run before any vendor is
called. An answer settled there is still a completed check and bills the same
operation; the source
field on each half reports where
the answer came from — vendor,
cache
or internal
—
and confidence
(0 to 1) says how sure we are.
A component that produced no answer is listed in
unavailable
and is not billed. A replacement tier that cannot
complete returns the base result and charges the base only. Exactly what was charged is
always in billing.operations,
billing.totalMicroUsd
and billing.totalCredits; current rates are on the
pricing page
and in GET /v1/pricing.
Wallet top-up: Use the portal to add credits. Your balance is visible on the portal dashboard.
Credits: wallet balances and prices are shown in credits: 1 credit = $0.0005 (2,000 credits = $1), so a $25 top-up adds 50,000 credits. You pay top-ups in US dollars. Catalog prices are whole credits; custom pricing can carry up to three decimals.
totalMicroUsd
and each price's microUsd
stay the exact integers to reconcile against;
totalCredits
and credits
are the same amounts in credits.
GET /v1/pricing
publishes the rate as creditsPerUsd
and creditMicroUsd.
Integration test mode
Send "test": true
(or dry_run) on any tenant API call or LeadConnector
workflow/embed run to check that auth, routing, and the integration are wired —
without live lookups, wallet charges, or CRM tag writes.
An operator must enable test mode on your account first. You get 3 test calls per UTC day. Over that limit the API returns HTTP 429. If test mode is off, the API returns HTTP 403 and does not run a live (paid) check.
Responses use the same envelope — a synthetic
phone
and/or email
half carrying test: true. They are not real deliverability or
carrier results, and the billing
block reports charged: false
with no operations, because nothing was charged. A valid API key (or workflow secret + connected location)
is still required.
POST /v1/check/phone
Authorization: Bearer <api_key>
{
"phone": "+18134696697",
"test": true
}
Verification sessions
Use the sessions API when you need magic-link or OTP email verification plus optional
SMS phone confirmation. Magic links point at your
app_base_url
— not the validator host.
POST /v1/sessions— start session; sends initial emailGET /v1/sessions/status?token=…— poll progress-
POST /v1/sessions/confirm-email— confirm magic link or email OTP POST /v1/sessions/resend— resend email or request phone SMSPOST /v1/sessions/verify-phone— confirm SMS OTP
Caller name lookup is not a session call — enable the
callerName
component on POST /v1/check/phone.
See the full flow in this guide and the OpenAPI spec.