Checks · Phone
Phone check
Validate a phone number and score its fraud risk from a multi-source consensus of three independent telecom data sources. The extended tier adds a fourth voter plus an identity / breaches block.
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
phone |
string | required |
Phone in any common format; normalized to E.164. Bad input →
400 invalid_target.
|
extended_check |
boolean | optional |
Run the extended tier (4th voter + advanced block). Default
false.
|
use_storage |
boolean | optional |
Reuse your own prior result of equal-or-higher tier for free. Default
false.
|
external_id |
string | optional | Your own correlation id; stored with the check, echoed back in the response, and searchable in the mini-app history. Max 128 chars. |
curl -X POST https://antifrauder.app/api/v1/check/phone \
-H "X-API-Key: $ANTIFRAUDER_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7e3b1c9a-2f44-4d10-9a8e-c0ffee000001" \
-d '{ "phone": "+1 415 555 0132", "extended_check": true }'
Response
Every check returns the same envelope. result holds the phone-specific
view; the other fields describe the transaction.
{
"checkId": "665f1a9c8e4b2a0012ab34cd",
"externalId": "order-12345",
"charged": 22,
"extended": true,
"fromStorage": false,
"balance": 4798,
"result": {
"extended": true,
"intel": {
"number": {
"e164": "+14155550132",
"formatInternational": "+1 415 555 0132", "formatNational": "(415) 555-0132",
"country": { "code": "US", "name": "United States", "prefix": "1" },
"geo": { "city": "San Francisco", "zip": "94103", "timezone": "America/Los_Angeles" }
},
"valid": { "value": true, "agreement": { "count": 3, "total": 3 }, "source": "consensus" },
"line": {
"type": { "value": "mobile", "agreement": { "count": 3, "total": 3 }, "source": "consensus" },
"carrier": { "value": "T-Mobile", "agreement": { "count": 2, "total": 3 },
"source": "consensus", "variants": ["T-Mobile USA", "T-Mobile"] },
"network": { "mcc": "310", "mnc": "260", "mccmnc": "310260" },
"activity": { "value": "active", "agreement": { "count": 3, "total": 3 }, "source": "consensus" }
},
"portability": {
"ported": { "value": "no", "agreement": { "count": 2, "total": 3 }, "source": "consensus" },
"possiblyPorted": false, "currentCarrier": null, "currentNetwork": null
},
"roaming": { "status": "no", "carrier": null, "country": { "code": null, "name": null } },
"risk": {
"fraudScore": 12, "recentAbuse": false, "risky": false,
"leaked": false, "spammer": false, "prepaid": true, "disposable": false
},
"intel": {
"social": { "whatsapp": true, "telegram": true, "facebook": true, "instagram": false },
"breaches": { "count": 1, "first": "2019-03-01", "last": "2019-03-01", "list": ["Collection#1"] },
"callerType": "consumer", "scoreCluster": "high",
"identity": { "firstName": "John", "lastName": "Doe" }
},
"meta": { "extended": true, "sources": { "ok": 3, "total": 4 } }
},
"fraud": {
"score": 0, "level": "low",
"positive": [
{ "code": "PH-T-01", "key": "has_whatsapp", "name": "WhatsApp active", "description": "The number has an active WhatsApp account.", "points": -5 },
{ "code": "PH-T-02", "key": "has_google", "name": "Google account", "description": "The number is linked to a Google account.", "points": -5 }
],
"negative": []
},
"basicErrors": {},
"advancedError": null
}
}
Envelope fields
| Field | Type | Description |
|---|---|---|
checkId |
string | Stored check id — use it with GET /api/v1/result/:checkId. |
externalId |
string |
Your external_id from the request (or the stored one), echoed back.
Absent when none was supplied.
|
charged |
integer | Cents debited (0 when served from storage). |
extended |
boolean | Whether the extended tier ran. |
fromStorage |
boolean | true when reused from a prior check (no charge). |
balance |
integer | Your balance after the charge, in cents. |
result |
object |
The phone view: extended, intel,
fraud, basicErrors (per-source error map,
{} when all answered) and advancedError (the extended
source's error, or null).
|
Intel (result)
result.intel is the consolidated, presentation-ready view. We fuse the
sources into one canonical vocabulary (lowercase tokens like
mobile / active), so a field is named the same everywhere.
Each consolidated attribute carries its provenance:
{ "value": "mobile", "agreement": { "count": 2, "total": 3 }, "source": "consensus" }
source is consensus (2+ sources agree),
single (only one source supplies it — e.g. carrier or
reputation), or derived (inferred by us).
agreement.total is the number of providers that responded.
Abstentions count: for connectivity, porting and roaming, if most
responders answer “unknown” the value is unknown — we never assert a status
off a single source against an undecided majority.
(Exception: a single voip line type still wins — it is the strongest
fraud signal.)
| Field | Type | Description |
|---|---|---|
number |
object |
e164, formatInternational,
formatNational, country{code,name,prefix},
and geo{city,zip,timezone} (or null).
|
valid |
consolidated<boolean> | Whether the number is a valid, assignable MSISDN. |
line.type |
consolidated<string> |
mobile, landline, voip,
toll_free, premium_rate, short_code,
pager, unknown.
|
line.carrier |
consolidated + variants[] |
Carrier brand (or null); variants lists the
legal-name spellings seen across sources.
|
line.network |
object | null | { mcc, mnc, mccmnc } when a source resolved it. |
line.activity |
consolidated<string> |
active, inactive, off,
unknown. Live reachability — fixed lines are often
unknown (no mobile-network HLR record).
|
portability |
object |
ported (consolidated yes/no/unknown),
possiblyPorted, currentCarrier,
currentNetwork.
|
roaming |
object | null |
status (yes/no/unknown), carrier,
country{code,name}.
|
risk |
object | null |
Reputation: fraudScore, recentAbuse,
risky, leaked, spammer,
prepaid, disposable.
|
intel |
object | null |
Extended identity block — see Extended block.
null on a basic check.
|
meta |
object |
extended (was the 4th source queried) and
sources{ok,total} — how many providers responded.
|
Fraud score
One accumulative score, 0–100, capped — higher is riskier. Triggered
signals are split into negative (risk — raise the score) and
positive (trust — e.g. an active WhatsApp — lower it). Each signal carries
a stable code (e.g. PH-R-06), a name, a
description and its points, so you can explain the score.
Codes are stable — the table below lists the most significant ones.
| Level | Score |
|---|---|
| low | 0 – 25 |
| medium | 26 – 50 |
| high | 51 – 75 |
| critical | 76 – 100 |
Key phone signals
| Code | Signal | Points | Trigger |
|---|---|---|---|
PH-R-01 |
not_valid |
+100 | No source considers it valid. |
PH-R-06 |
voip_line |
+100 | Number type VOIP. |
PH-R-13 |
disposable_number |
+100 | Flagged disposable by the extended layer. |
PH-R-08 / PH-R-10 |
premium_rate / short_code |
+90 | Premium-rate / short-code line. |
PH-R-02 |
disconnected |
+50 | Number not reachable on the network. |
PH-R-05 |
unknown_connectivity |
+20 | No source could confirm the line is reachable. |
PH-R-18 |
no_social_presence |
+20 | Extended: no linked social/messenger accounts. |
PH-R-11 |
number_ported |
+10 | Recently ported. |
PH-T-01 / PH-T-02 |
has_whatsapp / has_google |
−5 | Active mainstream account lowers risk. |
Extended block (intel.intel)
The identity block lives at result.intel.intel. It is populated only when
extended_check: true and the 4th source responded; otherwise it is
null and advancedError explains why. (Reputation fields under
intel.risk come from a basic source and are present on basic checks too.)
| Field | Type | Description |
|---|---|---|
social |
object |
Platform → boolean (account exists): whatsapp,
telegram, facebook, instagram,
google, apple, amazon,
office365, twitter.
|
breaches |
object |
{ count, first, last, list[] } — known data-breach
exposure.
|
callerType |
string | null | business / consumer / null. |
scoreCluster |
string | null |
Extended trust band: very_low, low,
review, high.
|
identity |
object | null |
{ firstName, lastName } — a
potential name behind the number, low confidence. Each field
may be null; the whole block is null when no name is
resolved.
|
Retrieve & batch
Fetch a stored result without paying again, or check thousands of numbers from a CSV.
Both return your latest stored result for that value / id, or
404 not_found.
CSV batch
multipart/form-data with a file field (one number per row, max
10 000 rows) and an optional extended_check field. The cost is
pre-estimated against your balance; rows are charged individually as a background job
processes them.
curl -X POST https://antifrauder.app/api/v1/batch/phone \
-H "X-API-Key: $ANTIFRAUDER_KEY" \
-F "[email protected]" \
-F "extended_check=true"
{ "jobId": "665f...e1", "total": 1240, "validRows": 1187, "estimate": 26114 }
Poll job status: { jobId, status, total, done, charged, fileId? }.
When status: "completed", returns a short-lived signed download URL for the
result CSV: { url, expiresAt }.