openapi: 3.0.3
info:
  title: WebMon API
  version: 1.0.0-beta
  description: |
    WebMon API v1 beta for managing monitors, reading check history, tags, alert settings and status pages.

    Access is currently in limited beta. API requests require a bearer token created from your WebMon profile after API access is enabled for your account.
  contact:
    name: WebMon Support
    url: https://webmon.site/contact
servers:
  - url: https://webmon.site/api/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Tokens
    description: API token lifecycle
  - name: Monitors
    description: Monitor management
  - name: Checks
    description: Monitor check history
  - name: Tags
    description: User tags
  - name: Alert settings
    description: Notification settings
  - name: Status pages
    description: Public status page listings
paths:
  /tokens:
    get:
      tags: [Tokens]
      summary: List API tokens
      description: Requires `tokens:manage` ability.
      parameters:
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Token list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiTokenListResponse'
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      tags: [Tokens]
      summary: Create API token
      description: Requires `tokens:manage` ability. The raw token is shown once in the response.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateApiTokenRequest'
            examples:
              cliToken:
                value:
                  name: Local scripts
                  abilities: [monitors:read, monitors:write, tags:read]
      responses:
        '201':
          description: Token created. Copy `data.token` now; it is not shown again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiTokenCreatedResponse'
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/ValidationError' }
  /tokens/{token}:
    delete:
      tags: [Tokens]
      summary: Revoke API token
      description: Requires `tokens:manage` ability.
      parameters:
        - name: token
          in: path
          required: true
          schema: { type: integer }
      responses:
        '204': { description: Token revoked }
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /monitors:
    get:
      tags: [Monitors]
      summary: List monitors
      description: Requires `monitors:read` ability. Returns monitors owned by the token owner.
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PerPage'
        - name: status
          in: query
          schema:
            type: string
            enum: [up, down, blocked, unknown, checking]
        - name: type
          in: query
          schema:
            type: string
            enum: [http, keyword, dns]
        - name: active
          in: query
          schema: { type: boolean }
        - name: tag
          in: query
          schema: { type: string }
        - name: search
          in: query
          schema: { type: string }
      responses:
        '200':
          description: Monitor list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorListResponse'
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      tags: [Monitors]
      summary: Create monitor
      description: Requires `monitors:write` ability. Plan limits and feature gates still apply.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMonitorRequest'
            examples:
              http:
                summary: HTTP monitor
                value:
                  name: Example HTTP
                  type: http
                  url: https://example.com
                  check_interval: 5
                  follow_redirects: true
                  is_active: true
              keyword:
                summary: Keyword monitor
                value:
                  name: Example keyword
                  type: keyword
                  url: https://example.com
                  keyword: Example Domain
                  keyword_match_type: contains
                  keyword_case_sensitive: false
                  check_interval: 5
              dns:
                summary: DNS monitor
                value:
                  name: Example DNS
                  type: dns
                  dns_hostname: example.com
                  dns_record_type: A
                  dns_expected_value: 93.184.216.34
                  dns_server: 1.1.1.1
                  check_interval: 5
      responses:
        '201':
          description: Monitor created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorResponse'
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/ValidationError' }
  /monitors/{monitor}:
    parameters:
      - $ref: '#/components/parameters/MonitorId'
    get:
      tags: [Monitors]
      summary: Read monitor
      description: Requires `monitors:read` ability.
      responses:
        '200':
          description: Monitor details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorResponse'
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      tags: [Monitors]
      summary: Update monitor
      description: Requires `monitors:write` ability. Partial update is allowed. Monitor type cannot be changed for existing monitors.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMonitorRequest'
      responses:
        '200':
          description: Monitor updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorResponse'
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/ValidationError' }
    delete:
      tags: [Monitors]
      summary: Delete monitor
      description: Requires `monitors:write` ability.
      responses:
        '204': { description: Monitor deleted }
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /monitors/{monitor}/checks:
    get:
      tags: [Checks]
      summary: List monitor check history
      description: Requires `monitors:read` ability.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PerPage'
        - name: status
          in: query
          schema:
            type: string
            enum: [up, down, blocked]
        - name: source_type
          in: query
          schema:
            type: string
            enum: [cloudflare, regional]
        - name: check_node
          in: query
          schema: { type: string, example: rn01 }
        - name: region
          in: query
          schema: { type: string, example: LHR }
        - name: country
          in: query
          schema: { type: string, example: GB }
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
      responses:
        '200':
          description: Check history
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorCheckListResponse'
        '401': { $ref: '#/components/responses/Unauthenticated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /monitors/{monitor}/pause:
    post:
      tags: [Monitors]
      summary: Pause monitor notifications
      description: Requires `monitors:write` ability.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [duration_minutes]
              properties:
                duration_minutes: { type: integer, minimum: 1, maximum: 10080, example: 60 }
                reason: { type: string, nullable: true, maxLength: 255, example: Maintenance window }
      responses:
        '200':
          description: Monitor paused
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorResponse'
        '422': { $ref: '#/components/responses/ValidationError' }
  /monitors/{monitor}/resume:
    post:
      tags: [Monitors]
      summary: Resume monitor notifications
      description: Requires `monitors:write` ability.
      parameters:
        - $ref: '#/components/parameters/MonitorId'
      responses:
        '200':
          description: Monitor resumed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorResponse'
  /tags:
    get:
      tags: [Tags]
      summary: List tags
      description: Requires `tags:read` ability.
      responses:
        '200':
          description: Tag list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagListResponse'
  /alert-settings:
    get:
      tags: [Alert settings]
      summary: Read alert settings
      description: Requires `alerts:read` ability.
      responses:
        '200':
          description: Alert settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertSettingsResponse'
    patch:
      tags: [Alert settings]
      summary: Update alert settings
      description: Requires `alerts:write` ability.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAlertSettingsRequest'
      responses:
        '200':
          description: Alert settings updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertSettingsResponse'
        '422': { $ref: '#/components/responses/ValidationError' }
  /status-pages:
    get:
      tags: [Status pages]
      summary: List status pages
      description: Requires `status-pages:read` ability.
      responses:
        '200':
          description: Status pages
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusPageListResponse'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: WebMon API token
  parameters:
    Page:
      name: page
      in: query
      schema: { type: integer, minimum: 1, default: 1 }
    PerPage:
      name: per_page
      in: query
      schema: { type: integer, minimum: 1, maximum: 100, default: 25 }
    MonitorId:
      name: monitor
      in: path
      required: true
      description: Monitor UUID
      schema: { type: string, format: uuid }
  responses:
    Unauthenticated:
      description: Missing or invalid bearer token
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example: { message: Unauthenticated. }
    Forbidden:
      description: API access disabled, missing plan feature, or missing token ability
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example: { message: API access is not enabled for this account. }
    NotFound:
      description: Resource not found or not owned by the token owner
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
          example: { message: Not Found }
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ValidationErrorResponse' }
  schemas:
    ErrorResponse:
      type: object
      properties:
        message: { type: string }
    ValidationErrorResponse:
      type: object
      properties:
        message: { type: string, example: The given data was invalid. }
        errors:
          type: object
          additionalProperties:
            type: array
            items: { type: string }
    ApiToken:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        abilities:
          type: array
          items:
            type: string
            enum: [monitors:read, monitors:write, tags:read, tags:write, alerts:read, alerts:write, status-pages:read, tokens:manage, '*']
        last_used_at: { type: string, format: date-time, nullable: true }
        expires_at: { type: string, format: date-time, nullable: true }
        created_at: { type: string, format: date-time }
    CreateApiTokenRequest:
      type: object
      required: [name, abilities]
      properties:
        name: { type: string, maxLength: 100 }
        abilities:
          type: array
          minItems: 1
          items:
            type: string
            enum: [monitors:read, monitors:write, tags:read, tags:write, alerts:read, alerts:write, status-pages:read, tokens:manage, '*']
        expires_at: { type: string, format: date-time, nullable: true }
    ApiTokenListResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/ApiToken' }
        links: { type: object }
        meta: { type: object }
    ApiTokenCreatedResponse:
      type: object
      properties:
        data:
          allOf:
            - $ref: '#/components/schemas/ApiToken'
            - type: object
              properties:
                token: { type: string, example: wm_live_xxx_shown_once }
    Monitor:
      type: object
      properties:
        id: { type: string, format: uuid }
        name: { type: string, nullable: true }
        url: { type: string, nullable: true, example: https://example.com }
        type: { type: string, enum: [http, keyword, dns] }
        is_active: { type: boolean }
        check_interval: { type: integer }
        primary_status: { type: string, nullable: true, example: up }
        last_checked_at: { type: string, format: date-time, nullable: true }
        response_time: { type: integer, nullable: true }
        status_code: { type: integer, nullable: true }
        default_check_source: { type: string, example: cloudflare }
        regional_checks_enabled: { type: boolean }
        assigned_check_nodes:
          type: array
          items: { type: object }
        tags:
          type: array
          items: { $ref: '#/components/schemas/Tag' }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    CreateMonitorRequest:
      type: object
      required: [type, check_interval]
      properties:
        name: { type: string, nullable: true, maxLength: 255 }
        type: { type: string, enum: [http, keyword, dns] }
        url: { type: string, format: uri, nullable: true, maxLength: 2048 }
        check_interval: { type: integer, minimum: 1 }
        is_active: { type: boolean }
        notifications_enabled: { type: boolean }
        follow_redirects: { type: boolean }
        keyword: { type: string, nullable: true, maxLength: 500 }
        keyword_match_type: { type: string, nullable: true, enum: [contains, not_contains] }
        keyword_case_sensitive: { type: boolean }
        dns_hostname: { type: string, nullable: true, maxLength: 255 }
        dns_record_type: { type: string, nullable: true, enum: [A, AAAA, CNAME, MX, TXT, NS] }
        dns_expected_value: { type: string, nullable: true, maxLength: 500 }
        dns_server: { type: string, nullable: true, maxLength: 100 }
        tag_ids:
          type: array
          items: { type: integer }
    UpdateMonitorRequest:
      allOf:
        - $ref: '#/components/schemas/CreateMonitorRequest'
    MonitorResponse:
      type: object
      properties:
        data: { $ref: '#/components/schemas/Monitor' }
    MonitorListResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Monitor' }
        links: { type: object }
        meta: { type: object }
    MonitorCheck:
      type: object
      properties:
        id: { type: integer }
        monitor_id: { type: string, format: uuid }
        checked_at: { type: string, format: date-time, nullable: true }
        received_at: { type: string, format: date-time, nullable: true }
        status: { type: string, enum: [up, down, blocked] }
        status_code: { type: integer, nullable: true }
        response_time: { type: integer, nullable: true }
        ttfb: { type: integer, nullable: true }
        content_size: { type: integer, nullable: true }
        redirect_count: { type: integer, nullable: true }
        redirect_url: { type: string, nullable: true }
        error_message: { type: string, nullable: true }
        source_type: { type: string, nullable: true, example: cloudflare }
        check_node_id: { type: integer, nullable: true }
        check_node_slug: { type: string, nullable: true }
        check_node_label: { type: string, nullable: true }
        check_region: { type: string, nullable: true }
        check_country: { type: string, nullable: true }
        rendered_with_browser: { type: boolean }
        ssl_subject: { type: string, nullable: true }
        ssl_issuer: { type: string, nullable: true }
        ssl_valid_from: { type: string, format: date-time, nullable: true }
        ssl_valid_until: { type: string, format: date-time, nullable: true }
        ssl_days_remaining: { type: integer, nullable: true }
        ssl_error: { type: string, nullable: true }
    MonitorCheckListResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/MonitorCheck' }
        links: { type: object }
        meta: { type: object }
    Tag:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        slug: { type: string }
        color: { type: string, nullable: true }
        monitor_count: { type: integer }
    TagListResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Tag' }
    AlertSettings:
      type: object
      properties:
        email_enabled: { type: boolean }
        telegram_enabled: { type: boolean }
        failure_threshold: { type: integer }
        slow_response_threshold: { type: integer }
        alert_on_redirect_change: { type: boolean }
        regional_alerts_enabled: { type: boolean }
        regional_alert_policy: { type: string, nullable: true }
    AlertSettingsResponse:
      type: object
      properties:
        data: { $ref: '#/components/schemas/AlertSettings' }
    UpdateAlertSettingsRequest:
      type: object
      properties:
        email_enabled: { type: boolean }
        telegram_enabled: { type: boolean }
        failure_threshold: { type: integer, minimum: 1 }
        slow_response_threshold: { type: integer, minimum: 1 }
        alert_on_redirect_change: { type: boolean }
        regional_alerts_enabled: { type: boolean }
    StatusPage:
      type: object
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        slug: { type: string }
        url: { type: string }
        is_public: { type: boolean }
        monitor_count: { type: integer }
        created_at: { type: string, format: date-time }
    StatusPageListResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/StatusPage' }
