openapi: 3.1.0
info:
  title: ProbeDeck — Domain intelligence API
  version: 1.0.0
  description: |
    One endpoint, every signal — WHOIS, DNS, SSL, HTTP timing, security
    headers, tech detection, OG/meta, favicon — all under one consistent
    JSON envelope.

    Distributed via RapidAPI Hub. Subscribe to get an API key:
    https://rapidapi.com/

    All endpoints accept either ?url= (a full URL) or ?domain= (a bare
    hostname). Responses share a uniform envelope:

    ```json
    {
      "success": true,
      "input":   { "url": "https://example.com", "domain": "example.com" },
      "data":    { ... endpoint-specific ... },
      "meta":    { "cached": false, "tookMs": 184, "version": "1.0", "requestId": "..." }
    }
    ```

    On failure the same envelope is returned with `success:false`,
    `data:null`, and an `error: { code, message, hint }`.

    Errors NEVER return HTTP 5xx — input errors come back with 200/4xx
    and the envelope reports the problem. Rate limits return 429.
  contact:
    name: ProbeDeck Support
    email: eric@evolutionitservice.com
    url: https://probedeck.dev
  license:
    name: MIT
servers:
  - url: https://api.probedeck.dev
    description: Production
  - url: http://localhost:8080
    description: Local dev
tags:
  - name: inspect
    description: Run every probe in one call
  - name: domain
    description: WHOIS / DNS / SSL / favicon — domain-level lookups
  - name: url
    description: HTTP / headers / tech / meta — URL-level lookups
  - name: system
    description: Health / metrics
paths:
  /v1/inspect:
    get:
      tags: [inspect]
      summary: Full intelligence report — runs all probes in parallel
      parameters:
        - $ref: '#/components/parameters/UrlParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/InspectResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/whois:
    get:
      tags: [domain]
      summary: WHOIS / RDAP lookup
      parameters:
        - $ref: '#/components/parameters/DomainParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/dns:
    get:
      tags: [domain]
      summary: DNS records (A, AAAA, MX, TXT, NS, CNAME, SOA, CAA)
      parameters:
        - $ref: '#/components/parameters/DomainParam'
        - in: query
          name: type
          description: Comma-separated list of DNS record types. Default A,AAAA,MX,TXT,NS.
          schema: { type: string, example: 'A,MX,TXT' }
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/ssl:
    get:
      tags: [domain]
      summary: TLS certificate subject/issuer/SANs/daysLeft/chainValid
      parameters:
        - $ref: '#/components/parameters/DomainParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/headers:
    get:
      tags: [url]
      summary: HTTP status + headers + security scorecard
      parameters:
        - $ref: '#/components/parameters/UrlParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/tech:
    get:
      tags: [url]
      summary: Tech stack detection from headers + HTML signatures
      parameters:
        - $ref: '#/components/parameters/UrlParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/meta:
    get:
      tags: [url]
      summary: Page title, description, OG, Twitter, canonical, lang
      parameters:
        - $ref: '#/components/parameters/UrlParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/health:
    get:
      tags: [url]
      summary: Quick HTTP probe — status, latency, redirect chain
      parameters:
        - $ref: '#/components/parameters/UrlParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /v1/favicon:
    get:
      tags: [domain]
      summary: Best favicon URL (apple-touch / link rel=icon / manifest)
      parameters:
        - $ref: '#/components/parameters/UrlParam'
        - $ref: '#/components/parameters/NoCacheParam'
      responses:
        '200': { $ref: '#/components/responses/EnvelopeResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /health:
    get:
      tags: [system]
      summary: Liveness check
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  redis: { type: boolean }
                  version: { type: string }
                  uptime: { type: integer }
  /metrics:
    get:
      tags: [system]
      summary: Prometheus metrics
      responses:
        '200':
          description: Prometheus text exposition format
          content:
            text/plain: {}
components:
  securitySchemes:
    rapidApi:
      type: apiKey
      in: header
      name: X-RapidAPI-Proxy-Secret
      description: Shared secret set on the RapidAPI listing. RapidAPI injects this on every proxied call.
  parameters:
    UrlParam:
      in: query
      name: url
      required: true
      description: Full URL to probe, with scheme (https://...).
      schema: { type: string, format: uri, example: 'https://example.com' }
    DomainParam:
      in: query
      name: domain
      required: true
      description: Bare hostname.
      schema: { type: string, example: 'example.com' }
    NoCacheParam:
      in: query
      name: nocache
      required: false
      description: Set to 1 to bypass the read-through cache (billed the same).
      schema: { type: string, enum: ['0', '1'] }
  responses:
    EnvelopeResponse:
      description: Standard envelope
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Envelope' }
    InspectResponse:
      description: Merged report from all probes
      content:
        application/json:
          schema: { $ref: '#/components/schemas/InspectEnvelope' }
    Unauthorized:
      description: Missing or invalid X-RapidAPI-Proxy-Secret
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorEnvelope' }
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorEnvelope' }
  schemas:
    EnvelopeMeta:
      type: object
      properties:
        cached: { type: boolean }
        tookMs: { type: integer }
        version: { type: string }
        requestId: { type: string }
    EnvelopeInput:
      type: object
      properties:
        url: { type: string }
        domain: { type: string }
    Envelope:
      type: object
      required: [success, input, meta]
      properties:
        success: { type: boolean }
        input: { $ref: '#/components/schemas/EnvelopeInput' }
        data: { type: object, additionalProperties: true }
        meta: { $ref: '#/components/schemas/EnvelopeMeta' }
    ErrorEnvelope:
      type: object
      required: [success, input, meta, error]
      properties:
        success: { type: boolean, enum: [false] }
        input: { $ref: '#/components/schemas/EnvelopeInput' }
        data: { type: 'null' }
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string, example: 'invalid_input' }
            message: { type: string }
            hint: { type: string }
        meta: { $ref: '#/components/schemas/EnvelopeMeta' }
    InspectEnvelope:
      allOf:
        - $ref: '#/components/schemas/Envelope'
        - type: object
          properties:
            data:
              type: object
              properties:
                whois:   { type: object }
                dns:     { type: object }
                ssl:     { type: object }
                http:    { type: object }
                headers: { type: object }
                tech:    { type: object }
                meta:    { type: object }
                favicon: { type: object }
                summary:
                  type: object
                  properties:
                    okCount:     { type: integer }
                    errorCount:  { type: integer }
                    totalProbes: { type: integer }
security:
  - rapidApi: []
