Skip to content
ProofTell
Sign inSign up
Risk score

One score. Every reason.

One call takes a phone number, an email address and an IP address, in any combination, and answers with a score from 0 to 100, a verdict and the reason for every point. $4.50 per 1,000 full checks.

No model, no black box: the same signal results always give the same score.

Sign-up check
+1 500 555 0002
3 signals · rule set 2026-10-01
Deny
95 / 10040 + 35 + 20
0Allow · Review · Deny100
  • The IP address is a Tor exit node.
    +40
  • The email address uses a disposable provider.
    +35
  • The number is a VoIP line, easy to obtain anonymously.
    +20
How a score is built

Three steps, none of them hidden.

  1. 1

    The signals run in parallel

    Phone, email and IP are checked at the same time, within a time budget. A source that does not answer in time is reported as unavailable instead of holding up the call.

  2. 2

    Rules turn facts into points

    Each rule that matches adds a fixed number of points and one sentence saying why. The score is the sum, capped at 100.

  3. 3

    Your thresholds give the verdict

    A score under your review threshold is an allow; at or above your deny threshold, a deny; in between, a review. The defaults are 30 and 70, and both are yours to change.

The reasons

Every tell the score can find.

Each reason in a response carries a stable code, the points it added and a sentence a person can read. The codes are yours to branch on; the sentences are yours to show an analyst.

PhoneWhat the response says
phone_invalidThe phone number is not a valid number.
phone_voipThe number is a VoIP line, easy to obtain anonymously.
phone_non_personalThe number is a toll-free, premium-rate, shared-cost or pager line, not a personal one.
phone_type_unknownThe line type could not be determined.
EmailWhat the response says
email_invalidThe email address is not a valid address.
email_undeliverableThe mailbox does not exist or rejects mail.
email_disposableThe email address uses a disposable provider.
email_catch_allThe domain accepts any address, so the mailbox could not be confirmed.
email_riskyThe mailbox is risky, for instance over quota.
email_unverifiableThe mail server would not say whether the mailbox exists.
IPWhat the response says
ip_torThe IP address is a Tor exit node.
ip_vpnThe IP address belongs to a VPN provider.
ip_datacenterThe IP address belongs to a datacenter, not a residential or mobile network.
ip_not_routableThe IP address is private or reserved, not an internet address.
Across signalsWhat the response says
geo_mismatchThe phone number’s country differs from the IP address’s.
signal_unavailable_<signal>A signal you supplied could not run. Zero points; the verdict is raised to at least review.
Your controls

The rules are ours. The decision is yours.

Set per organization, by an owner or an admin, in the dashboard.

Thresholds

Where review starts and where deny starts, for your organization: 30 and 70 until you change them in the dashboard.

The outage rule

A supplied signal that cannot run raises the verdict to at least review. You can switch the raise off if your own fallback covers it.

Retention

Assessments are kept 90 days by default, for review and audit. Choose another period up to 365 days, or zero, and no assessment is stored.

History

Your most recent assessments are in the dashboard, each with its inputs, score, reasons and every signal’s raw result. Live and sandbox are kept apart.

For developers

Run it now, with a sandbox key.

This request uses the sandbox’s documented inputs: a VoIP number, a disposable address and a Tor exit node. With apt_test_ key it returns exactly the response shown, free.

With a live key, each signal that returns a result is charged at its own rate, and the response carries the cost of the call and of each signal.

Read the API reference
curl https://api.prooftell.com/v1/assess \
  -H "Authorization: Bearer $PROOFTELL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+15005550002",
    "email": "disposable@sandbox.prooftell.com",
    "ip": "203.0.113.66",
    "reference": "signup-48213"
  }'
const res = await fetch("https://api.prooftell.com/v1/assess", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.PROOFTELL_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone: "+15005550002",
    email: "disposable@sandbox.prooftell.com",
    ip: "203.0.113.66",
    reference: "signup-48213",
  }),
})
const { score, verdict, reasons } = await res.json()
import os, requests

res = requests.post(
    "https://api.prooftell.com/v1/assess",
    headers={"Authorization": f"Bearer {os.environ['PROOFTELL_KEY']}"},
    json={
        "phone": "+15005550002",
        "email": "disposable@sandbox.prooftell.com",
        "ip": "203.0.113.66",
        "reference": "signup-48213",
    },
)
result = res.json()
print(result["verdict"], result["score"])
Response
{
  "id": "asm_oxyq6rmz74bzsrxm",
  "mode": "test",
  "reference": "signup-48213",
  "score": 95,
  "verdict": "deny",
  "reasons": [
    { "code": "phone_voip", "points": 20,
      "message": "The number is a VoIP line, easy to obtain anonymously." },
    { "code": "email_disposable", "points": 35,
      "message": "The email address uses a disposable provider." },
    { "code": "ip_tor", "points": 40,
      "message": "The IP address is a Tor exit node." }
  ],
  "signals": {
    "phone": { "status": "ok", "cost": "0.0000", "result": { … } },
    "email": { "status": "ok", "cost": "0.0000", "result": { … } },
    "ip":    { "status": "ok", "cost": "0.0000", "result": { … } }
  },
  "ruleset": "2026-10-01",
  "cost": "0.0000",
  "currency": "USD",
  "createdAt": "2026-10-01T07:55:41Z"
}
What it costs

The sum of the signals that answered.

There is no price for the score itself. A call costs the signals that returned a result, each at its own rate. A signal that was unavailable costs nothing.

See the rate card
You sendPer checkPer 1,000
Phone intelligence$0.0010$1.00
Email verification$0.0030$3.00
IP intelligence$0.0005$0.50
Phone, email and IP together$0.0045$4.50
FAQ

About the score.

Do I have to send all three inputs?
No. Send any mix of phone, email and IP. The score is built from the signals you supplied, and you pay only for those.
Is the score machine learning?
No. It is a set of transparent rules with fixed weights, and the same signal results always give the same score. A model trained on someone else’s fraud does not explain itself to your auditor; a rule that says “the IP address is a Tor exit node” does.
Can I change the weights?
The rule set is the same for everyone, and it is versioned: when the rules change, the version on each response changes with them. What is yours to set is the pair of thresholds that turns a score into allow, review or deny.
What happens when a signal is unavailable?
You get a 200 with the score from the signals that ran. The missing signal is reported as unavailable, costs nothing, and adds a zero-point reason such as signal_unavailable_email. The verdict is raised to at least review, so an outage can never turn a deny into an allow. If none of the supplied signals ran, the call answers 503 signals_unavailable and nothing is charged.
Should I block automatically on “deny”?
That decision is yours. A verdict is information: it says how the evidence compares with the thresholds you set. Many teams let “allow” through, block “deny” and send “review” to a second step such as a one-time code or a manual check. If a decision has a legal or similarly significant effect on a person, the reasons in the response are what let you explain it and let a person review it.
How do I tie an assessment to my own records?
Send a reference, your own identifier for the check (an order number, a signup id). It is echoed in the response and stored with the assessment. Every assessment also has its own id, such as asm_8f3k2m9q.
Can I test without paying?
Yes. A pt_test_ key makes every call a sandbox call: simulated, deterministic and free. Documented inputs produce the main outcomes, an unavailable signal among them, so you can build and test your integration before a real key is involved.

Find the tell before it costs you.

Create an account, take a sandbox key, make your first call in minutes.