openapi: 3.2.0 info: title: Numbers Online Phone Intelligence Community reporting 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: Community reporting description: Key-gated spam/scam reporting — the give-to-get half of the community sensor (receipt-gated, tags-only, lane-scoped supplementary signals) paths: /api/v1/report: post: tags: - Community reporting summary: Report a number (community sensor) description: 'The give-to-get half of the community sensor. Report a number with one or more tags using a single-use `receipt_id` from a prior POST /api/v1/inbound/lookup on the same number. Tags only — no free-text body. The receipt is an anti-replay nonce and rate control, NOT proof a call happened (it is self-mintable). Accounts with a verified business number report in the ACCOUNTABLE lane (reporter-weighted, earns credibility); free/personal accounts report in the CROWD lane (a bounded, deferring, credibility-firewalled supplementary signal that cannot sink a verified number on its own). You cannot report a number bound to your own account. The daily report quota is pooled per account: 10/day on the free floor, +100/day per verified business number. Requires an account-level key with the `report` use case (tenant sub-keys cannot report). Returns `{ ok, lane, report_id, provenance }` (provenance is a call-provenance label — matched / mismatched / null — for reports about an enrolled caller); a stale, used, or mismatched receipt returns 409, and an over-quota request returns 429.' operationId: reportNumber security: - ApiKeyAuth: [] - BearerAuth: [] requestBody: required: true content: application/json: schema: type: object required: - e164 - tags - receipt_id properties: e164: type: string description: The number to report (E.164 recommended). example: '+14155552671' tags: type: array items: type: string minItems: 1 description: One or more report tags (e.g. `robocall`). Tags only — free text is not accepted. example: - robocall receipt_id: type: string description: A single-use, anti-replay receipt nonce from a prior POST /api/v1/inbound/lookup on this same number. Spent on success; it is not proof the call occurred. example: nol_rec_… responses: '200': description: Report accepted. content: application/json: schema: type: object properties: schema_version: type: string example: '2026-06-13' ok: type: boolean example: true lane: type: string enum: - accountable - crowd report_id: type: - string - 'null' format: uuid provenance: type: - string - 'null' enum: - matched - mismatched description: 'Call-provenance label for an enrolled caller: `matched` (a pre-call edge confirms the reported caller looked up this receiver before the call), `mismatched` (enrolled caller + known receiver but no edge → the complaint may concern a spoofed call), or `null` (no enrolled-caller signal / not applicable). A supplementary signal — not proof.' '400': description: Invalid request (missing/invalid `e164`, no valid tag, or missing `receipt_id`). content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Authentication required content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Tenant sub-keys cannot report, or the number is bound to your own account. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: The calling key has no associated account (legacy/internal key). content: application/json: schema: $ref: '#/components/schemas/Error' '409': description: Invalid, already-used, or mismatched `receipt_id`. Do a fresh POST /api/v1/inbound/lookup on this number first. content: application/json: schema: $ref: '#/components/schemas/Error' '429': $ref: '#/components/responses/RateLimited' components: schemas: 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 responses: 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' 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.