openapi: 3.0.3
info:
  title: VeriRoute Intel API
  description: |
    Comprehensive API for phone number intelligence, including CNAM lookup, LRN routing, 
    messaging provider identification, and trust/spam detection services.
  version: 1.0.0
  contact:
    name: VeriRoute Intel Support
    url: https://verirouteintel.com
    email: support@verirouteintel.com
  license:
    name: Proprietary
    url: https://verirouteintel.com/terms

servers:
  - url: https://verirouteintel.com
    description: Production API Server

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: API key authentication using Bearer token

  headers:
    X-RateLimit-Limit:
      description: Maximum number of requests allowed in the current window.
      schema: { type: integer }
    X-RateLimit-Remaining:
      description: Remaining requests in the current window.
      schema: { type: integer }
    X-RateLimit-Reset:
      description: UNIX timestamp when the current window resets.
      schema: { type: integer, format: int64 }
    Retry-After:
      description: Seconds to wait before retrying.
      schema: { type: integer }

  responses:
    TooManyRequests429:
      description: Too Many Requests — rate limit exceeded.
      headers:
        X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
        X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
        X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
        Retry-After: { $ref: '#/components/headers/Retry-After' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/RateLimitError' }

  schemas:
    RateLimitError:
      type: object
      required: [error, code]
      properties:
        error: { type: string, example: 'Too Many Requests' }
        code: { type: string, example: 'rate_limited' }
        message: { type: string, example: 'You have exceeded the allowed request rate. Please retry after the specified interval.' }
        bucket: { type: string, example: 'per_api_key' }
        window_seconds: { type: integer, example: 60 }
        retry_after: { type: integer, example: 5 }

    CNAMResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            phone_number: { type: string, example: '15555550123' }
            caller_name: { type: string, example: 'JOHN DOE' }
            spam_risk: { type: boolean, example: false }
        success: { type: boolean, example: true }

    LRNResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            phone_number: { type: string, example: '15555550123' }
            lrn: { type: string, example: '15555550123' }
            carrier: { type: string, example: 'Verizon Wireless' }
        success: { type: boolean, example: true }

    MessagingResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            phone_number: { type: string, example: '15555550123' }
            provider: { type: string, example: 'Verizon' }
            enabled: { type: boolean, example: true }
            country: { type: string, example: 'US' }
        success: { type: boolean, example: true }

    SpamResponse:
      type: object
      description: Spam lookup response
      properties:
        phone_number: { type: string, example: '15555550123' }
        is_spam: { type: boolean, example: true }
        is_robocall: { type: boolean, example: false }
        is_scam: { type: boolean, example: false }
        spam_type: { type: string, enum: ['NONE', 'SPAM', 'ROBOCALL', 'SCAM'], example: 'SPAM' }
        cached: { type: boolean, example: false }
        source: { type: string, example: 'enhanced' }

    BatchSpamResponse:
      type: object
      description: Batch spam lookup response with deduplication and billing details
      properties:
        job_id: { type: string, format: uuid, example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' }
        results:
          type: array
          items:
            type: object
            properties:
              phone_number: { type: string, example: '15555550123' }
              is_spam: { type: boolean, example: true }
              is_robocall: { type: boolean, example: false }
              is_scam: { type: boolean, example: false }
              spam_type: { type: string, enum: ['NONE', 'SPAM', 'ROBOCALL', 'SCAM'] }
              source: { type: string, example: 'MCL_PROVIDER' }
              cached: { type: boolean, example: false }
              input_indices: { type: array, items: { type: integer }, example: [0, 2] }
              status: { type: string, enum: ['success', 'failed', 'invalid'], example: 'success' }
        summary:
          type: object
          properties:
            submitted: { type: integer, example: 3 }
            unique: { type: integer, example: 2 }
            duplicates_removed: { type: integer, example: 1 }
            succeeded: { type: integer, example: 2 }
            failed: { type: integer, example: 0 }
            from_cache: { type: integer, example: 0 }
            invalid_count: { type: integer, example: 0 }
        billing:
          type: object
          properties:
            estimated_cost: { type: number, example: 0.02 }
            actual_cost: { type: number, example: 0.02 }
            savings_from_deduplication: { type: number, example: 0.01 }
            per_lookup_rate: { type: number, example: 0.01 }
        timing:
          type: object
          properties:
            submitted_at: { type: string, format: date-time }
            started_at: { type: string, format: date-time }
            completed_at: { type: string, format: date-time }
            duration_ms: { type: integer, example: 1234 }

    TrustResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            number: { type: string, example: '15555550123' }
            is_spam: { type: boolean, example: false }
            is_robocall: { type: boolean, example: false }
            is_scam: { type: boolean, example: false }
            spam_type: { type: string, enum: ['NONE', 'SPAM', 'ROBOCALL', 'SCAM', 'TELEMARKETER'], example: 'NONE' }
            complaint_count: { type: integer, example: 0 }
            subjects: { type: array, items: { type: string }, example: [] }
            first_reported: { type: string, format: date-time, nullable: true }
            last_reported: { type: string, format: date-time, nullable: true }
            details: { type: string, example: '' }
        errors: { type: array, items: { type: string }, example: [] }

    TrustResponseV2:
      type: object
      description: Enhanced Trust response with reputation scoring (v2)
      properties:
        data:
          type: object
          properties:
            number: { type: string, example: '15555550123' }
            is_spam: { type: boolean, example: false }
            is_robocall: { type: boolean, example: false }
            is_scam: { type: boolean, example: false }
            spam_type: { type: string, enum: ['NONE', 'SPAM', 'ROBOCALL', 'SCAM', 'TELEMARKETER'], example: 'NONE' }
            complaint_count: { type: integer, example: 0 }
            subjects: { type: array, items: { type: string }, example: [] }
            reputation_score: { type: integer, minimum: 0, maximum: 100, example: 85, description: 'Reputation score from 0-100 (higher = more trustworthy)' }
            trust_level: { type: string, enum: ['high', 'medium', 'low'], example: 'high', description: 'Categorical trust level based on reputation score' }
            last_updated: { type: string, format: date-time, example: '2026-01-18T12:30:00Z', description: 'ISO 8601 timestamp of when data was last updated' }
        errors: { type: array, items: { type: string }, example: [] }

    CnamData:
      type: object
      description: Caller name information
      properties:
        caller_name: { type: string, example: 'ACME CORP', description: 'Caller ID name' }

    TrustData:
      type: object
      description: Trust and reputation information
      properties:
        is_spam: { type: boolean, example: false }
        is_robocall: { type: boolean, example: false }
        is_scam: { type: boolean, example: false }
        spam_type: { type: string, enum: ['NONE', 'SPAM', 'ROBOCALL', 'SCAM'], example: 'NONE' }
        reputation_score: { type: integer, minimum: 0, maximum: 100, example: 85 }
        trust_level: { type: string, enum: ['high', 'medium', 'low'], example: 'high' }
        last_updated: { type: string, format: date-time, example: '2026-01-18T12:30:00Z' }

    LRNResponseWithIncludes:
      type: object
      description: LRN response with optional CNAM and Trust data
      properties:
        phone_number: { type: string, example: '15555550123' }
        lrn: { type: string, example: '15555550123' }
        lrn_activated_at: { type: string, format: date-time, nullable: true, example: '2021-09-01T00:00:00+00:00' }
        carrier: { type: string, example: 'Verizon Wireless' }
        line_type: { type: string, enum: ['mobile', 'landline', 'voip', 'unknown'], example: 'mobile' }
        enhanced_lrn:
          type: object
          nullable: true
          properties:
            carrier: { type: string }
            carrier_type: { type: string }
            city: { type: string }
            state: { type: string }
            zip_code: { type: string }
            timezone: { type: string }
        messaging:
          type: object
          nullable: true
          properties:
            messaging_provider: { type: string }
            messaging_enabled: { type: boolean }
            messaging_country: { type: string }
        cnam:
          $ref: '#/components/schemas/CnamData'
        trust:
          $ref: '#/components/schemas/TrustData'

paths:
  /api/v1/health:
    get:
      tags: [Utility]
      summary: Health Check
      description: Check API service health status. No authentication required.
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, example: 'healthy' }
                  service: { type: string, example: 'Number Intelligence API' }
                  version: { type: string, example: '1.0' }
                  timestamp: { type: string, format: date-time, example: '2026-01-18T12:30:00Z' }

  /api/v1/cnam:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 500
        per_minute: 20
    get:
      tags: [CNAM]
      summary: CNAM Lookup (GET)
      description: Retrieve caller name information for a phone number via GET request
      operationId: cnamLookupGet
      security:
        - BearerAuth: []
      parameters:
        - name: phone_number
          in: query
          required: true
          schema:
            type: string
            example: '15555550123'
          description: Phone number in E.164 or national format
        - name: include_spam
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: Include spam detection data in the response
      responses:
        '200':
          description: Successful CNAM lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CNAMResponse' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }
    post:
      tags: [CNAM]
      summary: CNAM Lookup (POST)
      description: Retrieve caller name information for a phone number via POST request
      operationId: cnamLookupPost
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
                include_spam: { type: boolean, default: false, description: 'Include spam detection data in response' }
                include_spam_check: { type: boolean, default: false, description: 'Alias for include_spam' }
      responses:
        '200':
          description: Successful CNAM lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CNAMResponse' }
        '402':
          description: Insufficient balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: object }
                  errors: { type: array, items: { type: string }, example: ['Insufficient balance. Required: $0.01, Available: $0.00'] }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/cnam/bulk:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 20
    post:
      tags: [CNAM]
      summary: Bulk CNAM Lookup
      description: |
        Process up to 1,000 CNAM lookups in a single request.

        **Features:**
        - Automatic deduplication (duplicates charged once)
        - Two-phase billing (reserve → process → settle)
        - Job tracking with unique ID
        - Full transparency in response
      operationId: cnamBulkLookup
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_numbers]
              properties:
                phone_numbers:
                  type: array
                  items: { type: string }
                  maxItems: 1000
                  example: ['15555550123', '15555550124']
                include_spam: { type: boolean, default: false, description: 'Include spam detection for each number' }
      responses:
        '200':
          description: Successful bulk CNAM lookup
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id: { type: integer, example: 12345 }
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        phone_number: { type: string, example: '15555550123' }
                        cnam: { type: string, example: 'JOHN DOE' }
                        input_indices: { type: array, items: { type: integer }, example: [0, 2] }
                        status: { type: string, enum: ['success', 'failed', 'invalid'], example: 'success' }
                  summary:
                    type: object
                    properties:
                      submitted: { type: integer, example: 10 }
                      unique: { type: integer, example: 8 }
                      duplicates_removed: { type: integer, example: 2 }
                      successful: { type: integer, example: 8 }
                      failed: { type: integer, example: 0 }
        '402':
          description: Insufficient balance
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/lrn:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 500
        per_minute: 20
    get:
      tags: [LRN]
      summary: LRN Lookup (GET)
      description: Retrieve Local Routing Number information for a phone number
      operationId: lrnLookupGet
      security:
        - BearerAuth: []
      parameters:
        - name: phone_number
          in: query
          required: true
          schema:
            type: string
            example: '15555550123'
          description: Phone number in E.164 or national format
        - name: lrn_only
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: Return only LRN data (disables all other includes for minimal, fast response)
        - name: include_enhanced_lrn
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: Include enhanced carrier and location data (city, state, timezone)
        - name: messaging_lookup
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: Include messaging provider information
        - name: include_cnam
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: Include CNAM (caller name) data in response
        - name: include_trust
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: Include trust/reputation data with reputation_score and trust_level
      responses:
        '200':
          description: Successful LRN lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LRNResponseWithIncludes' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }
    post:
      tags: [LRN]
      summary: LRN Lookup (POST)
      description: |
        Retrieve Local Routing Number information for a phone number.

        Optional parameters allow including CNAM and Trust data in a single request:
        - `include_cnam`: Add caller name lookup
        - `include_trust`: Add reputation scoring with trust_level (high/medium/low)
        - `lrn_only`: Minimal response with only LRN data (overrides other includes)

        Each included lookup is billed separately.
      operationId: lrnLookupPost
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
                lrn_only: { type: boolean, default: false, description: 'Return only LRN data (overrides all other includes)' }
                include_cnam: { type: boolean, default: false, description: 'Include CNAM caller name data' }
                include_trust: { type: boolean, default: false, description: 'Include trust/reputation data with reputation_score (0-100) and trust_level (high/medium/low)' }
                include_enhanced_lrn: { type: boolean, default: false, description: 'Include enhanced carrier and location data' }
                messaging_lookup: { type: boolean, default: false, description: 'Include messaging provider information' }
      responses:
        '200':
          description: Successful LRN lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LRNResponseWithIncludes' }
        '402':
          description: Insufficient balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, example: 'Insufficient balance' }
                  code: { type: string, example: 'INSUFFICIENT_BALANCE' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/messaging:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      burst_multiplier: 2
      limits:
        GET_rps: 75
        POST_rps: 40
    get:
      tags: [Message Provider Lookup]
      summary: Messaging Provider Lookup (GET)
      description: Retrieve messaging provider information for a phone number
      security:
        - BearerAuth: []
      parameters:
        - name: phone_number
          in: query
          required: true
          schema:
            type: string
            example: '15555550123'
      responses:
        '200':
          description: Successful messaging provider lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MessagingResponse' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }
    post:
      tags: [Message Provider Lookup]
      summary: Messaging Provider Lookup (POST)
      description: Retrieve messaging provider information for a phone number
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
      responses:
        '200':
          description: Successful messaging provider lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MessagingResponse' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/trust:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      burst_multiplier: 2
      limits:
        GET_rps: 50
        POST_rps: 25
    get:
      tags: [Trust]
      summary: Trust/Spam Detection (GET)
      description: Retrieve trust, spam, and fraud risk information for a phone number
      security:
        - BearerAuth: []
      parameters:
        - name: phone_number
          in: query
          required: true
          schema:
            type: string
            example: '15555550123'
      responses:
        '200':
          description: Successful trust analysis
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustResponse' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }
    post:
      tags: [Trust]
      summary: Trust/Spam Detection (POST)
      description: Retrieve trust, spam, and fraud risk information for a phone number
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
      responses:
        '200':
          description: Successful trust analysis
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustResponse' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v2/trust:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      burst_multiplier: 2
      limits:
        POST_rps: 25
    post:
      tags: [Trust]
      summary: Trust/Spam Detection v2 (with Reputation Scoring)
      description: |
        Enhanced trust endpoint that includes quantitative reputation scoring.

        **New in v2:**
        - `reputation_score`: 0-100 integer (higher = more trustworthy)
        - `trust_level`: Categorical level (high >= 70, medium 40-69, low < 40)
        - `last_updated`: ISO 8601 timestamp

        **Reputation Score Ranges:**
        - Confirmed scam: 10
        - Confirmed robocall: 20
        - General spam: 35
        - Clean number: 50-85
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
      responses:
        '200':
          description: Successful trust analysis with reputation scoring
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TrustResponseV2' }
              examples:
                clean_number:
                  summary: Clean number with high trust
                  value:
                    data:
                      number: '15555550123'
                      is_spam: false
                      is_robocall: false
                      is_scam: false
                      spam_type: 'NONE'
                      complaint_count: 0
                      subjects: []
                      reputation_score: 85
                      trust_level: 'high'
                      last_updated: '2026-01-18T12:30:00Z'
                    errors: []
                spam_number:
                  summary: Spam number with low trust
                  value:
                    data:
                      number: '15555550123'
                      is_spam: true
                      is_robocall: true
                      is_scam: false
                      spam_type: 'ROBOCALL'
                      complaint_count: 15
                      subjects: ['Warranties', 'Auto']
                      reputation_score: 20
                      trust_level: 'low'
                      last_updated: '2026-01-18T12:30:00Z'
                    errors: []
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/lrn/bulk:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      burst_multiplier: 2
      limits:
        POST_rps: 10
    post:
      tags: [LRN]
      summary: Bulk LRN Lookup
      description: |
        Process up to 1,000 LRN lookups in a single request.

        **Features:**
        - Automatic deduplication (duplicates charged once)
        - Two-phase billing (reserve → process → settle)
        - Optional CNAM and Trust enrichment per number
        - Each included product billed separately

        **Billing:**
        - LRN lookup: base price per number
        - `include_cnam`: adds CNAM price per number
        - `include_trust`: adds Spam price per number
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_numbers]
              properties:
                phone_numbers:
                  type: array
                  items: { type: string }
                  maxItems: 1000
                  example: ['15555550123', '15555550124']
                include_cnam: { type: boolean, default: false, description: 'Include CNAM data for each number' }
                include_trust: { type: boolean, default: false, description: 'Include trust/reputation data for each number' }
                include_enhanced_lrn: { type: boolean, default: false, description: 'Include enhanced carrier data' }
                messaging_lookup: { type: boolean, default: false, description: 'Include messaging provider data' }
      responses:
        '200':
          description: Successful bulk LRN lookup
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id: { type: integer, example: 12345 }
                  results:
                    type: array
                    items: { $ref: '#/components/schemas/LRNResponseWithIncludes' }
                  summary:
                    type: object
                    properties:
                      submitted: { type: integer, example: 10 }
                      unique: { type: integer, example: 8 }
                      duplicates_removed: { type: integer, example: 2 }
                      successful: { type: integer, example: 8 }
                      failed: { type: integer, example: 0 }
        '402':
          description: Insufficient balance
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /graphql:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      limits:
        per_minute: 60
    x-graphql-cost:
      window_seconds: 60
      basis: per_api_key
      max_cost: 1000
      depth_limit: 12
      advice: 'Use persisted queries and avoid unbounded lists. Cost is based on field fanout and depth.'
    get:
      tags: [GraphQL]
      summary: GraphQL Playground
      description: GraphQL playground/introspection endpoint for schema exploration
      operationId: graphqlPlayground
      security:
        - BearerAuth: []
      responses:
        '200':
          description: GraphQL playground HTML
          content:
            text/html:
              schema:
                type: string
    post:
      tags: [GraphQL]
      summary: GraphQL API
      description: GraphQL endpoint for flexible querying of phone number intelligence data
      operationId: graphqlQuery
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query: { type: string, example: 'query { phoneNumberLookup(phoneNumber: "15555550123") { callerName spam } }' }
                variables: { type: object }
                operationName: { type: string }
      responses:
        '200':
          description: Successful GraphQL query
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: object }
                  errors: { type: array, items: { type: object } }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  # ===== SPAM ENDPOINTS =====
  /api/v1/spam:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 200
        per_minute: 20
    get:
      tags: [Spam]
      summary: Spam Lookup (GET)
      description: Check if a phone number is reported as spam, robocall, or scam
      operationId: spamLookupGet
      security:
        - BearerAuth: []
      parameters:
        - name: phone_number
          in: query
          required: true
          schema:
            type: string
            example: '15555550123'
          description: Phone number in E.164 or national format
        - name: check_spam
          in: query
          required: false
          schema:
            type: boolean
            default: true
          description: Whether to query external spam provider (default true)
      responses:
        '200':
          description: Successful spam lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SpamResponse' }
        '402':
          description: Insufficient balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, example: 'Insufficient balance' }
                  code: { type: string, example: 'INSUFFICIENT_BALANCE' }
        '429': { $ref: '#/components/responses/TooManyRequests429' }
    post:
      tags: [Spam]
      summary: Spam Lookup (POST)
      description: Check if a phone number is reported as spam, robocall, or scam
      operationId: spamLookupPost
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
                check_spam: { type: boolean, default: true, description: 'Whether to query external spam provider' }
      responses:
        '200':
          description: Successful spam lookup
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/X-RateLimit-Reset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SpamResponse' }
        '402':
          description: Insufficient balance
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/spam/batch:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 50
        per_minute: 5
    post:
      tags: [Spam]
      summary: Batch Spam Lookup
      description: |
        Process up to 100 phone numbers for spam detection in a single request.

        **Features:**
        - Automatic deduplication (duplicates charged once)
        - Two-phase billing (reserve → process → settle)
        - Job tracking with unique ID
        - Full transparency in response with timing and billing breakdown
      operationId: spamBatchLookup
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_numbers]
              properties:
                phone_numbers:
                  type: array
                  items: { type: string }
                  maxItems: 100
                  example: ['15555550123', '19494600638']
                check_spam: { type: boolean, default: true, description: 'Whether to query external spam provider' }
      responses:
        '200':
          description: Successful batch spam lookup
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BatchSpamResponse' }
        '402':
          description: Insufficient balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, example: 'Insufficient balance' }
                  code: { type: string, example: 'INSUFFICIENT_BALANCE' }
                  job_id: { type: integer }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/spam/report:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 50
        per_minute: 5
    post:
      tags: [Spam]
      summary: Report Spam Number (Authenticated)
      description: |
        Report a phone number as spam to the master spam database.
        Requires API key authentication. Prevents duplicate submissions within 24 hours from the same API key.
      operationId: spamReport
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
                report_type:
                  type: string
                  enum: ['spam', 'robocall', 'scam', 'telemarketing', 'fraud', 'phishing']
                  default: 'spam'
                  description: Type of spam report
                details: { type: string, maxLength: 1000, example: 'Automated call about car warranty', description: 'Optional description of the incident' }
      responses:
        '200':
          description: Spam report submitted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  message: { type: string, example: 'Spam report submitted successfully' }
                  data:
                    type: object
                    properties:
                      phone_number: { type: string, example: '+15555550123' }
                      report_type: { type: string, example: 'spam' }
                      reported_at: { type: string, format: date-time }
                      complaint_count: { type: integer, example: 1 }
        '429':
          description: Duplicate report or rate limit exceeded
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, example: 'Duplicate report detected' }
                  code: { type: string, enum: ['DUPLICATE_REPORT', 'RATE_LIMIT_EXCEEDED'] }
                  data:
                    type: object
                    properties:
                      phone_number: { type: string }
                      last_reported: { type: string, format: date-time }
                      retry_after: { type: string, format: date-time }

  /api/v1/spam/report/public:
    x-rate-limit:
      basis: per_ip
      window_seconds: 3600
      limits:
        hourly: 10
        per_minute: 2
    post:
      tags: [Spam]
      summary: Report Spam Number (Public - No Auth)
      description: |
        Report a phone number as spam without requiring API key authentication.
        Rate limited by IP address instead of API key.

        **Use cases:**
        - Landing pages
        - Public forms
        - Mobile apps where exposing API keys is not desirable
      operationId: spamReportPublic
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
                report_type:
                  type: string
                  enum: ['spam', 'robocall', 'scam', 'telemarketing', 'fraud', 'phishing']
                  default: 'spam'
                details: { type: string, maxLength: 1000, example: 'Caller claimed to be from IRS' }
                source: { type: string, example: 'my-website', description: 'Identifier for where the report is coming from' }
      responses:
        '200':
          description: Spam report submitted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  message: { type: string, example: 'Thank you! Your spam report has been submitted.' }
                  data:
                    type: object
                    properties:
                      phone_number: { type: string, example: '+15555550123' }
                      report_type: { type: string, example: 'robocall' }
                      total_reports: { type: integer, example: 5 }
        '429':
          description: Rate limit exceeded or duplicate report
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string }
                  code: { type: string, enum: ['DUPLICATE_REPORT', 'RATE_LIMIT_EXCEEDED'] }

  /api/v1/spam/lookup/enhanced:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 200
        per_minute: 20
    post:
      tags: [Spam]
      summary: Enhanced Multi-Source Spam Lookup
      description: |
        Advanced spam detection that aggregates data from multiple sources including:
        - Master spam database
        - External provider feeds
        - Web sources
        - Crowdsourced reports

        Returns comprehensive spam intelligence with confidence scores.
      operationId: spamEnhancedLookup
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone_number]
              properties:
                phone_number: { type: string, example: '15555550123' }
                include_web_sources: { type: boolean, default: true, description: 'Include web-scraped data' }
                include_provider: { type: boolean, default: true, description: 'Include external provider data' }
      responses:
        '200':
          description: Successful enhanced spam lookup
          content:
            application/json:
              schema:
                type: object
                properties:
                  phone_number: { type: string, example: '15555550123' }
                  is_spam: { type: boolean, example: true }
                  is_robocall: { type: boolean, example: true }
                  is_scam: { type: boolean, example: false }
                  spam_type: { type: string, enum: ['NONE', 'SPAM', 'ROBOCALL', 'SCAM'], example: 'ROBOCALL' }
                  confidence: { type: number, minimum: 0, maximum: 1, example: 0.92 }
                  total_complaints: { type: integer, example: 15 }
                  sources: { type: array, items: { type: string }, example: ['database', 'provider', 'web'] }
                  categories: { type: array, items: { type: string }, example: ['Warranties', 'Auto'] }
        '402':
          description: Insufficient balance
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  # ===== REPORTS & ANALYTICS ENDPOINTS =====
  /api/v1/reports/usage:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      limits:
        per_minute: 60
    get:
      tags: [Reports]
      summary: Usage Statistics
      description: Get usage statistics for the current API key including lookup counts and costs
      operationId: getUsageStats
      security:
        - BearerAuth: []
      parameters:
        - name: start_date
          in: query
          schema:
            type: string
            format: date
          description: Start date for usage period (default 30 days ago)
        - name: end_date
          in: query
          schema:
            type: string
            format: date
          description: End date for usage period (default today)
      responses:
        '200':
          description: Usage statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  data:
                    type: object
                    properties:
                      total_lookups: { type: integer, example: 1500 }
                      total_cost: { type: number, example: 15.00 }
                      period_start: { type: string, format: date }
                      period_end: { type: string, format: date }
                      by_product:
                        type: object
                        additionalProperties:
                          type: object
                          properties:
                            count: { type: integer }
                            cost: { type: number }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/reports/usage/all:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      limits:
        per_minute: 30
    get:
      tags: [Reports]
      summary: All API Keys Usage Statistics
      description: Get aggregated usage statistics across all API keys for the account
      operationId: getAllUsageStats
      security:
        - BearerAuth: []
      parameters:
        - name: start_date
          in: query
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Aggregated usage statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  data:
                    type: object
                    properties:
                      total_lookups: { type: integer }
                      total_cost: { type: number }
                      by_api_key:
                        type: array
                        items:
                          type: object
                          properties:
                            key_name: { type: string }
                            lookups: { type: integer }
                            cost: { type: number }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/reports/export:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 3600
      limits:
        hourly: 10
    get:
      tags: [Reports]
      summary: Export Usage Data (CSV)
      description: Export usage data as a CSV file for the specified date range
      operationId: exportUsageData
      security:
        - BearerAuth: []
      parameters:
        - name: start_date
          in: query
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          schema:
            type: string
            format: date
        - name: format
          in: query
          schema:
            type: string
            enum: ['csv', 'json']
            default: 'csv'
      responses:
        '200':
          description: Export file
          content:
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                type: object
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  /api/v1/analytics:
    x-rate-limit:
      basis: per_api_key
      window_seconds: 60
      limits:
        per_minute: 30
    get:
      tags: [Reports]
      summary: Analytics Dashboard Data
      description: Get analytics data for dashboard visualization
      operationId: getAnalytics
      security:
        - BearerAuth: []
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: ['day', 'week', 'month', 'year']
            default: 'month'
      responses:
        '200':
          description: Analytics data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: object
                    properties:
                      daily_lookups: { type: array, items: { type: object } }
                      top_products: { type: array, items: { type: object } }
                      cost_trend: { type: array, items: { type: object } }
        '429': { $ref: '#/components/responses/TooManyRequests429' }

  # ===== UTILITY ENDPOINTS =====
  /api/v1/pricing/all:
    get:
      tags: [Utility]
      summary: Get All Product Pricing
      description: |
        Retrieve current pricing for all products.
        No authentication required for default pricing.
        Authenticated requests return account-specific pricing if applicable.
      operationId: getAllPricing
      security: []
      responses:
        '200':
          description: Product pricing information
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean, example: true }
                  pricing:
                    type: object
                    additionalProperties: { type: number }
                    example:
                      lrn: 0.005
                      cnam: 0.01
                      spam: 0.008
                      messaging_provider: 0.005
                      enhanced_lrn: 0.003

  /api/v1/auth/validate-key:
    x-rate-limit:
      basis: per_ip
      window_seconds: 60
      limits:
        per_minute: 60
    post:
      tags: [Utility]
      summary: Validate API Key
      description: |
        Validate an API key without performing any billable operations.
        Useful for Live API Console widgets and key verification.
      operationId: validateApiKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [api_key]
              properties:
                api_key: { type: string, example: 'vri_live_xxxxxxxxxxxx' }
      responses:
        '200':
          description: API key is valid
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, example: 'valid' }
                  message: { type: string, example: 'API key is valid' }
        '401':
          description: Invalid API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, example: 'Invalid API key' }