openapi: "3.1.0"
info:
  title: Certack API
  description: |
    Infrastructure monitoring platform — SSL/TLS, Uptime, DNS, Domain expiry, Latency.
    Monitor your infrastructure with comprehensive checks and alerting.
  version: "1.0.0"
  contact:
    name: Certack Support
    email: support@certack.com
    url: https://certack.com/docs/api
  license:
    name: Proprietary
    url: https://certack.com/terms

servers:
  - url: https://api.certack.com/v1
    description: Production

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: "Bearer token authentication. Use your API key (ct_...) or Supabase JWT. API keys available on all plans."
    RateLimitAuth:
      type: apiKey
      in: header
      name: Authorization
      description: "Rate limiting is enforced per API key. Monthly call limits: Free=100, Starter=2,000, Pro=10,000. QPS limits (per 10s window): Free=10, Starter=50, Pro=100."

  schemas:
    Site:
      type: object
      properties:
        id:
          type: string
          format: uuid
        domain:
          type: string
        check_types:
          type: array
          items:
            type: string
            enum: [ssl, dns, domain, ct]
        custom_port:
          type: integer
          nullable: true
        origin_ip:
          type: string
          nullable: true
        alert_days:
          type: integer
          default: 14
        monitoring_enabled:
          type: boolean
        created_at:
          type: string
          format: date-time

    SSLCheckResult:
      type: object
      properties:
        valid:
          type: boolean
        issuer:
          type: string
        expires_at:
          type: string
          format: date-time
        days_remaining:
          type: integer
        san:
          type: array
          items:
            type: string
        chain:
          type: array
          items:
            type: object
        chain_complete:
          type: boolean
        chain_error:
          type: string
          nullable: true
        error:
          type: string
          nullable: true

    DNSCheckResult:
      type: object
      properties:
        records:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
              values:
                type: array
                items:
                  type: string
        error:
          type: string
          nullable: true

    DomainCheckResult:
      type: object
      properties:
        registrar:
          type: string
          nullable: true
        expires_at:
          type: string
          format: date-time
          nullable: true
        days_remaining:
          type: integer
          nullable: true
        name_servers:
          type: array
          items:
            type: string
          nullable: true
        privacy_protected:
          type: boolean
          nullable: true
        error:
          type: string
          nullable: true

    CertHistoryResult:
      type: object
      properties:
        history:
          type: array
          items:
            type: object
            properties:
              fingerprint:
                type: string
              change_type:
                type: string
                enum: [renewal, replacement, issuer_change, san_change]
              changed_at:
                type: string
                format: date-time
              issuer:
                type: string
        total:
          type: integer

    UptimeCheckResult:
      type: object
      properties:
        status:
          type: string
          enum: [up, degraded, down]
        status_code:
          type: integer
        response_time_ms:
          type: number
        ttfb_ms:
          type: number
        keyword_match:
          type: boolean
          nullable: true
        error:
          type: string
          nullable: true

    Incident:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        severity:
          type: string
          enum: [minor, major, critical]
        affected_sites:
          type: array
          items:
            type: string
            format: uuid
          nullable: true
        auto_create:
          type: boolean
        status:
          type: string
          enum: [investigating, identified, monitoring, resolved]
        updates:
          type: array
          items:
            type: object
            properties:
              status:
                type: string
                enum: [investigating, identified, monitoring, resolved]
              message:
                type: string
              created_at:
                type: string
                format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    MaintenanceWindow:
      type: object
      properties:
        id:
          type: string
          format: uuid
        site_id:
          type: string
          format: uuid
          nullable: true
        starts_at:
          type: string
          format: date-time
        ends_at:
          type: string
          format: date-time
        description:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time

    NotificationSettings:
      type: object
      properties:
        email:
          type: boolean
        slack_webhook_url:
          type: string
          nullable: true
        discord_webhook_url:
          type: string
          nullable: true
        teams_webhook:
          type: string
          nullable: true
        custom_webhook_url:
          type: string
          nullable: true

    Alert:
      type: object
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum: [ssl, dns, domain, ct, cert_change]
        severity:
          type: string
          enum: [info, warning, critical]
        message:
          type: string
        suggestion:
          type: string
          nullable: true
        is_resolved:
          type: boolean
        site_id:
          type: string
          format: uuid
        created_at:
          type: string
          format: date-time

    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string

    RateLimitError:
      type: object
      properties:
        error:
          type: string
          example: "Rate limit exceeded"
        message:
          type: string
          example: "Monthly API call limit exceeded. Upgrade your plan for more calls."
        retry_after:
          type: integer
          description: Seconds until the rate limit resets
          nullable: true

paths:
  /sites:
    get:
      summary: List monitored sites
      operationId: listSites
      responses:
        "200":
          description: List of sites
          content:
            application/json:
              schema:
                type: object
                properties:
                  sites:
                    type: array
                    items:
                      $ref: "#/components/schemas/Site"
                  plan:
                    type: string
                  userId:
                    type: string
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Maximum requests per minute
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Remaining requests in current window
            X-RateLimit-Reset:
              schema:
                type: integer
              description: Unix timestamp when the window resets
            X-MonthlyLimit-Limit:
              schema:
                type: integer
              description: Monthly API call limit for your plan
            X-MonthlyLimit-Used:
              schema:
                type: integer
              description: API calls used this month
            X-MonthlyLimit-Remaining:
              schema:
                type: integer
              description: API calls remaining this month
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RateLimitError"

    post:
      summary: Add a new site
      operationId: createSite
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
                custom_port:
                  type: integer
                origin_ip:
                  type: string
                alert_days:
                  type: integer
                check_types:
                  type: array
                  items:
                    type: string
                dns_provider:
                  type: string
                dns_provider_config:
                  type: object
                deploy_target:
                  type: string
                deploy_config:
                  type: object
      responses:
        "200":
          description: Site created
          content:
            application/json:
              schema:
                type: object
                properties:
                  site:
                    $ref: "#/components/schemas/Site"
        "400":
          description: Bad request
        "401":
          description: Unauthorized
        "403":
          description: Plan limit reached
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RateLimitError"

  /sites?id={site_id}:
    patch:
      summary: Update site configuration
      operationId: updateSite
      parameters:
        - name: site_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                custom_port:
                  type: integer
                origin_ip:
                  type: string
                alert_days:
                  type: integer
                check_types:
                  type: array
                  items:
                    type: string
                dns_provider:
                  type: string
                dns_provider_config:
                  type: object
                deploy_target:
                  type: string
                deploy_config:
                  type: object
      responses:
        "200":
          description: Site updated
        "401":
          description: Unauthorized
        "404":
          description: Site not found

    delete:
      summary: Remove a site
      operationId: deleteSite
      parameters:
        - name: site_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Site deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        "401":
          description: Unauthorized
        "404":
          description: Site not found

  /check-ssl:
    post:
      summary: Run SSL/TLS certificate check
      operationId: checkSSL
      description: Free+ plan required. QPS and monthly limits apply per plan.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
                port:
                  type: integer
                  default: 443
                origin_ip:
                  type: string
                site_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: SSL/TLS check result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SSLCheckResult"
        "401":
          description: Unauthorized

  /check-dns:
    post:
      summary: Run DNS record check
      operationId: checkDNS
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
                site_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: DNS check result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DNSCheckResult"
        "401":
          description: Unauthorized

  /check-domain:
    post:
      summary: Run domain expiry check
      operationId: checkDomain
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
                site_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Domain check result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DomainCheckResult"
        "401":
          description: Unauthorized

  /checks:
    get:
      summary: Get check history
      operationId: getChecks
      parameters:
        - name: site_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: check_type
          in: query
          schema:
            type: string
            enum: [ssl, dns, domain, ct, uptime]
        - name: from
          in: query
          schema:
            type: string
        - name: to
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 200
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Check results
          content:
            application/json:
              schema:
                type: object
                properties:
                  checks:
                    type: array
                    items:
                      type: object
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized

  /alerts:
    get:
      summary: List alerts
      operationId: listAlerts
      parameters:
        - name: type
          in: query
          schema:
            type: string
            enum: [ssl, dns, domain, ct, cert_change]
        - name: severity
          in: query
          schema:
            type: string
            enum: [info, warning, critical]
        - name: resolved
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Alert list
          content:
            application/json:
              schema:
                type: object
                properties:
                  alerts:
                    type: array
                    items:
                      $ref: "#/components/schemas/Alert"
                  total:
                    type: integer
        "401":
          description: Unauthorized

  /alerts?id={alert_id}:
    patch:
      summary: Resolve an alert
      operationId: resolveAlert
      parameters:
        - name: alert_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                is_resolved:
                  type: boolean
      responses:
        "200":
          description: Alert resolved
          content:
            application/json:
              schema:
                type: object
                properties:
                  alert:
                    $ref: "#/components/schemas/Alert"
        "401":
          description: Unauthorized
        "404":
          description: Alert not found

  /api-keys:
    get:
      summary: List API keys
      operationId: listApiKeys
      description: "API keys available on all plans. Key limits: Free (1 key), Starter (3), Pro (5)."
      responses:
        "200":
          description: API key list
          content:
            application/json:
              schema:
                type: object
                properties:
                  keys:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        name:
                          type: string
                        prefix:
                          type: string
                        last_used_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
        "401":
          description: Unauthorized

    post:
      summary: Create API key
      operationId: createApiKey
      description: "API keys available on all plans. Key limits: Free (1 key), Starter (3), Pro (5)."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
      responses:
        "200":
          description: API key created
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
                  prefix:
                    type: string
                  name:
                    type: string
        "401":
          description: Unauthorized

  /api-keys?id={key_id}:
    delete:
      summary: Delete API key
      operationId: deleteApiKey
      parameters:
        - name: key_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Key deleted
        "401":
          description: Unauthorized

  /cert-history:
    get:
      summary: Get certificate change history
      operationId: getCertHistory
      parameters:
        - name: site_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Certificate history
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CertHistoryResult"
        "401":
          description: Unauthorized

  /incidents:
    get:
      summary: List incidents
      operationId: listIncidents
      responses:
        "200":
          description: Incident list
          content:
            application/json:
              schema:
                type: object
                properties:
                  incidents:
                    type: array
                    items:
                      $ref: "#/components/schemas/Incident"
                  total:
                    type: integer
        "401":
          description: Unauthorized

    post:
      summary: Create incident
      operationId: createIncident
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title]
              properties:
                title:
                  type: string
                severity:
                  type: string
                  enum: [minor, major, critical]
                affected_sites:
                  type: array
                  items:
                    type: string
                    format: uuid
                auto_create:
                  type: boolean
      responses:
        "200":
          description: Incident created
          content:
            application/json:
              schema:
                type: object
                properties:
                  incident:
                    $ref: "#/components/schemas/Incident"
        "401":
          description: Unauthorized

  /incidents/{id}/updates:
    post:
      summary: Add incident update
      operationId: addIncidentUpdate
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [status, message]
              properties:
                status:
                  type: string
                  enum: [investigating, identified, monitoring, resolved]
                message:
                  type: string
      responses:
        "200":
          description: Update added
          content:
            application/json:
              schema:
                type: object
                properties:
                  incident:
                    $ref: "#/components/schemas/Incident"
        "401":
          description: Unauthorized
        "404":
          description: Incident not found

  /maintenance-windows:
    get:
      summary: List maintenance windows
      operationId: listMaintenanceWindows
      responses:
        "200":
          description: Maintenance window list
          content:
            application/json:
              schema:
                type: object
                properties:
                  windows:
                    type: array
                    items:
                      $ref: "#/components/schemas/MaintenanceWindow"
                  total:
                    type: integer
        "401":
          description: Unauthorized

    post:
      summary: Create maintenance window
      operationId: createMaintenanceWindow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [starts_at, ends_at]
              properties:
                site_id:
                  type: string
                  format: uuid
                starts_at:
                  type: string
                  format: date-time
                ends_at:
                  type: string
                  format: date-time
                description:
                  type: string
      responses:
        "200":
          description: Maintenance window created
          content:
            application/json:
              schema:
                type: object
                properties:
                  window:
                    $ref: "#/components/schemas/MaintenanceWindow"
        "401":
          description: Unauthorized

    delete:
      summary: Delete maintenance window
      operationId: deleteMaintenanceWindow
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Maintenance window deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        "401":
          description: Unauthorized
        "404":
          description: Maintenance window not found

  /settings/notifications:
    get:
      summary: Get notification settings
      operationId: getNotificationSettings
      responses:
        "200":
          description: Notification settings
          content:
            application/json:
              schema:
                type: object
                properties:
                  settings:
                    $ref: "#/components/schemas/NotificationSettings"
        "401":
          description: Unauthorized

    patch:
      summary: Update notification settings
      operationId: updateNotificationSettings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: boolean
                slack_webhook_url:
                  type: string
                discord_webhook_url:
                  type: string
                teams_webhook:
                  type: string
                custom_webhook_url:
                  type: string
      responses:
        "200":
          description: Settings updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  settings:
                    $ref: "#/components/schemas/NotificationSettings"
        "401":
          description: Unauthorized
        "403":
          description: Plan restriction

    post:
      summary: Send test notification
      operationId: sendTestNotification
      responses:
        "200":
          description: Test notification sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  channels:
                    type: array
                    items:
                      type: string
        "401":
          description: Unauthorized

  /mcp:
    post:
      summary: MCP endpoint (JSON-RPC 2.0)
      operationId: mcpEndpoint
      description: |
        Model Context Protocol endpoint supporting JSON-RPC 2.0 requests.
        Available methods: initialize, tools/list, tools/call, ping
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [jsonrpc, id, method]
              properties:
                jsonrpc:
                  type: string
                  enum: ["2.0"]
                id:
                  type: integer
                method:
                  type: string
                  enum: [initialize, "tools/list", "tools/call", ping]
                params:
                  type: object
      responses:
        "200":
          description: JSON-RPC response
          content:
            application/json:
              schema:
                type: object
                properties:
                  jsonrpc:
                    type: string
                    enum: ["2.0"]
                  id:
                    type: integer
                  result:
                    type: object
                  error:
                    type: object
                    properties:
                      code:
                        type: integer
                      message:
                        type: string
        "401":
          description: Unauthorized

  /renewal:
    post:
      summary: Trigger certificate renewal
      operationId: triggerRenewal
      deprecated: true
      description: Deprecated — not available to new users. Pro+ plan required. Triggers renewal via Let's Encrypt.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [site_id]
              properties:
                site_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Renewal initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  site_id:
                    type: string
        "401":
          description: Unauthorized
        "403":
          description: Pro+ plan required

    get:
      summary: Get renewal logs
      operationId: getRenewalLogs
      deprecated: true
      description: Deprecated — not available to new users.
      parameters:
        - name: site_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Renewal logs
          content:
            application/json:
              schema:
                type: object
                properties:
                  logs:
                    type: array
                    items:
                      type: object
                      properties:
                        status:
                          type: string
                          enum: [pending, success, failed]
                        message:
                          type: string
                        created_at:
                          type: string
                          format: date-time
        "401":
          description: Unauthorized

  /public/check-ssl:
    get:
      summary: Public SSL/TLS check (no auth)
      operationId: publicCheckSSL
      security: []
      parameters:
        - name: domain
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: SSL/TLS check result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SSLCheckResult"
        "429":
          description: Rate limited (10 req/min per IP)
