openapi: 3.0.3
info:
  title: Uptime Basics API
  version: 1.0.0
  description: |
    Secure, tenant-scoped access to Uptime Basics monitor data and controls.
    Every response includes an `X-Request-Id`. Authorized responses also include
    per-minute rate-limit headers and, when available, monthly allowance headers.
servers:
  - url: https://api.uptimebasics.com/v1
security:
  - bearerToken: []
x-uptime-basics-webhooks:
  envelopeSchemaVersion: 1
  management: Account > Integrations
  delivery:
    contentType: application/json
    httpsOnly: true
    port: 443
    timeoutSeconds: 5
    followsRedirects: false
    maximumAttempts: 5
    retryDelaySeconds: [60, 300, 900, 900]
    historyDays: 30
    responseBodiesStored: false
  signing:
    algorithm: HMAC-SHA256
    header: Uptime-Basics-Signature
    signedValue: t + "." + raw_request_body
    previousSecretGraceHours: 24
  identifiers:
    eventHeader: Uptime-Basics-Event-Id
    deliveryHeader: Uptime-Basics-Delivery
    redeliveryPreservesEventId: true
  schema:
    $ref: '#/components/schemas/WebhookEventEnvelope'
paths:
  /health:
    get:
      security: []
      summary: Check API health
      responses:
        '200':
          description: API is available
  /usage:
    get:
      summary: View the current API request allowance
      responses:
        '200':
          description: Current monthly and per-minute usage
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            X-RateLimit-Monthly-Limit:
              $ref: '#/components/headers/MonthlyLimit'
            X-RateLimit-Monthly-Remaining:
              $ref: '#/components/headers/MonthlyRemaining'
            X-RateLimit-Monthly-Reset:
              $ref: '#/components/headers/MonthlyReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /capabilities:
    get:
      summary: Discover token, plan, feature, and allowance capabilities
      description: Use this endpoint instead of assuming that every account or token supports the same operations.
      responses:
        '200':
          description: Effective capabilities for the authenticated token
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilitiesResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /monitors:
    get:
      summary: List monitors
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/MonitorStatus'
        - $ref: '#/components/parameters/Enabled'
        - $ref: '#/components/parameters/MonitorType'
        - $ref: '#/components/parameters/UpdatedAfter'
        - $ref: '#/components/parameters/UpdatedBefore'
      responses:
        '200':
          description: A page of tenant-owned monitors
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      summary: Create a monitor
      description: |
        Requires `monitors:create`, an unrestricted token, and an `Idempotency-Key`.
        The monitor remains pending until its queued first check produces evidence.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MonitorCreate'
      responses:
        '201':
          description: Monitor created and first check queued
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
            Location:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorResponse'
        '409':
          description: Duplicate monitor, plan capacity, or idempotency conflict
  /monitors/{monitorId}:
    get:
      summary: Get a monitor
      parameters:
        - $ref: '#/components/parameters/MonitorId'
      responses:
        '200':
          description: Safe monitor configuration and current state
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorResponse'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      summary: Update supported monitor settings
      description: Requires `monitors:update`. Unknown fields are rejected and secret values are never returned.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MonitorUpdate'
      responses:
        '200':
          description: Monitor updated
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
        '412':
          description: The supplied monitor version is stale
        '428':
          description: If-Match is required
  /monitors/{monitorId}/summary:
    get:
      summary: Get an uptime and response-time summary
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - name: range
          in: query
          schema:
            type: string
            enum: [24h, 7d, 30d]
            default: 24h
      responses:
        '200':
          description: Aggregated monitor metrics and bounded timeline buckets
  /monitors/{monitorId}/checks:
    get:
      summary: List retained detailed checks
      description: Requires the Pro plan and the checks:read scope.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of check results
        '403':
          $ref: '#/components/responses/Forbidden'
  /monitors/{monitorId}/incidents:
    get:
      summary: List incidents for a monitor
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of incident records
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncidentListResponse'
  /monitors/{monitorId}/alerts:
    get:
      summary: List alert history for a monitor
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of monitor-scoped alert-delivery records
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertListResponse'
  /monitors/{monitorId}/alert-settings:
    get:
      summary: View per-monitor alert settings
      description: Requires `alerts:write`. Destination values are selected from the account's verified destination IDs.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
      responses:
        '200':
          description: Current alert channels, destination IDs, and slow-response settings
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorAlertSettingsResponse'
    patch:
      summary: Update per-monitor alert settings
      description: |
        Requires `alerts:write`, `If-Match`, and `Idempotency-Key`. Only destination IDs
        returned by `/alert-destinations` are accepted. This route cannot add or verify
        email addresses or phone numbers.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MonitorAlertSettingsUpdate'
      responses:
        '200':
          description: Alert settings updated
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
        '412':
          description: The supplied monitor version is stale
        '428':
          description: If-Match is required
  /monitors/{monitorId}/expiry:
    get:
      summary: View SSL certificate and domain-expiry monitoring state
      parameters:
        - $ref: '#/components/parameters/MonitorId'
      responses:
        '200':
          description: Current expiry-monitoring configuration and observations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpiryResponse'
  /monitors/{monitorId}/status-page:
    get:
      summary: View a monitor status-page configuration
      parameters:
        - $ref: '#/components/parameters/MonitorId'
      responses:
        '200':
          description: Public status-page state and URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusPageResponse'
    patch:
      summary: Enable, disable, or update safe status-page content
      description: |
        Requires `status-pages:write`, `If-Match`, and `Idempotency-Key`. The initial page
        and slug must be created in the dashboard. Branding, ownership verification,
        analytics, asset uploads, custom domains, certificates, and CloudFront operations
        remain dashboard-only.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StatusPageUpdate'
      responses:
        '200':
          description: Status-page settings and public snapshot updated
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
        '409':
          description: Initial dashboard setup or a dashboard-only custom-domain operation is required
        '412':
          description: The supplied monitor version is stale
        '428':
          description: If-Match is required
  /monitors/{monitorId}/pause:
    post:
      summary: Pause a monitor
      description: Requires `monitors:control` and an `Idempotency-Key`. Legacy `monitors:write` tokens remain compatible.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Monitor paused
        '409':
          description: Idempotency conflict
  /monitors/{monitorId}/activate:
    post:
      summary: Activate a monitor
      description: Requires `monitors:control` and an `Idempotency-Key`. Legacy `monitors:write` tokens remain compatible.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Monitor activated
        '409':
          description: Idempotency conflict
  /monitors/{monitorId}/archive:
    post:
      summary: Archive a monitor without deleting retained history
      description: Requires `monitors:archive`. Permanent monitor deletion is not exposed through the customer API.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          description: Monitor archived
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
        '412':
          description: The supplied monitor version is stale
        '428':
          description: If-Match is required
  /monitors/{monitorId}/reactivate:
    post:
      summary: Reactivate an archived monitor
      description: Requires `monitors:archive`. Plan capacity and interval limits are revalidated.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                intervalSeconds:
                  type: integer
                  enum: [60, 300, 900, 1800, 3600]
      responses:
        '200':
          description: Monitor reactivated and first check queued
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
        '412':
          description: The supplied monitor version is stale
        '428':
          description: If-Match is required
  /exports:
    post:
      summary: Create an asynchronous check-log export
      description: |
        Requires `exports:create` and an `Idempotency-Key`. The export runs outside
        the request Lambda. A tenant can have at most two queued or processing exports.
        Check ranges cannot exceed the current 30-day raw-check retention window and
        exports are limited to 50,000 matching rows.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExportCreate'
      responses:
        '202':
          description: Export accepted for asynchronous processing
          headers:
            Location:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExportResponse'
        '400':
          description: Invalid range, filter, type, or format
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Idempotency conflict
        '429':
          $ref: '#/components/responses/RateLimited'
  /exports/{exportId}:
    get:
      summary: Get asynchronous export status
      description: Requires `exports:read`. Internal artifact keys and request credentials are never returned.
      parameters:
        - $ref: '#/components/parameters/ExportId'
      responses:
        '200':
          description: Export metadata and processing state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExportResponse'
        '404':
          $ref: '#/components/responses/NotFound'
  /exports/{exportId}/download:
    get:
      summary: Create a short-lived export download link
      description: |
        Requires `exports:read`. Ready artifacts remain available for six hours. This
        endpoint returns a private S3 URL that expires after at most five minutes.
      parameters:
        - $ref: '#/components/parameters/ExportId'
      responses:
        '200':
          description: Short-lived private download URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExportDownloadResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Export is still queued, processing, or failed
        '410':
          description: Export artifact has expired
  /alerts:
    get:
      summary: List alert history
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of alert-delivery records
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertListResponse'
  /alert-destinations:
    get:
      summary: List verified destinations available for monitor alerts
      description: |
        Requires `alerts:write`. Only destinations already verified in Account > Alerts
        are returned. Email and phone verification is intentionally unavailable through
        the customer API.
      responses:
        '200':
          description: Verified alert destinations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertDestinationListResponse'
  /alerts/{alertId}:
    get:
      summary: Get one alert-delivery record
      parameters:
        - $ref: '#/components/parameters/AlertId'
      responses:
        '200':
          description: Alert-delivery detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertResponse'
        '404':
          $ref: '#/components/responses/NotFound'
  /incidents:
    get:
      summary: List incidents across accessible monitors
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/IncidentStatus'
        - $ref: '#/components/parameters/IncidentType'
      responses:
        '200':
          description: A page of tenant incidents
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncidentListResponse'
  /incidents/{incidentId}:
    get:
      summary: Get an incident with bounded diagnostics and activity
      parameters:
        - $ref: '#/components/parameters/IncidentId'
      responses:
        '200':
          description: Incident detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IncidentDetailResponse'
        '404':
          $ref: '#/components/responses/NotFound'
  /status-pages:
    get:
      summary: List configured public status pages
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Enabled'
      responses:
        '200':
          description: A page of status-page records
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusPageListResponse'
  /status-pages/{monitorId}:
    get:
      summary: Get a configured public status page by monitor
      parameters:
        - $ref: '#/components/parameters/MonitorId'
      responses:
        '200':
          description: Public status-page state and URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusPageResponse'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  securitySchemes:
    bearerToken:
      type: http
      scheme: bearer
      bearerFormat: ub_live
      description: API token created from Account > Integrations.
  parameters:
    MonitorId:
      name: monitorId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    IncidentId:
      name: incidentId
      in: path
      required: true
      description: Opaque incident identifier returned by an incident list route.
      schema:
        type: string
    AlertId:
      name: alertId
      in: path
      required: true
      description: Opaque alert identifier returned by an alert list route.
      schema:
        type: string
    ExportId:
      name: exportId
      in: path
      required: true
      description: Opaque export identifier returned when an export is created.
      schema:
        type: string
        pattern: '^exp_[a-f0-9]{32}$'
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    Cursor:
      name: cursor
      in: query
      schema:
        type: string
      description: Opaque pagination cursor returned by the previous response.
    MonitorStatus:
      name: status
      in: query
      schema:
        type: string
        enum: [up, down, pending, paused]
    Enabled:
      name: enabled
      in: query
      schema:
        type: boolean
    MonitorType:
      name: type
      in: query
      schema:
        type: string
        enum: [http]
    UpdatedAfter:
      name: updated_after
      in: query
      schema:
        type: string
        format: date-time
    UpdatedBefore:
      name: updated_before
      in: query
      schema:
        type: string
        format: date-time
    IncidentStatus:
      name: status
      in: query
      schema:
        type: string
        enum: [open, resolved]
    IncidentType:
      name: type
      in: query
      schema:
        type: string
        enum: [availability, performance]
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 8
        maxLength: 128
    IfMatch:
      name: If-Match
      in: header
      required: true
      description: ETag returned by the latest monitor detail or write response.
      schema:
        type: string
  headers:
    RequestId:
      description: Stable identifier to include when contacting support about this request.
      schema:
        type: string
    RateLimit:
      description: Maximum requests allowed for the current token in the active minute.
      schema:
        type: integer
    RateLimitRemaining:
      description: Requests remaining for the current token in the active minute.
      schema:
        type: integer
    RateLimitReset:
      description: Unix timestamp when the active minute allowance resets.
      schema:
        type: integer
        format: int64
    MonthlyLimit:
      description: Monthly request allowance for the account.
      schema:
        type: integer
    MonthlyRemaining:
      description: Requests remaining in the current UTC month.
      schema:
        type: integer
    MonthlyReset:
      description: Unix timestamp when the monthly allowance resets.
      schema:
        type: integer
        format: int64
    RetryAfter:
      description: Seconds to wait before retrying the request.
      schema:
        type: integer
    ETag:
      description: Version token used with If-Match for optimistic concurrency.
      schema:
        type: string
  schemas:
    Pagination:
      type: object
      required: [limit, nextCursor]
      properties:
        limit:
          type: integer
          minimum: 1
          maximum: 100
        nextCursor:
          type: string
          nullable: true
    ExpiryState:
      type: object
      required: [enabled, warnDays, status, lastCheckedAt, expiresAt, daysRemaining]
      properties:
        enabled:
          type: boolean
        warnDays:
          type: integer
        status:
          type: string
          enum: [unknown, ok, warning, expired, error]
        lastCheckedAt:
          type: string
          format: date-time
          nullable: true
        expiresAt:
          type: string
          format: date-time
          nullable: true
        daysRemaining:
          type: integer
          nullable: true
    Monitor:
      type: object
      required: [id, name, url, type, enabled, archived, status, requestMethod, intervalSeconds, timeoutMs, followRedirects]
      properties:
        id:
          type: string
        version:
          type: string
          nullable: true
        name:
          type: string
        url:
          type: string
          format: uri
        type:
          type: string
          enum: [http]
        enabled:
          type: boolean
        archived:
          type: boolean
        status:
          type: string
          enum: [up, down, pending, paused, archived]
        requestMethod:
          type: string
          enum: [GET, HEAD]
        intervalSeconds:
          type: integer
        timeoutMs:
          type: integer
        followRedirects:
          type: boolean
        acceptedStatusCodes:
          type: array
          items:
            type: integer
            minimum: 100
            maximum: 599
        requestHeaders:
          type: array
          description: Sensitive values are redacted. `valueSet` indicates a saved value.
          items:
            $ref: '#/components/schemas/RequestHeader'
        authConfig:
          $ref: '#/components/schemas/AuthConfigResponse'
        lastCheckAt:
          type: string
          format: date-time
          nullable: true
        lastStatusAt:
          type: string
          format: date-time
          nullable: true
        responseTimeMs:
          type: integer
          nullable: true
        httpStatus:
          type: integer
          nullable: true
        expiryMonitoring:
          type: object
          properties:
            ssl:
              $ref: '#/components/schemas/ExpiryState'
            domain:
              $ref: '#/components/schemas/ExpiryState'
        slowResponse:
          type: object
          properties:
            enabled:
              type: boolean
            thresholdMs:
              type: integer
            consecutiveLimit:
              type: integer
            state:
              type: string
              enum: [healthy, slow]
            currentIncidentStartedAt:
              type: string
              format: date-time
              nullable: true
            lastAlertedAt:
              type: string
              format: date-time
              nullable: true
            lastRecoveredAt:
              type: string
              format: date-time
              nullable: true
        createdAt:
          type: string
          format: date-time
          nullable: true
        updatedAt:
          type: string
          format: date-time
          nullable: true
    RequestHeader:
      type: object
      required: [name]
      properties:
        name:
          type: string
        value:
          type: string
        valueSet:
          type: boolean
    AuthConfigInput:
      type: object
      additionalProperties: false
      properties:
        type:
          type: string
          enum: [none, basic]
        username:
          type: string
        password:
          type: string
          format: password
    AuthConfigResponse:
      type: object
      properties:
        type:
          type: string
          enum: [none, basic]
        username:
          type: string
        passwordSet:
          type: boolean
    MonitorWriteFields:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          maxLength: 120
        url:
          type: string
          format: uri
        intervalSeconds:
          type: integer
          enum: [60, 300, 900, 1800, 3600]
        timeoutMs:
          type: integer
          minimum: 1000
          maximum: 10000
        requestMethod:
          type: string
          enum: [GET, HEAD, get, head]
        followRedirects:
          type: boolean
        acceptedStatusCodes:
          type: array
          items:
            type: integer
            minimum: 100
            maximum: 599
        requestHeaders:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/RequestHeader'
        authConfig:
          $ref: '#/components/schemas/AuthConfigInput'
        alertChannels:
          type: object
          additionalProperties: false
          properties:
            email:
              type: boolean
            sms:
              type: boolean
        alertContacts:
          type: object
          deprecated: true
          description: Use alertDestinationIds for new integrations. Supplied values must already be verified.
          additionalProperties: false
          properties:
            email:
              type: string
              format: email
              nullable: true
            phone:
              type: string
              nullable: true
        alertDestinationIds:
          $ref: '#/components/schemas/AlertDestinationSelection'
        sslMonitoring:
          type: object
        domainMonitoring:
          type: object
        slowResponseAlerts:
          type: object
    MonitorCreate:
      allOf:
        - $ref: '#/components/schemas/MonitorWriteFields'
        - type: object
          required: [url]
          properties:
            enabled:
              type: boolean
    MonitorUpdate:
      $ref: '#/components/schemas/MonitorWriteFields'
    MonitorResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: '#/components/schemas/Monitor'
    MonitorListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Monitor'
        pagination:
          $ref: '#/components/schemas/Pagination'
    Incident:
      type: object
      required: [id, monitorId, startedAt, status, type]
      properties:
        id:
          type: string
        monitorId:
          type: string
          format: uuid
        monitorName:
          type: string
          nullable: true
        startedAt:
          type: string
          format: date-time
        resolvedAt:
          type: string
          format: date-time
          nullable: true
        status:
          type: string
          enum: [open, resolved]
        type:
          type: string
          enum: [availability, performance]
        thresholdMs:
          type: integer
          nullable: true
        durationMinutes:
          type: integer
          nullable: true
        httpStatus:
          type: integer
          nullable: true
        responseTimeMs:
          type: integer
          nullable: true
        failureCategory:
          type: string
          nullable: true
        failureReason:
          type: string
          nullable: true
    IncidentListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Incident'
        pagination:
          $ref: '#/components/schemas/Pagination'
    IncidentDetailResponse:
      type: object
      required: [data]
      properties:
        data:
          allOf:
            - $ref: '#/components/schemas/Incident'
            - type: object
              properties:
                diagnostics:
                  type: object
                  nullable: true
                diagnosticsUpdatedAt:
                  type: string
                  format: date-time
                  nullable: true
                activity:
                  type: array
                  maxItems: 100
                  items:
                    type: object
    Alert:
      type: object
      required: [id, timestamp, type, delivered, suppressed]
      properties:
        id:
          type: string
        monitorId:
          type: string
          format: uuid
          nullable: true
        monitorName:
          type: string
          nullable: true
        timestamp:
          type: string
          format: date-time
        type:
          type: string
          enum: [availability, performance, ssl_expiry, domain_expiry, test]
        status:
          type: string
          nullable: true
        previousStatus:
          type: string
          nullable: true
        channel:
          type: string
          enum: [email, sms]
          nullable: true
        deliveryStatus:
          type: string
          enum: [queued, sending, sent, delivered, delayed, failed, bounced, complaint, rejected, suppressed, sms_disabled]
        delivered:
          type: boolean
        suppressed:
          type: boolean
        responseTimeMs:
          type: integer
          nullable: true
        httpStatus:
          type: integer
          nullable: true
    AlertResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: '#/components/schemas/Alert'
    AlertDestinationSelection:
      type: object
      additionalProperties: false
      properties:
        email:
          type: string
          nullable: true
          pattern: '^dest_email_[a-f0-9]{24}$'
        sms:
          type: string
          nullable: true
          pattern: '^dest_sms_[a-f0-9]{24}$'
    AlertDestination:
      type: object
      required: [id, channel, value, maskedValue, isDefault]
      properties:
        id:
          type: string
        channel:
          type: string
          enum: [email, sms]
        value:
          type: string
        maskedValue:
          type: string
        label:
          type: string
          nullable: true
        isDefault:
          type: boolean
    AlertDestinationListResponse:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AlertDestination'
    AlertChannelSettings:
      type: object
      additionalProperties: false
      properties:
        email:
          type: boolean
        sms:
          type: boolean
    SlowResponseAlertSettings:
      type: object
      additionalProperties: false
      properties:
        enabled:
          type: boolean
        thresholdMs:
          type: integer
          enum: [1000, 2000, 3000, 5000, 10000]
        consecutiveLimit:
          type: integer
          enum: [2, 3, 4, 5]
        state:
          type: string
          enum: [healthy, slow]
          readOnly: true
    MonitorAlertSettingsUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        channels:
          $ref: '#/components/schemas/AlertChannelSettings'
        destinationIds:
          $ref: '#/components/schemas/AlertDestinationSelection'
        slowResponseAlerts:
          $ref: '#/components/schemas/SlowResponseAlertSettings'
    MonitorAlertSettings:
      type: object
      required: [monitorId, version, channels, destinationIds, slowResponseAlerts]
      properties:
        monitorId:
          type: string
        version:
          type: string
        channels:
          $ref: '#/components/schemas/AlertChannelSettings'
        destinationIds:
          $ref: '#/components/schemas/AlertDestinationSelection'
        slowResponseAlerts:
          $ref: '#/components/schemas/SlowResponseAlertSettings'
    MonitorAlertSettingsResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: '#/components/schemas/MonitorAlertSettings'
    AlertListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Alert'
        pagination:
          $ref: '#/components/schemas/Pagination'
    ExpiryResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          required: [monitorId, ssl, domain]
          properties:
            monitorId:
              type: string
              format: uuid
            ssl:
              $ref: '#/components/schemas/ExpiryState'
            domain:
              $ref: '#/components/schemas/ExpiryState'
    StatusPage:
      type: object
      required: [monitorId, enabled, verificationStatus]
      properties:
        monitorId:
          type: string
          format: uuid
        enabled:
          type: boolean
        slug:
          type: string
          nullable: true
        url:
          type: string
          format: uri
          nullable: true
        title:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        noindex:
          type: boolean
        displayOptions:
          $ref: '#/components/schemas/StatusPageDisplayOptions'
        verificationStatus:
          type: string
          enum: [unverified, pending, verified, failed]
        customDomain:
          type: object
          readOnly: true
          properties:
            configured:
              type: boolean
            enabled:
              type: boolean
            status:
              type: string
        version:
          type: string
          nullable: true
        updatedAt:
          type: string
          format: date-time
          nullable: true
    StatusPageDisplayOptions:
      type: object
      additionalProperties: false
      properties:
        uptimeTimeline:
          type: boolean
        responseTime:
          type: boolean
        incidents:
          type: boolean
        recentHistory:
          type: boolean
    StatusPageUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        enabled:
          type: boolean
        title:
          type: string
          maxLength: 120
        description:
          type: string
          maxLength: 500
          nullable: true
        noindex:
          type: boolean
        displayOptions:
          $ref: '#/components/schemas/StatusPageDisplayOptions'
    StatusPageResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: '#/components/schemas/StatusPage'
    StatusPageListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/StatusPage'
        pagination:
          $ref: '#/components/schemas/Pagination'
    ExportFilters:
      type: object
      additionalProperties: false
      properties:
        status:
          type: string
          enum: [all, up, down]
          default: all
        statusCode:
          type: integer
          minimum: 100
          maximum: 599
          nullable: true
        failureCategory:
          type: string
          maxLength: 80
          nullable: true
        minLatencyMs:
          type: integer
          minimum: 0
          maximum: 120000
          nullable: true
        maxLatencyMs:
          type: integer
          minimum: 0
          maximum: 120000
          nullable: true
    ExportCreate:
      type: object
      additionalProperties: false
      required: [monitorId]
      properties:
        monitorId:
          type: string
        type:
          type: string
          enum: [checks]
          default: checks
        format:
          type: string
          enum: [csv]
          default: csv
        from:
          type: string
          format: date-time
          description: Defaults to 24 hours before `to`.
        to:
          type: string
          format: date-time
          description: Defaults to the current time.
        filters:
          $ref: '#/components/schemas/ExportFilters'
    ExportJob:
      type: object
      required: [id, monitorId, type, format, status, range, filters, createdAt]
      properties:
        id:
          type: string
        monitorId:
          type: string
        monitorName:
          type: string
          nullable: true
        type:
          type: string
          enum: [checks]
        format:
          type: string
          enum: [csv]
        status:
          type: string
          enum: [queued, processing, ready, failed, expired]
        range:
          type: object
          required: [from, to]
          properties:
            from:
              type: string
              format: date-time
            to:
              type: string
              format: date-time
        filters:
          $ref: '#/components/schemas/ExportFilters'
        rowCount:
          type: integer
          nullable: true
        createdAt:
          type: string
          format: date-time
        startedAt:
          type: string
          format: date-time
          nullable: true
        completedAt:
          type: string
          format: date-time
          nullable: true
        artifactExpiresAt:
          type: string
          format: date-time
          nullable: true
        failureCode:
          type: string
          nullable: true
        notificationStatus:
          type: string
          enum: [pending, sent, failed, skipped]
          nullable: true
    ExportResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: '#/components/schemas/ExportJob'
    ExportDownloadResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          required: [exportId, url, expiresAt]
          properties:
            exportId:
              type: string
            url:
              type: string
              format: uri
            expiresAt:
              type: string
              format: date-time
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, requestId]
          properties:
            code:
              type: string
            message:
              type: string
            requestId:
              type: string
            details:
              type: array
              items:
                type: object
    UsageResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          required: [period, requestsUsed, requestsLimit, requestsRemaining, requestsPerMinute, percentUsed, warningLevel, resetsAt, plan]
          properties:
            period:
              type: string
              example: '202607'
            requestsUsed:
              type: integer
            requestsLimit:
              type: integer
            requestsRemaining:
              type: integer
            requestsPerMinute:
              type: integer
            percentUsed:
              type: number
            warningLevel:
              type: string
              enum: [normal, warning, critical, exhausted]
            resetsAt:
              type: string
              format: date-time
            plan:
              type: string
    CapabilitiesResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          required: [version, plan, scopes, limits, features, webhookEvents]
          properties:
            version:
              type: string
              example: v1
            plan:
              type: string
            scopes:
              type: object
              properties:
                token:
                  type: array
                  items:
                    type: string
                available:
                  type: array
                  items:
                    type: string
            limits:
              type: object
              additionalProperties:
                type: integer
            features:
              type: object
              additionalProperties:
                type: boolean
            webhookEvents:
              type: array
              items:
                type: string
    WebhookEventEnvelope:
      type: object
      additionalProperties: false
      required: [schemaVersion, eventId, eventType, createdAt, delivery, data]
      properties:
        schemaVersion:
          type: integer
          enum: [1]
        eventId:
          type: string
          description: Immutable event identifier shared by automatic retries and manual redeliveries.
        eventType:
          type: string
          enum: [monitor.status_changed, incident.opened, incident.resolved, performance.slow, performance.recovered, ssl.expiring, domain.expiring]
        createdAt:
          type: string
          format: date-time
        delivery:
          $ref: '#/components/schemas/WebhookDeliveryContext'
        data:
          $ref: '#/components/schemas/WebhookEventData'
    WebhookDeliveryContext:
      type: object
      additionalProperties: false
      required: [id, attempt, redelivery, redeliveryOf]
      properties:
        id:
          type: string
          description: Identifier for this delivery sequence.
        attempt:
          type: integer
          minimum: 1
          maximum: 5
        redelivery:
          type: boolean
        redeliveryOf:
          type: string
          nullable: true
    WebhookEventData:
      type: object
      additionalProperties: false
      properties:
        test:
          type: boolean
        monitor:
          $ref: '#/components/schemas/WebhookMonitorData'
        incident:
          $ref: '#/components/schemas/WebhookIncidentData'
        performance:
          $ref: '#/components/schemas/WebhookPerformanceData'
        expiry:
          $ref: '#/components/schemas/WebhookExpiryData'
    WebhookMonitorData:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
        url:
          type: string
          format: uri
          nullable: true
        status:
          type: string
          nullable: true
        previousStatus:
          type: string
          nullable: true
        checkedAt:
          type: string
          format: date-time
          nullable: true
        responseTimeMs:
          type: number
          nullable: true
        httpStatus:
          type: number
          nullable: true
        failureCategory:
          type: string
          nullable: true
        failureReason:
          type: string
          nullable: true
    WebhookIncidentData:
      type: object
      additionalProperties: false
      properties:
        monitorId:
          type: string
          nullable: true
        startedAt:
          type: string
          format: date-time
          nullable: true
        resolvedAt:
          type: string
          format: date-time
          nullable: true
        type:
          type: string
          enum: [availability, performance]
          nullable: true
        status:
          type: string
          enum: [open, resolved]
          nullable: true
    WebhookPerformanceData:
      type: object
      additionalProperties: false
      properties:
        thresholdMs:
          type: number
          nullable: true
        status:
          type: string
          enum: [slow, recovered]
          nullable: true
    WebhookExpiryData:
      type: object
      additionalProperties: false
      properties:
        kind:
          type: string
          enum: [ssl, domain]
          nullable: true
        status:
          type: string
          nullable: true
        previousStatus:
          type: string
          nullable: true
        expiresAt:
          type: string
          format: date-time
          nullable: true
        daysRemaining:
          type: number
          nullable: true
        warningThresholdDays:
          type: number
          nullable: true
  responses:
    Unauthorized:
      description: The API token is missing, invalid, expired, or revoked.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: The token lacks the required scope or plan feature.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: The requested tenant-owned resource was not found.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: A per-minute or monthly API request limit was reached.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
