do-not-contact scrub + spoofing defence
Two endpoints for outbound voice/SMS senders — dialers, SBCs, and AI voice agents. Run a pre-call scrub on the number you are about to dial, and enrol your own verified business numbers so that calls spoofing your caller ID don't get charged to your reputation.
Positioning — read this first. The pre-call check is a supplementary signal, never a consent grant: a
NO_MATCHresult is not permission to call — you remain responsible for your own lawful basis. Enrolment records who you actually dialled so the absence of a record becomes evidence a reported call wasn't yours; it is not a compliance determination, a block, or a verdict on any call.
Paths. The canonical spellings are now
POST /api/v1/outbound/lookupandPOST /api/v1/outbound/enroll— the outbound mirror of/api/v1/inbound/lookup. The/api/v1/precall/*paths used throughout this guide are permanent aliases of the same handlers; everything below works unchanged on either spelling. Your verified numbers are also listable atGET /api/v1/account/phones, withPATCH /api/v1/account/phones/{e164}as a resource-shaped enrolment toggle.
POST /api/v1/precall/lookup) — right before you dial a number, check whether its verified owner has asked not to be marketed to (a consent-first preference we hold, not the official/government do-not-call registry, which remains yours to check), read a supplementary government/licensed compliance signal where a partner is configured, read an outbound dial-risk signal (the cost/abuse risk of dialing the destination), and read an indicative cost-to-reach estimate (retail per-minute voice and per-message SMS list prices). Free, bundled, fail-open.from) is one of your own enrolled verified business numbers, the pre-call check records a short-lived, hashed (from → to) edge. If a complaint later arrives about a call you never placed (because someone spoofed your number), the missing edge distinguishes your real, attested calls from the spoofed ones. The spoofed-call report is then treated as a supplementary signal about the spoofing and withheld from your own risk read — so the business whose identity was forged isn't the one penalised.Both endpoints require an API key with the precall use case. Every self-service key has it by default.
POST /api/v1/precall/lookup
X-API-Key: <your key>
Content-Type: application/json
{
"from": "+14155550100", # your calling number (E.164)
"to": "+14155552671", # the number you are about to dial (E.164)
"context": "outbound_voice" # outbound_voice | outbound_sms — picks the suppression list scrubbed
}
Response:
{
"schema_version": "2026-06-23",
"to": "+14155552671",
"dnc": "NO_MATCH", // SUPPRESS | NO_MATCH | UNKNOWN
"dnc_channel": "voice", // which suppression list answered: voice | sms (from `context`)
"dnc_note": "Supplementary signal only. NO_MATCH is not consent; you remain responsible for your own lawful basis to call.",
"compliance": { /* supplementary compliance signal, where a licensed provider is configured */ },
"dial_risk": { // outbound cost/abuse risk of dialing `to` (supplementary)
"risk": 6, // 0–100
"level": "low", // low (<40) | medium (40–69) | high (≥70)
"structural_type": "intl_premium",// what `to` resolved to (null if no range matched)
"reason_codes": ["structural:intl_premium"],
"note": "Supplementary signal: low-confidence estimate of the cost/abuse risk of DIALING this destination …"
},
"cost_estimate": { // indicative retail cost to reach `to`; both channels, null where uncovered
"currency": "USD",
"note": "Supplementary signal: indicative RETAIL price to reach this destination …",
"voice": { // priced per minute (null if no voice deck covers `to`)
"unit": "per_minute",
"min_usd": "0.0072", "avg_usd": "0.0146", "max_usd": "0.32",
"network_type": "mobile", // the line-type filter applied (null = unfiltered)
"providers": 3, // distinct providers behind the aggregate
"rate_count": 5,
"international": { "min_usd": "0.013", "avg_usd": "0.018", "max_usd": "0.32" },
"local": { "min_usd": "0.0072", "avg_usd": "0.0072", "max_usd": "0.0072" },
"breakdown": [ // named, displayable providers only (cheapest avg first)
{ "name": "DIDWW", "domain": "didww.com", "min_usd": "0.0072", "avg_usd": "0.0072", "max_usd": "0.0072",
"international": null, "local": { "min_usd": "0.0072", "avg_usd": "0.0072", "max_usd": "0.0072" } }
]
},
"sms": { // priced per message (null if no SMS deck covers `to`)
"unit": "per_message",
"min_usd": "0.0667", "avg_usd": "0.0998", "max_usd": "0.2575",
"network_type": "mobile", "providers": 2, "rate_count": 3,
"person": { "min_usd": "0.0667", "avg_usd": "0.0667", "max_usd": "0.0667" }, // P2P decks
"application": { "min_usd": "0.1329", "avg_usd": "0.1329", "max_usd": "0.1329" }, // A2P decks
"breakdown": [ { "name": "DIDWW", "domain": "didww.com", "min_usd": "0.0667", "avg_usd": "0.0998", "max_usd": "0.1329",
"person": { "min_usd": "0.0667", "avg_usd": "0.0667", "max_usd": "0.0667" },
"application": { "min_usd": "0.1329", "avg_usd": "0.1329", "max_usd": "0.1329" } } ]
}
},
"enrolled": true, // is `from` an enrolled number of yours?
"provenance_recorded": true, // was a provenance edge written for this call?
"provenance_note": "…",
"edge_id": "nol_edge_…", // present only when an edge was recorded
"ttl_seconds": 604800 // edge lifetime (7 days)
}
dnc — the first-party do-not-contact preference of the destination's verified owner, held by Numbers Online — not the government/licensed do-not-call registry (that is the separate compliance field). SUPPRESS (the verified owner asked not to be marketed to — remove them), NO_MATCH (no suppression on record — not consent), or UNKNOWN (not resolvable). The scrub is per channel: context: "outbound_sms" is answered from the SMS suppression list, anything else from voice (dnc_channel echoes which). Channel opt-ins are independent — the same number can be SUPPRESS for voice and NO_MATCH for SMS, or the reverse. (Before 2026-06-11 every scrub was answered from the voice list regardless of context.)dial_risk — a supplementary read on the cost/abuse risk of dialing to (separate from dnc, which is about the owner's marketing preference). level bands the risk score (0–100) into low / medium / high; structural_type tells you why — e.g. intl_premium (a +1 number that looks domestic but routes international/high-cost), premium_prs (premium/special-rate), satellite, intl_network, or unallocated_dialable. reason_codes lists the legible drivers. It is a low-confidence signal, never a block verdict — you decide whether to dial. null if scoring failed (treat as no signal).cost_estimate — an indicative read on what it costs to reach to, distinct from dial_risk (risk points) — actual retail money. We price both channels regardless of context: voice is per minute, sms per message; each is null when no rate deck covers the destination, and the whole object is null when neither does. All amounts are decimal USD strings (money is integer micro-USD internally). Each channel carries a cross-provider min/avg/max, the channel's lane splits (voice → international/local; SMS → person (P2P) / application (A2P)), and a breakdown[] of named, publicly-displayable providers cheapest-average first (anonymous sources stay folded into the aggregate). network_type is the line-type filter applied (e.g. mobile, premium), null when unfiltered. These are published list prices aggregated across providers — not wholesale interconnect cost and not a quote; your real price depends on your own provider and contract.UNKNOWN (and dial_risk / cost_estimate null) and proceed under your own lawful basis.Enrol once per number you own; thereafter every pre-call check from that number records a provenance edge.
POST /api/v1/precall/enroll
X-API-Key: <your key>
Content-Type: application/json
{
"number": "+14155550100", # one of YOUR verified business numbers
"enrolled": true # false to revoke
}
Response:
{
"ok": true,
"number": "+14155550100",
"enrolled": true,
"attestation": "By enrolling, you attest that this number only places calls after a Numbers Online outbound pre-call lookup. We treat the absence of a matching pre-call edge as a supplementary signal that a complaint may concern a spoofed call, not one you placed. Enrollment is revocable and abuse-monitored. Supplementary signal only — not a compliance determination."
}
Requirements:
403).404 otherwise). Verify a business number first via POST /api/v1/account/verify-phone/start → /confirm."enrolled": false to stop recording edges.matched (a pre-call edge confirms you dialled this receiver), mismatched (enrolled owner + known receiver but no edge → the complaint may concern a spoofed call → withheld from your read and feeds a bounded spoofing signal), or null (not applicable).Both ends of every edge are stored only as SHA-256 hashes of the canonical E.164; raw numbers are never logged or stored. Edges are server-only and expire after 7 days. The provenance label on a report is a supplementary signal — not proof — and is never surfaced to either party as a verdict.