numbersonline
API reference/Integration guides/FreeSWITCH / FusionPBX integration
PBX & softphones

FreeSWITCH / FusionPBX integration

inbound caller name & risk signal

Call numbers.online when an inbound call arrives to label it with a caller name (CNAM) and an optional risk tag before it reaches an agent or extension. There are two ways to wire it, depending on what your build can do:

PathEndpointAuthYou getBest for
mod_cidlookup (simplest)/v1/cid (plain text)?key= query paramA caller name, optionally spam-taggedThe fastest drop-in; no scripting
Lua dialplan (full)/v1/lookup (JSON)Authorization: Bearer headerCNAM and spam_score, line type, carrier; verified TLSRouting on score, header auth, richer display

Positioning. numbers.online provides a supplementary identity and risk signal. Your PBX keeps every routing, labelling, and blocking decision and remains responsible for compliance. The API never asserts a call is lawful, unlawful, "safe", or "spam" — spam_score is a labelled, low-confidence signal. Whether any risk wording reaches a caller name is an operator opt-in (you choose the tag text and threshold).

Artifacts referenced here:

  • public/integrations/freeswitch/cidlookup.conf.xml — the mod_cidlookup config
  • public/integrations/freeswitch/numbers_online_cnam.lua — the Lua dialplan script

Get an API key

POST https://numbers.online/v1/account/signup
{ "email": "[email protected]", "name": "Your Company" }

The key is returned exactly once — store it. Free tier: 60 requests/min, unbilled. Full docs: https://numbers.online/docs. Use a dedicated key for the PBX (see Security notes).


Path 1 — mod_cidlookup (simplest)

mod_cidlookup substitutes the inbound number into a URL and treats the HTTP response body, verbatim, as the caller-ID name. Our /v1/cid endpoint always returns text/plain — a name, an optional spam-tag prefix, or the literal UNAVAILABLE — which is exactly what mod_cidlookup expects.

Why /v1/cid and not /v1/lookup

  • mod_cidlookup cannot send HTTP headers, so it cannot use Bearer/X-API-Key auth. The key must travel as the ?key= query param, which only /v1/cid accepts.
  • /v1/cid returns plain text. /v1/lookup returns JSON, and mod_cidlookup would paste a raw JSON blob (or an error body) into the caller name. Never point mod_cidlookup at /v1/lookup.

Install

  1. Copy cidlookup.conf.xml to conf/autoload_configs/cidlookup.conf.xml.

  2. Set the url param's YOUR_API_KEY to your key, and country=US to your default if bare national numbers are not NANP. In XML, & is written &:

    <param name="url"
      value="https://numbers.online/v1/cid/${caller_id_number}?key=YOUR_API_KEY&amp;country=US"/>
    
  3. The shipped config also sets cache=true, cache-expire=300, curl-timeout=2000 (a 2-second fail-open cap). Reload:

    fs_cli -x 'reload mod_cidlookup'
    

${caller_id_number} is the only substitution token mod_cidlookup offers; it is the raw inbound number (often 10/11-digit national, not E.164). /v1/cid accepts that loose format and resolves it via &country.

Optional spam tag

Risk wording is opt-in. Append to the url value (remember &amp;):

&amp;spam_tag=Spam%3F&amp;spam_threshold=80

When a name scores at/above the threshold, the body is returned prefixed (Spam? ACME CORP); with no name but a crossing score, the tag alone is returned. Omit spam_tag entirely to never surface risk text in the caller name. Spam%3F is the URL-encoded form of Spam?.

FusionPBX GUI wiring

In Dialplan → Inbound Routes, edit the route and add ordered inline actions so the lookup runs before the bridge to an extension:

set   caller_id_name=${cidlookup(${caller_id_number})}    inline=true
set   effective_caller_id_name=${caller_id_name}          inline=true

In the FusionPBX action editor: Application = set, Data = the right-hand side, and tick Inline. Place these above the existing bridge/transfer action. The cidlookup API function reads the config you installed above.


Path 2 — Lua dialplan (full JSON + header auth + spam score)

Use this when you want the spam_score for routing, header-based auth, and verified TLS. The script (numbers_online_cnam.lua) calls /v1/lookup (JSON) and sets:

Channel variableMeaning
effective_caller_id_nameCNAM, optionally prefixed with your spam tag
numbers_online_spam_score199 integer, for downstream routing
numbers_online_cnamthe raw CNAM string (no tag prefix)

Why Lua and not mod_curl

mod_curl and the dialplan curl app cannot set an Authorization header (open FreeSWITCH issue #1377). The script therefore shells out to the system curl binary via io.popen with -H "Authorization: Bearer ..." and --max-time 2.

Install

  1. Copy numbers_online_cnam.lua to your mod_lua script directory (conf/scripts/numbers_online_cnam.lua).

  2. Add globals to conf/vars.xml — the key is never hardcoded in the script:

    <X-PRE-PROCESS cmd="set" data="numbers_online_key=YOUR_API_KEY"/>
    <!-- optional risk-tag opt-in -->
    <X-PRE-PROCESS cmd="set" data="numbers_online_spam_tag=Spam?"/>
    <X-PRE-PROCESS cmd="set" data="numbers_online_spam_threshold=80"/>
    

    Leave numbers_online_spam_tag unset to never prefix the caller name with risk text. Reload vars with fs_cli -x 'reloadxml'.

  3. Confirm a system curl binary is on FreeSWITCH's PATH.

FusionPBX GUI wiring

In Dialplan → Inbound Routes, add early in the route (before the bridge):

lua   numbers_online_cnam.lua   inline=false

Action editor: Application = lua, Data = numbers_online_cnam.lua, leave Inline unticked. Downstream actions can branch on ${numbers_online_spam_score}, e.g. send a high score to an IVR challenge or voicemail instead of ringing through — that routing decision is entirely yours.


Multi-tenant (FusionPBX domains / MSPs)

If you run multiple tenants, issue a per-tenant sub-key through the MSP control plane so usage, billing, and rate limits are attributed per tenant:

POST https://numbers.online/v1/account/tenants          # create a tenant
POST https://numbers.online/v1/account/tenants/{id}/keys # mint a per-tenant sub-key

Then template the per-domain key into that domain's cidlookup.conf.xml url, or set a domain-scoped numbers_online_key global, so each FusionPBX domain dips with its own key. Never share one key across tenants you bill separately.

Security notes

  • Query-param key visibility (Path 1). With mod_cidlookup the key rides in the URL (?key=), which can land in proxy, gateway, or access logs. Treat it as low-trust: use a dedicated key for the CID endpoint and rotate it on a schedule. The Lua path avoids this by sending the key in a header.
  • mod_cidlookup disables TLS verification. It sets CURLOPT_SSL_VERIFYPEER=0, so although the request is HTTPS, the certificate is not validated against a CA. This is a mod_cidlookup limitation, not a numbers.online setting. The Lua path uses the system curl, which verifies TLS normally — prefer it where peer authentication matters.
  • Never log raw numbers or keys. The shipped artifacts keep both out of stdout and logs; keep it that way in any local customisation.

Fail-open guidance

Caller-name lookup sits on the call-setup path, so it must never block or delay a call:

  • Keep curl-timeout/--max-time tight — 2s recommended, 5s max.
  • On any timeout or error, both paths proceed with the call unchanged: mod_cidlookup yields no name; the Lua script returns without touching the channel.
  • Invalid or unknown numbers return UNAVAILABLE (Path 1) or null fields (Path 2), never an error you must handle on the call leg.

One mod_cidlookup caveat to know about (Path 1 only). mod_cidlookup uses the raw HTTP response body as the caller name and only rejects the literal UNKNOWN / UNAVAILABLE sentinels. The numbers.online API itself never returns anything but plain text on this endpoint — but infrastructure in front of it can: if the service is unreachable, Cloudflare may answer with an HTML error page faster than your curl-timeout fires, and mod_cidlookup would briefly display that markup as a caller name. This is inherent to how mod_cidlookup consumes responses, and it affects every URL-based CNAM source the same way. If that residual risk matters to you, use the Lua path instead: the script pattern-matches the JSON response and treats anything that doesn't look like a lookup result — including any HTML error page — as "no data" (fail open, channel untouched).

Pricing

EventPrice
Lookup with a fresh CNAM dip$0.004
Cache hit, or no CNAM supplier$0.002
Invalid numberfree

Free tier is unbilled at 60 requests/min. Caching (cache-expire / max_cache_age) directly trims cost on repeat callers.

Where to get help

Docs: https://numbers.online/docs. For higher rate limits, paid tiers, or multi-tenant onboarding: [email protected].

Need a key? Get one self-service — it’s shown once and stored only as a hash. Browse the other guides, grab copy-paste artifacts on the integrations page, or read the full machine-readable schema at /api/spec.
Asterisk / FreePBX integration3CX CRM integration