Numbers Online Inbound API

Inbound caller-intelligence lookup for operators, PBX, and softphones

Operations 1

POST /api/v1/inbound/lookup Inbound caller lookup #

Documentation

Specifications

Other Resources

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/numbers-online:numbers-online-inbound-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

numbers-online-inbound-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Numbers Online Phone Intelligence Inbound 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: Inbound
  description: Inbound caller-intelligence lookup for operators, PBX, and softphones
paths:
  /api/v1/inbound/lookup:
    post:
      tags:
      - Inbound
      summary: Inbound caller lookup
      description: 'Signed, privacy-safe caller intelligence as a supplementary signal for an inbound call/message event. Returns identity type, a risk score (0-100, higher = worse), evidence signals, and a recommended action. A verified individual returns only "Verified & online" — never a name. Requires an API key with the inbound_lookup use case. Billed per dip on the standard tier ($0.004); free on the free tier (rate-limited, and a high-risk caller is degraded from block_candidate to challenge_or_route). Anti-enumeration: a known and an unknown number return the same 200 shape (found vs no_record). Caller-ID-authentication inputs `verstat` and `attestation` are read at the top level: a supplied value modulates whether this number''s standing applies to THIS call (a verified call trusts our read; a failed validation may indicate spoofing of the number) and, when an auth signal is present, contributes to a corroboration-gated spoofing-prevalence signal accumulated across sources — a supplementary signal, never stamped onto the number''s stored standing; absence is not a failed validation. The optional `to` (the receiver''s own number) binds call-provenance. The remaining fields (destination_number, client_type, client_name, the nested stir_shaken object, call_id_hash, timestamp) are accepted but currently reserved and ignored.'
      operationId: inboundLookup
      security:
      - ApiKeyAuth: []
      - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - number
              properties:
                number:
                  type: string
                  example: '+14155551212'
                context:
                  type: string
                  enum:
                  - inbound_voice
                  - inbound_sms
                  - inbound_waba
                verstat:
                  type: string
                  description: STIR/SHAKEN verstat passthrough — a bare token (e.g. TN-Validation-Passed), a verstat=… parameter, or a full SIP/tel header value; normalized to verified/unverified/unknown. Absence is not a failed validation.
                  example: TN-Validation-Passed
                attestation:
                  type: string
                  enum:
                  - A
                  - B
                  - C
                  description: P-Attestation-Indicator level; an explicit A/B/C overrides verstat.
                to:
                  type: string
                  description: The receiver's own number (E.164). Binds call-provenance so a later spoofing report can be attributed; stored only as a hash.
                  example: '+14155550100'
                destination_number:
                  type: string
                  description: Reserved; accepted but currently ignored.
                client_type:
                  type: string
                  description: Reserved; accepted but currently ignored.
                client_name:
                  type: string
                  description: Reserved; accepted but currently ignored.
                call_id_hash:
                  type: string
                  description: Reserved; accepted but currently ignored.
                timestamp:
                  type: string
                  format: date-time
                  description: Reserved; accepted but currently ignored.
      responses:
        '200':
          description: Signed caller assessment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InboundLookupResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          $ref: '#/components/responses/InsufficientBalance'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  responses:
    InsufficientBalance:
      description: Standard-tier prepaid balance is exhausted. Top up (POST /api/v1/account/topup) to resume.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InsufficientBalance'
    RateLimited:
      description: Per-key rate limit exceeded. Retry after the number of seconds in the Retry-After header.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    InboundLookupResponse:
      type: object
      properties:
        schema_version:
          type: string
          example: '2026-05-31'
        result:
          type: string
          enum:
          - found
          - no_record
        number:
          type: string
        identity_type:
          type: string
          enum:
          - verified_business
          - verified_individual
          - unverified
          - unknown
        display_label:
          type: string
          description: Business name, or "Verified & online" for individuals — never a personal name.
        profile_url:
          type:
          - string
          - 'null'
          description: Business profile URL only; null for individuals and unknown.
        personal_details_exposed:
          type: boolean
          example: false
        risk_score:
          type:
          - integer
          - 'null'
          description: 0-100, higher = worse; null when identity_type is unknown. FROZEN field name — `risk` is the disclosed read.
        risk_level:
          type: string
          enum:
          - low
          - medium
          - high
          - unknown
        risk:
          $ref: '#/components/schemas/RiskView'
        signals:
          type: array
          items:
            type: string
          description: PII-free evidence labels.
        recommended_action:
          type: string
          enum:
          - allow
          - label
          - challenge_or_route
          - block_candidate
          - allow_with_default_policy
        ttl_seconds:
          type: integer
        receipt_id:
          type: string
          example: nol_rec_…
        response_signature:
          type: string
          description: '''ed25519:<base64>'', or ''unsigned'' when no signing key is configured.'
    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
          description: Legacy field emitted ONLY by the pre-v1 parse family (/api/parse, /api/parse/bulk, /api/countries). /v1 routes return only `error` — do not depend on `success` there.
        error:
          type: string
          description: Human-readable error message (prose — switch on `code`, not on this string).
        code:
          type: string
          enum:
          - missing_key
          - invalid_key
          - use_case_forbidden
          - rate_limited_key
          - rate_limited_pool
          - rate_limited_ip
          - signature_invalid
          - insufficient_balance
          - account_suspended
          - paid_verification_required
          - receipt_invalid
          description: 'Stable machine-readable error code (added 2026-06-12, additive — older errors may omit it). See the "Error codes" section in the API description for the full table. A valid key on the wrong use case returns 403 use_case_forbidden (not 401): re-authing will not fix a permissions problem.'
        retry_after_seconds:
          type: integer
          description: 'Present on 429s: seconds until the window resets (mirrors the Retry-After header).'
      required:
      - error
    InsufficientBalance:
      type: object
      description: 402 body returned when a standard-tier account has no remaining credit for a billed request. A SUSPENDED account instead returns 403 with code `account_suspended` and no top-up pointer (payment does not lift a suspension).
      properties:
        error:
          type: string
          example: Insufficient prepaid balance for this request.
        code:
          type: string
          enum:
          - insufficient_balance
          description: Stable machine-readable code (added 2026-06-12).
        balance_micros:
          type:
          - integer
          - 'null'
          description: Remaining balance in microdollars (may be 0 or null).
          example: 0
        topup:
          type: string
          description: How to add credit (prose; prefer the structured siblings).
          example: 'POST /api/v1/account/topup with {"amount_cents": 500} (minimum $5) to add credit.'
        topup_url:
          type: string
          example: /api/v1/account/topup
          description: Top-up endpoint path.
        topup_min_cents:
          type: integer
          example: 500
          description: Minimum top-up amount in cents.
    RiskView:
      type: object
      description: 'One risk vocabulary (added 2026-06-11, additive — no schema_version bumps): the same numeric signal as the surface''s legacy field, plus a band and the MODEL label that says which pipeline scored it. The legacy fields (`spam_score`, `risk_score`/`risk_level`) are frozen forever; this object is the disclosed, consistent read. A null score yields the uniform unknown shape ({score:null, level:"unknown", model:null}) on every branch (anti-enumeration; fail-open nulls are load-bearing). The numeric scales are deliberately NOT unified across models — `model` is what tells them apart. See "Risk models" in the spec intro.'
      properties:
        score:
          type:
          - integer
          - 'null'
          description: The risk score on the MODEL's own scale (1–99 for first_party_plus_restricted_sources; 0–100 for the others); null when no signal.
        level:
          type: string
          enum:
          - low
          - medium
          - high
          - unknown
          description: 'Shared banding: <40 low, <70 medium, ≥70 high; unknown when score is null. NOTE: the SBC default flag threshold is a separate policy knob (80) — level high does not automatically flag.'
        model:
          type:
          - string
          - 'null'
          enum:
          - first_party_plus_restricted_sources
          - first_party_plus_external
          - dial_structural
          - null
          description: Which scoring pipeline produced the score; null when score is null.
  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.