openapi: 3.1.0
info:
  title: CountdownShare Developer API
  version: 1.0.0
  description: >-
    Create, manage, and monitor CountdownShare timers. Authenticated responses
    include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers.
servers:
  - url: https://countdownshare.com/api/v1
security:
  - bearerApiKey: []
paths:
  /timers:
    get:
      operationId: listTimers
      summary: List timers
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - name: type
          in: query
          schema: { type: string, enum: [fixed, duration, recurring, personalized] }
        - name: status
          in: query
          schema: { type: string, enum: [draft, published, archived] }
        - name: sort
          in: query
          schema: { type: string, enum: [created_at, -created_at, updated_at, -updated_at], default: -created_at }
      responses:
        '200': { $ref: '#/components/responses/TimerList' }
        '401': { $ref: '#/components/responses/Error' }
    post:
      operationId: createTimer
      summary: Create a timer
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreateTimerRequest' }
      responses:
        '201': { $ref: '#/components/responses/Timer' }
        '400': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}:
    parameters:
      - $ref: '#/components/parameters/TimerId'
    get:
      operationId: getTimer
      summary: Get a timer
      responses:
        '200': { $ref: '#/components/responses/Timer' }
        '404': { $ref: '#/components/responses/Error' }
    patch:
      operationId: updateTimer
      summary: Update a timer
      parameters:
        - name: If-Match
          in: header
          required: true
          schema: { type: integer, minimum: 1 }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UpdateTimerRequest' }
      responses:
        '200': { $ref: '#/components/responses/Timer' }
        '409': { $ref: '#/components/responses/Error' }
    delete:
      operationId: deleteTimer
      summary: Delete a timer
      responses:
        '200': { $ref: '#/components/responses/Deleted' }
        '404': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/duplicate:
    parameters:
      - $ref: '#/components/parameters/TimerId'
    post:
      operationId: duplicateTimer
      summary: Duplicate a timer
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                name: { type: string, minLength: 1, maxLength: 200 }
      responses:
        '201': { $ref: '#/components/responses/Timer' }
        '404': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/archive:
    parameters:
      - $ref: '#/components/parameters/TimerId'
    post:
      operationId: archiveTimer
      summary: Archive a timer
      responses:
        '200': { $ref: '#/components/responses/Timer' }
        '404': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/restore:
    parameters:
      - $ref: '#/components/parameters/TimerId'
    post:
      operationId: restoreTimer
      summary: Restore an archived timer
      responses:
        '200': { $ref: '#/components/responses/Timer' }
        '404': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/actions:
    post:
      operationId: controlDurationTimer
      summary: Start, pause, resume, or reset a duration timer
      parameters:
        - $ref: '#/components/parameters/TimerId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [action]
              properties:
                action: { type: string, enum: [start, pause, resume, reset] }
      responses:
        '200': { $ref: '#/components/responses/Timer' }
        '400': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/status:
    get:
      operationId: getTimerStatus
      summary: Get live timer status
      parameters:
        - $ref: '#/components/parameters/TimerId'
      responses:
        '200': { $ref: '#/components/responses/TimerStatus' }
        '404': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/outputs:
    get:
      operationId: getTimerOutputs
      summary: Get timer display options
      parameters:
        - $ref: '#/components/parameters/TimerId'
      responses:
        '200': { $ref: '#/components/responses/TimerOutputs' }
        '404': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/activity:
    get:
      operationId: listTimerActivity
      summary: List timer activity
      parameters:
        - $ref: '#/components/parameters/TimerId'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200': { $ref: '#/components/responses/TimerActivityList' }
  /timers/{timer_id}/metrics:
    get:
      operationId: getTimerMetrics
      summary: Get timer metrics
      parameters:
        - $ref: '#/components/parameters/TimerId'
      responses:
        '200': { $ref: '#/components/responses/TimerMetrics' }
  /timezones:
    get:
      operationId: listTimezones
      summary: List accepted IANA timezones
      parameters:
        - name: search
          in: query
          schema: { type: string, maxLength: 100 }
      responses:
        '200': { $ref: '#/components/responses/Timezones' }
  /webhook-destinations:
    get:
      operationId: listWebhookDestinations
      summary: List webhook destinations
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200': { $ref: '#/components/responses/WebhookDestinationList' }
    post:
      operationId: createWebhookDestination
      summary: Create a webhook destination
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreateWebhookDestinationRequest' }
      responses:
        '201': { $ref: '#/components/responses/WebhookDestinationCreated' }
        '400': { $ref: '#/components/responses/Error' }
  /webhook-destinations/{destination_id}:
    parameters:
      - $ref: '#/components/parameters/DestinationId'
    get:
      operationId: getWebhookDestination
      summary: Get a webhook destination
      responses:
        '200': { $ref: '#/components/responses/WebhookDestination' }
        '404': { $ref: '#/components/responses/Error' }
    patch:
      operationId: updateWebhookDestination
      summary: Update a webhook destination
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UpdateWebhookDestinationRequest' }
      responses:
        '200': { $ref: '#/components/responses/WebhookDestination' }
        '400': { $ref: '#/components/responses/Error' }
        '404': { $ref: '#/components/responses/Error' }
    delete:
      operationId: deleteWebhookDestination
      summary: Delete a webhook destination
      responses:
        '200': { $ref: '#/components/responses/Data' }
        '409': { $ref: '#/components/responses/Error' }
  /webhook-destinations/{destination_id}/test:
    parameters:
      - $ref: '#/components/parameters/DestinationId'
    post:
      operationId: testWebhookDestination
      summary: Send a test webhook delivery
      responses:
        '202': { $ref: '#/components/responses/Data' }
        '404': { $ref: '#/components/responses/Error' }
  /webhook-destinations/{destination_id}/rotate-secret:
    parameters:
      - $ref: '#/components/parameters/DestinationId'
    post:
      operationId: rotateWebhookDestinationSecret
      summary: Rotate a webhook signing secret
      responses:
        '200': { $ref: '#/components/responses/Data' }
        '404': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/webhook-rules:
    parameters:
      - $ref: '#/components/parameters/TimerId'
    get:
      operationId: listTimerWebhookRules
      summary: List webhook rules for a timer
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200': { $ref: '#/components/responses/WebhookRuleList' }
    post:
      operationId: createTimerWebhookRule
      summary: Create a webhook rule for a timer
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WebhookRuleRequest' }
      responses:
        '201': { $ref: '#/components/responses/WebhookRule' }
        '400': { $ref: '#/components/responses/Error' }
  /timers/{timer_id}/webhook-rules/{rule_id}:
    parameters:
      - $ref: '#/components/parameters/TimerId'
      - $ref: '#/components/parameters/RuleId'
    get:
      operationId: getTimerWebhookRule
      summary: Get a webhook rule
      responses:
        '200': { $ref: '#/components/responses/WebhookRule' }
        '404': { $ref: '#/components/responses/Error' }
    patch:
      operationId: updateTimerWebhookRule
      summary: Update a webhook rule
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UpdateWebhookRuleRequest' }
      responses:
        '200': { $ref: '#/components/responses/WebhookRule' }
        '400': { $ref: '#/components/responses/Error' }
    delete:
      operationId: deleteTimerWebhookRule
      summary: Delete a webhook rule
      responses:
        '200': { $ref: '#/components/responses/Data' }
  /webhook-deliveries:
    get:
      operationId: listWebhookDeliveries
      summary: List webhook deliveries
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - name: status
          in: query
          schema: { type: string, enum: [queued, processing, retrying, succeeded, failed, cancelled] }
      responses:
        '200': { $ref: '#/components/responses/WebhookDeliveryList' }
  /webhook-deliveries/{delivery_id}:
    parameters:
      - $ref: '#/components/parameters/DeliveryId'
    get:
      operationId: getWebhookDelivery
      summary: Get a webhook delivery
      responses:
        '200': { $ref: '#/components/responses/WebhookDelivery' }
        '404': { $ref: '#/components/responses/Error' }
  /webhook-deliveries/{delivery_id}/replay:
    parameters:
      - $ref: '#/components/parameters/DeliveryId'
    post:
      operationId: replayWebhookDelivery
      summary: Replay a webhook delivery
      responses:
        '202': { $ref: '#/components/responses/Data' }
        '404': { $ref: '#/components/responses/Error' }
  /usage:
    get:
      operationId: getUsage
      summary: Get current plan usage and limits
      responses:
        '200': { $ref: '#/components/responses/Usage' }
        '429': { $ref: '#/components/responses/Error' }
  /usage/requests:
    get:
      operationId: listRequestLogs
      summary: List API request logs
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - name: status
          in: query
          schema: { type: string, enum: [success, error] }
        - name: route
          in: query
          schema: { type: string }
        - name: key_id
          in: query
          schema: { type: string, format: uuid }
        - name: request_id
          in: query
          schema: { type: string, format: uuid }
      responses:
        '200': { $ref: '#/components/responses/RequestLogList' }
        '429': { $ref: '#/components/responses/Error' }
components:
  securitySchemes:
    bearerApiKey:
      type: http
      scheme: bearer
      description: Use a cs_test or cs_live API key.
  parameters:
    TimerId:
      name: timer_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    DestinationId:
      name: destination_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    RuleId:
      name: rule_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    DeliveryId:
      name: delivery_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    Cursor:
      name: cursor
      in: query
      schema: { type: string }
    Limit:
      name: limit
      in: query
      schema: { type: integer, minimum: 1, maximum: 100, default: 25 }
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema: { type: string, minLength: 1, maxLength: 255 }
  schemas:
    WebhookEventType:
      type: string
      description: |
        Domain event recorded against a timer. These are the values stored on a
        delivery record. Note that the outbound webhook body uses
        `timer.rule.matched` for any delivery produced by a webhook rule; see
        WebhookPayload.
      enum:
        - timer.created
        - timer.updated
        - timer.deleted
        - timer.duplicated
        - timer.archived
        - timer.restored
        - timer.started
        - timer.paused
        - timer.resumed
        - timer.reset
        - timer.completed
        - timer.recurrence_completed
      example: timer.completed
    WebhookPayload:
      type: object
      description: |
        The JSON body POSTed to a webhook destination.

        Every request carries three headers:

        - `X-CountdownShare-Event-Id` - the event id, stable across retries and replays.
        - `X-CountdownShare-Timestamp` - Unix seconds, included in the signed string.
        - `X-CountdownShare-Signature` - `v1=<hex>`, an HMAC-SHA256 of
          `"{timestamp}.{raw_body}"` using the destination signing secret.

        Verify the signature against the raw request body before parsing it, and
        treat delivery as at-least-once: the same `id` may arrive more than once
        after a retry or a manual replay, so handlers must be idempotent.
      required: [id, type, occurred_at, delivery_id, data]
      properties:
        id:
          type: string
          format: uuid
          description: Event id. Identical across retries and replays of the same event.
        type:
          type: string
          description: |
            `timer.rule.matched` when a webhook rule produced this delivery,
            otherwise the underlying WebhookEventType.
          example: timer.rule.matched
        occurred_at: { type: string, format: date-time }
        delivery_id:
          type: string
          format: uuid
          description: Unique per delivery attempt chain; differs between an original and its replay.
        timer:
          type: [object, 'null']
          properties:
            id: { type: string, format: uuid }
            name: { type: [string, 'null'] }
            type: { type: [string, 'null'], enum: [fixed, duration, recurring, personalized, null] }
            status: { type: [string, 'null'] }
        rule:
          type: [object, 'null']
          description: Present only when a webhook rule produced this delivery.
          properties:
            id: { type: string, format: uuid }
            name: { type: [string, 'null'] }
        data:
          type: object
          description: |
            Timer status values at the moment of delivery, merged with any
            event-specific fields. Recurring cycle events add
            `recurrence_cycle: "completed"` plus `occurrence_at` and
            `next_occurrence_at`.
          properties:
            status: { type: string }
            remaining_seconds: { type: [number, 'null'] }
            progress_percent: { type: [number, 'null'] }
            server_time: { type: string, format: date-time }
            recurrence_cycle: { type: string, enum: [completed] }
            occurrence_at: { type: string, format: date-time }
            next_occurrence_at: { type: string, format: date-time }
    CreateTimerRequest:
      type: object
      required: [name, type]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 200 }
        type: { type: string, enum: [fixed, duration, recurring, personalized] }
        deadline_at: { type: string, format: date-time, description: Required for fixed-date countdowns and must include Z or a numeric UTC offset. }
        duration_seconds: { type: integer, minimum: 1, description: Required for duration and personalized timers. }
        timezone: { type: string, default: UTC }
        recurrence: { $ref: '#/components/schemas/Recurrence' }
        external_user_id: { type: string, minLength: 1, maxLength: 255, description: Required for personalized countdowns. }
        publish: { type: boolean, default: false }
        expiry: { $ref: '#/components/schemas/Expiry' }
        metadata: { $ref: '#/components/schemas/Metadata' }
    UpdateTimerRequest:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 200 }
        deadline_at: { type: string, format: date-time, description: Fixed timers only; must include Z or a numeric UTC offset. }
        duration_seconds: { type: integer, minimum: 1, maximum: 31536000 }
        timezone: { type: string }
        recurrence:
          oneOf:
            - $ref: '#/components/schemas/Recurrence'
            - type: 'null'
        external_user_id: { type: string, minLength: 1, maxLength: 255 }
        publish: { type: boolean, description: Make the Developer public page available or unavailable. }
        expiry: { $ref: '#/components/schemas/Expiry' }
        metadata: { $ref: '#/components/schemas/Metadata' }
    Recurrence:
      type: object
      required: [frequency, local_time]
      additionalProperties: false
      properties:
        frequency: { type: string, enum: [daily, weekly, monthly, custom] }
        interval: { type: integer, minimum: 1, maximum: 365, default: 1 }
        days_of_week:
          type: array
          uniqueItems: true
          items: { type: string, enum: [monday, tuesday, wednesday, thursday, friday, saturday, sunday] }
        local_time: { type: string, pattern: '^([01]\\d|2[0-3]):[0-5]\\d$' }
        day_of_month: { type: integer, minimum: 1, maximum: 31, description: Required for monthly schedules. }
        rrule: { type: string, maxLength: 500, description: Required for custom schedules. Supports the DAILY, WEEKLY, and MONTHLY RFC 5545 subset. }
    Expiry:
      type: object
      additionalProperties: false
      properties:
        behavior: { type: string, enum: [show_message, hide, redirect], default: show_message }
        message: { type: string, maxLength: 500 }
        redirect_url: { type: string, format: uri }
    Metadata:
      type: object
      maxProperties: 50
      additionalProperties:
        type: [string, number, boolean, 'null']
    Timer:
      type: object
      required: [id, name, type, status, revision, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        type: { type: string, enum: [fixed, duration, recurring, personalized] }
        status: { type: string, enum: [draft, published, archived] }
        revision: { type: integer, minimum: 1 }
        deadline_at: { type: [string, 'null'], format: date-time }
        duration_seconds: { type: [integer, 'null'] }
        remaining_seconds: { type: [integer, 'null'], minimum: 0 }
        timezone: { type: string, default: UTC }
        recurrence:
          oneOf:
            - $ref: '#/components/schemas/Recurrence'
            - type: 'null'
        external_user_id: { type: [string, 'null'] }
        expiry: { $ref: '#/components/schemas/Expiry' }
        metadata: { $ref: '#/components/schemas/Metadata' }
        outputs:
          oneOf:
            - $ref: '#/components/schemas/TimerOutputs'
            - type: 'null'
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    TimerOutputs:
      type: object
      properties:
        public_page_url:
          type: [string, 'null']
          format: uri
          description: Uses /api/c/{timer_id} for countdowns and /api/t/{timer_id} for duration timers.
        website_embed_html: { type: [string, 'null'] }
        email_embed_html: { type: [string, 'null'] }
        json_data_url: { type: string, format: uri }
    TimerStatus:
      type: object
      required: [status, server_time, remaining, ended]
      properties:
        status: { type: string, enum: [scheduled, running, paused, ended] }
        server_time: { type: string, format: date-time }
        target_at: { type: [string, 'null'], format: date-time }
        timezone: { type: string }
        remaining: { $ref: '#/components/schemas/RemainingTime' }
        progress: { type: [number, 'null'], minimum: 0, maximum: 1 }
        ended: { type: boolean }
    RemainingTime:
      type: object
      required: [total_seconds, days, hours, minutes, seconds]
      properties:
        total_seconds: { type: integer, minimum: 0 }
        days: { type: integer, minimum: 0 }
        hours: { type: integer, minimum: 0, maximum: 23 }
        minutes: { type: integer, minimum: 0, maximum: 59 }
        seconds: { type: integer, minimum: 0, maximum: 59 }
    TimerActivity:
      type: object
      additionalProperties: false
      required: [id, event_type, changes, created_at]
      properties:
        id: { type: string, format: uuid }
        event_type:
          $ref: '#/components/schemas/WebhookEventType'
        changes: { type: object }
        created_at: { type: string, format: date-time }
    TimerMetrics:
      type: object
      additionalProperties: false
      required: [views, unique_visitors, activity_events, interactions, conversions]
      properties:
        views: { type: integer, minimum: 0 }
        unique_visitors: { type: integer, minimum: 0 }
        activity_events: { type: integer, minimum: 0 }
        interactions: { type: integer, minimum: 0 }
        conversions: { type: integer, minimum: 0 }
    Usage:
      type: object
      additionalProperties: false
      required: [environment, cycle, metrics]
      properties:
        environment: { type: string, enum: [sandbox, production] }
        cycle:
          type: object
          additionalProperties: false
          required: [id, starts_at, resets_at]
          properties:
            id: { type: string, format: uuid }
            starts_at: { type: string, format: date-time }
            resets_at: { type: string, format: date-time }
        metrics:
          type: array
          items:
            type: object
            additionalProperties: false
            required: [metric, total, limit, remaining, starts_at, resets_at, warning_threshold]
            properties:
              metric: { type: string, enum: [api_requests, new_timers, timer_updates, webhook_deliveries, automation_executions] }
              total: { type: integer, minimum: 0 }
              limit: { oneOf: [{ type: integer, minimum: 0 }, { type: string, const: unlimited }] }
              remaining: { oneOf: [{ type: integer, minimum: 0 }, { type: string, const: unlimited }] }
              starts_at: { type: string, format: date-time }
              resets_at: { type: string, format: date-time }
              warning_threshold: { type: integer, enum: [0, 50, 80, 90, 100] }
    RequestLog:
      type: object
      additionalProperties: false
      required: [id, request_id, environment, method, route, status_code, duration_ms, created_at]
      properties:
        id: { type: string, format: uuid }
        request_id: { type: string, format: uuid }
        api_key_id: { type: [string, 'null'], format: uuid }
        api_key_name: { type: [string, 'null'] }
        api_key_prefix: { type: [string, 'null'] }
        environment: { type: string, enum: [sandbox, production] }
        method: { type: string }
        route: { type: string }
        status_code: { type: integer, minimum: 100, maximum: 599 }
        error_code: { type: [string, 'null'] }
        duration_ms: { type: integer, minimum: 0 }
        created_at: { type: string, format: date-time }
    CreateWebhookDestinationRequest:
      type: object
      required: [name, url]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 100 }
        url: { type: string, format: uri, description: Production destinations must use public HTTPS URLs. }
        description: { type: string, maxLength: 500 }
        enabled: { type: boolean, default: true }
    UpdateWebhookDestinationRequest:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 100 }
        url: { type: string, format: uri }
        description: { type: string, maxLength: 500 }
        enabled: { type: boolean }
    WebhookDestinationCreated:
      type: object
      allOf:
        - $ref: '#/components/schemas/WebhookDestination'
        - type: object
          required: [signing_secret]
          properties:
            signing_secret: { type: string, description: Returned only when the destination is created or its secret is rotated. }
    WebhookDestination:
      type: object
      required: [id, name, url, enabled, signing_secret_prefix, secret_rotated_at, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        url: { type: string, format: uri }
        description: { type: [string, 'null'] }
        enabled: { type: boolean }
        signing_secret_prefix: { type: string }
        secret_rotated_at: { type: string, format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    WebhookRuleRequest:
      type: object
      required: [name, webhook_destination_id, condition]
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 100 }
        webhook_destination_id: { type: string }
        condition:
          type: object
          required: [field, operator, value]
          additionalProperties: false
          properties:
            field: { type: string, enum: [status, remaining_seconds, progress_percent, server_time, action, recurrence_cycle] }
            operator: { type: string, enum: [equals, less_than_or_equal, greater_than_or_equal] }
            value: {}
        delivery: { type: string, enum: [once, repeat], default: once }
        enabled: { type: boolean, default: true }
    UpdateWebhookRuleRequest:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        name: { type: string, minLength: 1, maxLength: 100 }
        webhook_destination_id: { type: string, format: uuid }
        condition:
          type: object
          required: [field, operator, value]
          additionalProperties: false
          properties:
            field: { type: string, enum: [status, remaining_seconds, progress_percent, server_time, action, recurrence_cycle] }
            operator: { type: string, enum: [equals, less_than_or_equal, greater_than_or_equal] }
            value: {}
        delivery: { type: string, enum: [once, repeat] }
        enabled: { type: boolean }
    WebhookRule:
      type: object
      required: [id, timer_id, webhook_destination_id, name, condition, delivery, enabled, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        timer_id: { type: string, format: uuid }
        webhook_destination_id: { type: string, format: uuid }
        destination_name: { type: [string, 'null'] }
        name: { type: string }
        condition:
          type: object
          required: [field, operator, value]
          properties:
            field: { type: string }
            operator: { type: string }
            value: {}
        delivery: { type: string, enum: [once, repeat] }
        enabled: { type: boolean }
        last_matched_at: { type: [string, 'null'], format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    WebhookDelivery:
      type: object
      required: [id, event_id, event_type, status, attempt_count, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        event_id: { type: string, format: uuid }
        timer_id: { type: [string, 'null'], format: uuid }
        event_type:
          $ref: '#/components/schemas/WebhookEventType'
        webhook_destination_id: { type: [string, 'null'], format: uuid }
        destination_name: { type: [string, 'null'] }
        webhook_rule_id: { type: [string, 'null'], format: uuid }
        replay_of_delivery_id: { type: [string, 'null'], format: uuid }
        status: { type: string, enum: [queued, processing, retrying, succeeded, failed, cancelled] }
        attempt_count: { type: integer, minimum: 0 }
        response_status: { type: [integer, 'null'], minimum: 100, maximum: 599 }
        response_excerpt: { type: [string, 'null'] }
        last_error: { type: [string, 'null'] }
        next_retry_at: { type: [string, 'null'], format: date-time }
        completed_at: { type: [string, 'null'], format: date-time }
        attempts:
          type: array
          items:
            type: object
            properties:
              attempt_number: { type: integer }
              request_started_at: { type: string, format: date-time }
              response_status: { type: [integer, 'null'] }
              duration_ms: { type: integer }
              error: { type: [string, 'null'] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    TimezoneList:
      type: object
      required: [data, meta, request_id]
      properties:
        data:
          type: array
          items:
            type: object
            required: [id]
            properties:
              id: { type: string, example: Asia/Kolkata }
        meta:
          type: object
          required: [default, source]
          properties:
            default: { type: string, const: UTC }
            source: { type: string, const: IANA Time Zone Database }
        request_id: { type: string, format: uuid }
    SuccessData:
      type: object
      required: [data, request_id]
      properties:
        data: {}
        request_id: { type: string, format: uuid }
    PaginatedData:
      type: object
      required: [data, page, request_id]
      properties:
        data: { type: array, items: {} }
        page:
          type: object
          required: [has_more]
          properties:
            next_cursor: { type: [string, 'null'] }
            has_more: { type: boolean }
        request_id: { type: string, format: uuid }
    Error:
      type: object
      required: [error, request_id]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string, enum: [unauthorized, invalid_api_key, forbidden, suspended, not_found, invalid_timer_id, missing_field, invalid_timezone, validation_error, idempotency_conflict, revision_conflict, rate_limited, quota_exhausted, internal_error] }
            message: { type: string }
            details: {}
        request_id: { type: string, format: uuid }
  responses:
    Timer:
      description: Timer response
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/Timer' }, request_id: { type: string, format: uuid } }
    TimerList:
      description: Paginated timer list
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/PaginatedData'
              - type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/Timer' } }
    TimerStatus:
      description: Timer status
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/TimerStatus' }, request_id: { type: string, format: uuid } }
    TimerOutputs:
      description: Timer display options
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/TimerOutputs' }, request_id: { type: string, format: uuid } }
    TimerActivityList:
      description: Paginated timer activity
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/PaginatedData'
              - type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/TimerActivity' } }
    TimerMetrics:
      description: Timer metrics
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/TimerMetrics' }, request_id: { type: string, format: uuid } }
    Usage:
      description: Current billing-cycle usage
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/Usage' }, request_id: { type: string, format: uuid } }
    RequestLogList:
      description: Paginated API request logs
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/PaginatedData'
              - type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/RequestLog' } }
    Timezones:
      description: Accepted timezone identifiers
      content:
        application/json:
          schema: { $ref: '#/components/schemas/TimezoneList' }
    WebhookDestinationCreated:
      description: Webhook destination with its one-time signing secret
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/WebhookDestinationCreated' }, request_id: { type: string, format: uuid } }
    WebhookDestination:
      description: Webhook destination
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/WebhookDestination' }, request_id: { type: string, format: uuid } }
    WebhookDestinationList:
      description: Paginated webhook destinations
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/PaginatedData'
              - type: object
                properties: { data: { type: array, items: { $ref: '#/components/schemas/WebhookDestination' } } }
    WebhookRule:
      description: Webhook rule
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/WebhookRule' }, request_id: { type: string, format: uuid } }
    WebhookRuleList:
      description: Paginated webhook rules
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/PaginatedData'
              - type: object
                properties: { data: { type: array, items: { $ref: '#/components/schemas/WebhookRule' } } }
    WebhookDelivery:
      description: Webhook delivery with attempts
      content:
        application/json:
          schema:
            type: object
            required: [data, request_id]
            properties: { data: { $ref: '#/components/schemas/WebhookDelivery' }, request_id: { type: string, format: uuid } }
    WebhookDeliveryList:
      description: Paginated webhook deliveries
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/PaginatedData'
              - type: object
                properties: { data: { type: array, items: { $ref: '#/components/schemas/WebhookDelivery' } } }
    Deleted:
      description: Deleted timer response
      content:
        application/json:
          schema: { $ref: '#/components/schemas/SuccessData' }
    Data:
      description: Data response
      content:
        application/json:
          schema: { $ref: '#/components/schemas/SuccessData' }
    PaginatedData:
      description: Paginated data response
      content:
        application/json:
          schema: { $ref: '#/components/schemas/PaginatedData' }
    Error:
      description: Error response
      headers:
        X-Request-Id:
          description: Unique request identifier for support and log correlation.
          schema: { type: string, format: uuid }
        RateLimit-Limit:
          description: Maximum accepted requests in the current one-minute window when authenticated.
          schema: { type: integer }
        RateLimit-Remaining:
          description: Requests remaining in the current one-minute window.
          schema: { type: integer }
        RateLimit-Reset:
          description: Unix timestamp when the current rate-limit window resets.
          schema: { type: integer }
        Retry-After:
          description: Seconds to wait after a rate or monthly quota limit is reached.
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
