openapi: 3.2.0 info: title: Numbers Online Phone Intelligence PBX API description: Phone number parsing, validation, and inbound caller-intelligence as a supplementary signal. version: 1.0.0 contact: name: Phone Numbers Online url: https://numbers.online servers: - url: https://numbers.online description: Production server - url: http://localhost:3000 description: Development server tags: - name: PBX description: Plain-text caller-id lookup for header-less PBX integrations (FreeSWITCH mod_cidlookup and similar) paths: /api/v1/cid/{number}: get: tags: - PBX summary: Plain-text caller-id lookup description: 'Caller-name (CNAM) lookup that ALWAYS returns `text/plain` — every response, including auth, balance, and rate-limit failures, is plain text so a PBX can paste the body verbatim into a caller name without ever rendering a JSON error blob. Built for header-less integrations such as FreeSWITCH `mod_cidlookup`, which substitutes the raw inbound number into a URL and uses the response body as the caller name. The body is the resolved name (`ACME CORP`), the name prefixed with an operator-chosen risk tag when one is configured and the spam signal crosses the threshold (`Spam? ACME CORP`), or the literal sentinel `UNAVAILABLE` when there is no name, the number is unresolvable, the caller is tenant-suppressed, or auth/balance/rate-limit fails (the HTTP status code is still meaningful — `200` for an ordinary no-result, `401`/`402`/`429` for the failure cases). The number is resolved loosely: 10/11-digit national, `+`E.164, or `00`-prefixed international are all accepted (anonymous callers and alphanumeric SIP user-parts yield `UNAVAILABLE`). Billing is identical to /api/v1/lookup (`$0.004` on a fresh wholesale CNAM dip, `$0.002` otherwise; unresolvable and tenant-suppressed numbers are free). Requires an API key with the `lookup` use case. Fail-open: a slow or failing supplier yields `UNAVAILABLE`, never an error on the call path.' operationId: cidLookup security: - ApiKeyAuth: [] - BearerAuth: [] - CidQueryKeyAuth: [] parameters: - name: number in: path required: true description: The inbound caller number as it arrived from the trunk — a bare national number (`2025550123`), 11-digit (`12025550123`), full E.164 (`+12025550123`), or its URL-encoded form. PBX clients substitute this token (e.g. mod_cidlookup's `${caller_id_number}`) with no normalization. schema: type: string example: '12025550123' - name: key in: query required: false description: 'API key as a query param. Intended for header-less integrations (e.g. FreeSWITCH `mod_cidlookup`) that cannot send an `Authorization`/`X-API-Key` header. Also accepted by the webhook adapters (`/api/v1/integrations/retell/inbound`, `/api/v1/integrations/vapi/tool`, `/api/v1/sbc/redirect`) for the same reason. The key can land in proxy/gateway access logs, so use a dedicated, rotated key. Prefer header auth (`Authorization: Bearer` / `X-API-Key`) wherever the client can send headers.' schema: type: string example: nol_YOUR_API_KEY - name: country in: query required: false description: Default country (ISO 3166-1 alpha-2) used to resolve bare national numbers that arrive without a country code. Defaults to `US`. schema: type: string example: US - name: spam_tag in: query required: false description: Operator-opt-in risk prefix. When set, a caller whose supplementary spam signal is at or above `spam_threshold` has the body returned prefixed with this text (e.g. `Spam? ACME CORP`, or the tag alone when no name is available). When absent, the risk signal NEVER alters the body — risk wording is strictly the operator's choice. schema: type: string example: Spam? - name: spam_threshold in: query required: false description: Spam-signal threshold (1–99, default 80) at or above which the `spam_tag` prefix is applied. Has no effect unless `spam_tag` is also set. schema: type: integer minimum: 1 maximum: 99 default: 80 example: 80 - name: max_cache_age in: query required: false description: 'Operator TTL control: maximum acceptable CNAM cache age in seconds. A cached value older than this triggers a fresh wholesale dip; `0` forces a fresh dip (billed at the `$0.004` rate).' schema: type: integer minimum: 0 example: 300 responses: '200': description: Plain-text caller name, an operator-tagged name, or the `UNAVAILABLE` sentinel (no name, unresolvable number, or tenant-suppressed caller). Always `text/plain`. headers: Cache-Control: description: Always `no-store` — the response is per-request and billed. schema: type: string example: no-store content: text/plain: schema: type: string example: ACME CORP '401': description: 'Authentication failed (missing or invalid key). Body is still plain text: `UNAVAILABLE`.' content: text/plain: schema: type: string example: UNAVAILABLE '402': description: 'Standard-tier prepaid balance is exhausted. Body is still plain text: `UNAVAILABLE`. Top up (POST /api/v1/account/topup) to resume.' content: text/plain: schema: type: string example: UNAVAILABLE '429': description: Per-key rate limit exceeded. Body is plain text `UNAVAILABLE`; a `Retry-After` header (seconds) is set. headers: Retry-After: description: Seconds to wait before retrying. schema: type: integer content: text/plain: schema: type: string example: UNAVAILABLE /api/v1/cid: get: tags: - PBX summary: Plain-text caller-id (zero-segment fallback) description: Zero-segment fallback for a PBX whose URL template failed to substitute the caller number (so the request arrives at `/api/v1/cid` with no number). Always returns `text/plain` `UNAVAILABLE` with HTTP 200, so a broken template never renders a 404 page as the caller name. Takes no key and runs no lookup — purely the safe default for `mod_cidlookup` and similar header-less clients still being wired up. operationId: cidLookupFallback security: [] responses: '200': description: Always the plain-text sentinel `UNAVAILABLE`. headers: Cache-Control: description: Always `no-store`. schema: type: string example: no-store content: text/plain: schema: type: string example: UNAVAILABLE components: securitySchemes: ApiKeyAuth: type: apiKey in: header name: X-API-Key description: API key for authentication BearerAuth: type: http scheme: bearer description: Bearer token authentication CidQueryKeyAuth: type: apiKey in: query name: key description: API key in the `?key=` query param. Accepted by the header-less PBX endpoint GET /api/v1/cid/{number} and by the webhook adapters POST /api/v1/integrations/retell/inbound, POST /api/v1/integrations/vapi/tool, and POST /api/v1/sbc/redirect, whose upstream platforms set only a static webhook URL and cannot send an Authorization/X-API-Key header. The key can leak into access logs — use a dedicated, rotated key, and prefer header auth wherever the client supports it.