a ClearIP-compatible call-setup signal for Kamailio, OpenSIPS, dSIPRouter, Sansay, Oracle/Acme Packet & ProSBC
Add a supplementary call-screening signal to a session border controller using numbers.online. On an inbound INVITE your SBC asks for a low-confidence decision on the calling number and maps it to a standard SIP final response — 603 to decline, 302 to divert, 503/404 to allow — exactly the contract Sansay ERS, Oracle/Acme session agents, and ProSBC route-retry already speak.
Positioning. This is a low-confidence, supplementary signal — not a verdict, and not a compliance determination. Your SBC keeps every routing and blocking decision. numbers.online never asserts that a call is lawful, unlawful, safe, or spam. A 603 block is only ever returned for a deterministic fact (an invalid/unroutable calling number) or a government/licensed-authoritative one (a number that is DNC-listed or reassigned according to a configured data partner). The crowd/heuristic spam score can only ever raise an advisory
flagor — if you explicitly opt in — aredirect; it can never drive a block.
numbers.online is an HTTPS/JSON service behind Cloudflare; it is not a SIP endpoint, so your SBC cannot dial it directly over SIP. Instead you run a tiny Kamailio or OpenSIPS redirect-server shim (config below) inside your own network:
SBC ──INVITE──▶ numbers.online shim (Kamailio/OpenSIPS) ──HTTPS──▶ /api/v1/sbc/redirect
▲ │ │
└────── 603 / 302 / 503 / 404 ◀───── maps the JSON decision to a SIP final response ◀┘
SIP stays entirely inside your network; only a single outbound HTTPS dip leaves. This is the same affordance ClearIP ships as its "In-Line Proxy" — your SBC points at the shim with zero SBC-vendor cooperation, because SIP redirect/route servers are a generic SBC feature.
POST https://numbers.online/api/v1/sbc/redirect
Request (JSON):
{
"number": "+14155552671",
"allow_code": 503,
"spam_threshold": 80,
"redirect_threshold": 90,
"block_reassigned": false,
"block_invalid": true
}
| Field | Default | Meaning |
|---|---|---|
number (required) | — | The calling number (E.164 recommended). |
allow_code | 503 | SIP code for allow/route-advance: 503 (ClearIP default) or 404 (use for Oracle/Acme, Ribbon, Metaswitch — they re-INVITE on 503). |
spam_threshold | 80 | spam_score at/above which the decision becomes flag (advisory). |
redirect_threshold | (off) | Opt-in: spam_score at/above which the decision becomes redirect (302 auto-divert). |
block_reassigned | false | Treat reassigned yes (from a configured partner) as a block. |
block_invalid | true | Block an unparseable/invalid calling number (deterministic). |
verstat, called_number | — | Optional STIR/SHAKEN passthrough and dialed number (reserved). |
Response (JSON, uniform shape for known/unknown/valid/invalid numbers):
{
"schema_version": "2026-06-06",
"e164": "+14155552671",
"valid": true,
"decision": "allow",
"sip": { "code": 503, "reason": "Service Unavailable" },
"redirect_target": null,
"advisory": {
"spam_score": 12,
"confidence": "low",
"line_type": "mobile",
"verstat": "unknown",
"dnc_status": "unknown",
"reassigned_status": "unknown"
},
"signal": "supplementary",
"provider": "numbers.online",
"receipt_id": "nol_rec_8sd91kfh20aJ",
"insufficient_balance": false,
"as_of": "2026-06-06T00:00:00.000Z"
}
decision | sip.code | Your shim emits |
|---|---|---|
block | 603 | 603 Decline — drop the call. |
redirect | 302 | 302 Moved Temporarily with a Contact you set (your IVR/CAPTCHA/voicemail). numbers.online supplies no routing target. |
flag | 503/404 | The allow code, plus an elevated advisory spam_score your own logic may screen on. |
allow | 503/404 | The allow code — your SBC route-advances using its own table. |
sip.code is authoritative — your shim can emit it directly. decision is the human-readable label.
curl -s https://numbers.online/api/v1/sbc/redirect \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"+14155552671"}' | jq .decision
A key with the sbc_redirect use case is required — every lookup-entitled key has it (it is backfilled, and new keys get it by default).
Download /integrations/kamailio/numbers_online_sbc.cfg. It loads http_async_client + jansson, queries the endpoint with a sub-second $http_req(timeout), and maps the decision to sl_send_reply. Set your API key from a #!define/AVP loaded from the environment — never a committed literal. Kamailio is the recommended path when you need a tight (sub-500ms) call-setup budget.
Download /integrations/opensips/numbers_online_sbc.cfg. It uses rest_client (async(rest_post(...), resume)) + the json module. Note: rest_client timeouts are integer-seconds, so the OpenSIPS path's worst case is ~1s, not sub-500ms — size your SBC budget accordingly or use Kamailio for tighter budgets.
dSIPRouter is Kamailio underneath — apply the Kamailio recipe to its managed config. See /integrations/dsiprouter/README.md. Multi-tenant deployments mint a per-tenant sub-key through the MSP control plane so usage rolls up per customer.
These SBCs query a redirect server natively — point them at your shim:
session-agent + local-policy with redirect-action recurse; 302 recurses to Contact, 603 drops, 404 route-advances (set allow_code: 404).NAP + Route with per-response-code retry actions: 302 reroutes to the Contact list, 404/503 → Continue call (route advance), 603 → Stop call. Set allow_code: 404.decision: "allow" (never a 5xx) on its own timeout or error.X-SBC-Budget-Ms header (or budget_ms in the body, capped at 5000). We never bill a decision that overran your budget.Idempotency-Key header so retries bill at most once.insufficient_balance: true and still allows the call on the deterministic fields (no fresh CNAM dip). Top up to restore the full signal.For operator-grade auth you can require a signed request per key. Fetch the key's HMAC secret and toggle signing:
# Fetch the derived HMAC secret + scheme for the calling key
curl -s https://numbers.online/api/v1/account/signing -H "Authorization: Bearer YOUR_API_KEY" | jq .
# Require signed requests on this key
curl -s -X POST https://numbers.online/api/v1/account/signing \
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled":true}'
Then sign each request with HMAC-SHA256 over the canonical string and send three headers:
canonical = METHOD \n PATH \n sha256(body)hex \n timestamp \n nonce
X-Operator-Signature: hmac-sha256:<base64(hmac)>
X-Operator-Timestamp: <unix seconds> # must be within ±300s
X-Operator-Nonce: <unique per request> # single-use (replay defense)
The secret is HKDF-derived server-side from a deployment master + your immutable key id — nothing operator-specific is stored at rest, and it is re-derivable through your own key. The nonce is genuinely single-use for the full validity window, and the timestamp must be within ±300s. Auth fails closed: a missing/expired/replayed/invalid signature is a 401 (and your shim then fails open locally). RFC 9421 HTTP Message Signatures (alg="ed25519") is a documented future upgrade.
Path note for signed requests: the canonical
PATHis the exact request path. Sign and send the full/api/v1/sbc/redirectpath — not the/v1/sbc/redirectshort form — so the signature matches what the server canonicalizes.
Every SBC decision on a valid number emits a signed, PII-free receipt (long-retained). Export them as a signed robocall-mitigation evidence bundle — see the FCC evidence-bundle guide.
sbc_redirect use case (all lookup-entitled keys have it).mcp_call bundle. Top up at POST /api/v1/account/topup.?key= query-param fallback (where headers are impossible), use a dedicated, rotatable key.