visdom

GitHub

A flexible tool for creating, organizing, and sharing visualizations of live, rich data. Supports Torch and Numpy https://visdom.dev

10,266 stars Python
Raw JSON API

visdom Specification

Located in openapi.yaml on branch HEAD

3.x (YAML) YAML 68.8 KB
Raw YAML Specification
# SPDX-License-Identifier: Apache-2.0
#
# https://www.apache.org/licenses/LICENSE-2.0
openapi: 3.1.0
info:
  title: Visdom API
  description: >
    Visdom is a flexible tool for creating, organizing, and sharing
    visualizations of live, rich data. It supports Plotly-based charts,
    images, text, audio, video, embeddings, and more. This specification
    documents the HTTP REST endpoints exposed by the Visdom Tornado server.


    All POST endpoints accept JSON request bodies (`application/json`).
    When authentication is enabled, most endpoints require a valid session
    cookie (set via the login endpoint at `POST /`). Unauthenticated
    requests currently receive a `400` status code.


    The server also supports WebSocket connections at `/socket` (read-only
    subscriber) and `/vis_socket` (write-capable visualization source), as
    well as HTTP polling fallbacks at `/socket_wrap` and `/vis_socket_wrap`.
  version: 0.2.4
  license:
    name: Apache-2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
  contact:
    name: Visdom Authors
    url: https://github.com/fossasia/visdom

servers:
  - url: "{protocol}://{host}:{port}{base_url}"
    description: Visdom server
    variables:
      protocol:
        default: http
        enum: [http, https]
      host:
        default: "0.0.0.0"
        description: Server hostname or IP address
      port:
        default: "8097"
        description: Server port
      base_url:
        default: ""
        description: Optional base URL prefix (e.g., "/visdom")

tags:
  - name: Visualization
    description: Create and update visualization windows (panes)
  - name: Window Management
    description: Query, close, and retrieve window data
  - name: Environment
    description: Manage environments (create, delete, fork, list, save, compare)
  - name: Experiments
    description: Track experiment metadata (hyper-parameters, metrics, tags)
  - name: Authentication
    description: Login and session management
  - name: Socket Polling
    description: HTTP polling fallbacks for WebSocket connections
  - name: System
    description: Health check and diagnostics

security:
  - cookieAuth: []
  - {}

paths:
  /events:
    post:
      operationId: createWindow
      tags: [Visualization]
      summary: Create a new visualization window
      description: >
        Creates a new visualization pane in the specified environment. The
        window type is determined by `data[].type`. If `win` is omitted, a
        new window ID is generated automatically. If `win` is provided and
        already exists, the existing window is replaced.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWindowRequest"
      responses:
        "200":
          description: Window created successfully. Returns the window ID as plain text.
          content:
            text/plain:
              schema:
                type: string
                description: The ID of the created window.
                examples:
                  - "window_38fa9db8e4f5b2"
        "400":
          description: Authentication required but not provided (when login is enabled).

  /update:
    post:
      operationId: updateWindow
      tags: [Visualization]
      summary: Update an existing visualization window
      description: >
        Updates the data, options, or layout of an existing window. Supports
        appending data to scatter/line plots, updating heatmaps (append/prepend
        rows or columns), appending text, managing image history, updating
        embeddings selections, adding/removing traces, and modifying plot
        options or layout properties. If `append` is true and the window does
        not exist, a new window is created.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWindowRequest"
      responses:
        "200":
          description: >
            Window updated successfully. Returns the window ID, or an error
            message string if the window does not exist or the update is not
            supported for the window type.
          content:
            text/plain:
              schema:
                type: string
              examples:
                success:
                  value: "window_38fa9db8e4f5b2"
                not_found:
                  value: "win does not exist"
                unsupported:
                  value: "win is not scatter, heatmap, custom, image_history, embeddings, or text; was bar"
        "400":
          description: Authentication required but not provided (when login is enabled).

  /close:
    post:
      operationId: closeWindow
      tags: [Window Management]
      summary: Close one or all windows in an environment
      description: >
        Closes (deletes) a specific window or all windows in the given
        environment. The close event is broadcast to all subscribed clients.
        If `win` is omitted or null, all windows in the environment are
        closed.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                win:
                  type:
                    - string
                    - "null"
                  description: >
                    Window ID to close. If null or omitted, all windows in
                    the environment are closed.
                eid:
                  type:
                    - string
                    - "null"
                  description: >
                    Environment ID. Defaults to `"main"` if omitted.
      responses:
        "200":
          description: >
            Window(s) closed. A close broadcast is sent to all subscribed
            clients. No body is returned.
        "400":
          description: Authentication required but not provided (when login is enabled).

  /win_exists:
    post:
      operationId: checkWindowExists
      tags: [Window Management]
      summary: Check if a window exists in an environment
      description: >
        Returns `"true"` or `"false"` (as a plain text string) indicating
        whether the specified window exists in the given environment.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [win]
              properties:
                win:
                  type: string
                  description: Window ID to check.
                eid:
                  type:
                    - string
                    - "null"
                  description: >
                    Environment ID. Defaults to `"main"` if omitted.
      responses:
        "200":
          description: Returns `"true"` or `"false"` as plain text.
          content:
            text/plain:
              schema:
                type: string
                enum: ["true", "false"]
        "400":
          description: Authentication required but not provided (when login is enabled).

  /win_data:
    post:
      operationId: getOrSetWindowData
      tags: [Window Management]
      summary: Get or set raw window/environment data
      description: >
        When `data` is absent, retrieves the raw JSON data for a specific
        window (if `win` is provided) or all windows in an environment (if
        `win` is null). When `data` is present, sets (overwrites) the
        window or environment data.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/GetWindowDataRequest"
                - $ref: "#/components/schemas/SetWindowDataRequest"
      responses:
        "200":
          description: >
            For GET operations: returns JSON data of the window or all
            windows. For SET operations: no body, but broadcasts an
            environment update.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/WindowObject"
                  - type: object
                    additionalProperties:
                      $ref: "#/components/schemas/WindowObject"
                    description: Map of window IDs to window objects (when win is null).
        "400":
          description: Authentication required but not provided (when login is enabled).
        "500":
          description: >
            Server error. Occurs if the requested window does not exist
            (unhandled assertion error).

  /save:
    post:
      operationId: saveEnvironments
      tags: [Environment]
      summary: Persist environments to disk
      description: >
        Saves the specified environments to JSON files on disk. Environment
        IDs that do not exist in the current server state are silently
        dropped. Returns the list of environment IDs that were actually
        saved.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [data]
              properties:
                data:
                  type: array
                  items:
                    type: string
                  description: List of environment IDs to save.
                  examples:
                    - ["main", "experiment_1"]
      responses:
        "200":
          description: Returns JSON array of successfully saved environment IDs.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
                examples:
                  - ["main", "experiment_1"]
        "400":
          description: Authentication required but not provided (when login is enabled).

  /delete_env:
    post:
      operationId: deleteEnvironment
      tags: [Environment]
      summary: Delete an environment
      description: >
        Permanently deletes an environment and its associated JSON file on
        disk. The `"main"` environment cannot be deleted (the request is
        silently ignored). After deletion, an `env_update` broadcast is
        sent to all subscribed clients. Handles both standard and
        hash-named environment files.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                eid:
                  type: string
                  description: >
                    Environment ID to delete. Forward/backward slashes are
                    replaced with underscores.
      responses:
        "200":
          description: >
            Environment deleted (or silently ignored if `"main"`). An
            `env_update` broadcast is sent. No body returned.
        "400":
          description: Authentication required but not provided (when login is enabled).

  /env_state:
    post:
      operationId: listEnvironments
      tags: [Environment]
      summary: List all available environments
      description: >
        Returns a JSON array of all environment IDs currently loaded in
        the server state. The request body is ignored but must be valid
        JSON (an empty object `{}` is sufficient).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Request body is ignored. Send an empty object.
      responses:
        "200":
          description: JSON array of environment IDs.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
                examples:
                  - ["main", "experiment_1", "experiment_2"]
        "400":
          description: Authentication required but not provided (when login is enabled).

  /fork_env:
    post:
      operationId: forkEnvironment
      tags: [Environment]
      summary: Clone an environment with a new ID
      description: >
        Deep-copies the source environment (all windows and metadata) into
        a new environment with the specified ID. The forked environment is
        immediately saved to disk and an `env_update` broadcast is sent.
        Fails with an assertion error if the source environment does not
        exist.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [prev_eid, eid]
              properties:
                prev_eid:
                  type: string
                  description: Source environment ID to clone from.
                eid:
                  type: string
                  description: New environment ID for the clone.
      responses:
        "200":
          description: Returns the new environment ID as plain text.
          content:
            text/plain:
              schema:
                type: string
        "400":
          description: Authentication required but not provided (when login is enabled).
        "500":
          description: Server error. Occurs if the source environment does not exist (unhandled assertion error).

  /experiments/log:
    post:
      operationId: logExperiment
      tags: [Experiments]
      summary: Record experiment metadata for an environment
      description: >
        Attaches experiment metadata (hyper-parameters, metric observations,
        and tags) to an environment, stored under the environment's
        `experiment` key and persisted through the server's data store. The
        `action` field selects the operation:


        - `log` (default): create or update the experiment. Repeated calls
        merge new `params`/`tags` and overwrite `name`/`description`.


        - `metrics`: append one or more `{name: value}` observations at an
        optional `step`, creating the experiment if it does not exist yet.


        - `finish`: mark the experiment terminal (`finished` or `failed`). An
        experiment that is already terminal cannot be finished again.


        Once an experiment is terminal, every action is rejected with `409`, so
        neither a finished run's recorded data nor its final status can change
        after the fact.
        Every action writes, so the endpoint is rejected when the server is
        running in readonly mode.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                eid:
                  type: string
                  description: Target environment ID. Defaults to `main`.
                action:
                  type: string
                  enum: [log, metrics, finish]
                  default: log
                  description: Operation to perform.
                name:
                  type: string
                  description: Display name (action `log`). Defaults to the eid.
                description:
                  type: string
                  description: Free-form description (action `log`).
                params:
                  type: object
                  additionalProperties: true
                  description: 'Hyper-parameters as `{name: value}` (action `log`).'
                tags:
                  type: object
                  additionalProperties: true
                  description: 'Free-form tags as `{name: value}` (action `log`).'
                metrics:
                  type: object
                  additionalProperties:
                    type: number
                  description: >
                    Metric observations as `{name: value}` (action `metrics`).
                    Must be a non-empty object.
                step:
                  type: integer
                  description: Optional training step for the metrics (action `metrics`).
                status:
                  type: string
                  enum: [finished, failed]
                  default: finished
                  description: Terminal status (action `finish`).
      responses:
        "200":
          description: The stored experiment as JSON.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Experiment"
        "400":
          description: >
            Invalid request — unknown `action`, non-object `params`/`tags`/`metrics`,
            empty `metrics`, or a non-terminal `finish` status. Also returned when
            authentication is required but not provided.
        "403":
          description: >
            Experiment logging is disabled because the server is in readonly
            mode.
        "404":
          description: A `finish` was requested for an env that has no experiment.
        "409":
          description: >
            A write was attempted on a terminal (finished/failed) experiment —
            a `log`/`metrics` call, or a second `finish`.

  /experiments/search:
    post:
      operationId: searchExperiments
      tags: [Experiments]
      summary: Search experiments across all environments
      description: >
        Returns the experiments matching `query`, sorted and paged. The query is
        a small readable syntax of comparisons (`<`, `<=`, `>`, `>=`, `=`, `!=`,
        `contains`) combined with `AND`/`OR` and parentheses, for example
        `lr < 0.01 AND (acc > 0.9 OR status = finished)`. Omitting `query`
        matches every experiment.


        Comparison names are the experiment's built-in fields (`name`,
        `status`, `created_at`, ...), its params, its metrics and its tags. A
        name may be given bare (`acc`) or namespaced when it is ambiguous
        (`metric.acc`, `param.lr`, `tag.owner`); metrics compare on their latest
        logged value. A name no experiment has simply matches nothing.


        Queries are parsed into a predicate and evaluated in Python — never
        eval'd and never turned into SQL — so a hostile query is rejected as a
        parse error rather than executed.


        Experiments are read back through the server's data store, so a server
        running with no persistence path configured has nothing to search.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                query:
                  type: string
                  nullable: true
                  description: >
                    Filter expression. Omitted, null or blank matches every
                    experiment.
                sort_by:
                  type: string
                  nullable: true
                  default: created_at
                  description: >
                    Field to sort by — any name a query can compare on.
                    Experiments lacking the field sort last in both directions.
                descending:
                  type: boolean
                  default: true
                  description: Sort direction; newest/highest first by default.
                limit:
                  type: integer
                  nullable: true
                  minimum: 0
                  default: 100
                  description: >
                    Maximum number of experiments in the reply. `0` returns just
                    the count; `null` returns all matches.
                offset:
                  type: integer
                  minimum: 0
                  default: 0
                  description: Number of matches to skip before the returned page.
      responses:
        "200":
          description: The matching page of experiments.
          content:
            application/json:
              schema:
                type: object
                required: [experiments, total, limit, offset, query]
                properties:
                  experiments:
                    type: array
                    description: One page of matching experiments.
                    items:
                      $ref: "#/components/schemas/Experiment"
                  total:
                    type: integer
                    description: >
                      Total matches for the query, ignoring `limit`/`offset`.
                  limit:
                    type: integer
                    nullable: true
                    description: The limit applied to this reply.
                  offset:
                    type: integer
                    description: The offset applied to this reply.
                  query:
                    type: string
                    description: The query used, `""` if none was given.
        "400":
          description: >
            Invalid request — a body that is not a JSON object, malformed
            `query` syntax, a non-string `query`/`sort_by`, or a `limit`/`offset`
            that is not a non-negative integer. Also returned when
            authentication is required but not provided.

  /experiments/compare:
    post:
      operationId: compareExperiments
      tags: [Experiments]
      summary: Compare experiments field by field
      description: >
        Lines several experiments up beside each other and reports, per section
        (params, metrics and tags), which fields they agree on and which they do
        not — the short list of knobs that actually changed between runs.


        The runs are named by `env_ids` and compared in the order given; every id
        must have an experiment, otherwise `404`.


        Finding the runs is `/experiments/search`'s job: it answers "which runs
        match?", this answers "how do these runs differ?". To compare a query's
        matches, search first and pass the resulting ids on.


        Metrics are a time series, so a comparison uses each metric's latest
        observation — the same value a search compares on.


        Experiments are read back through the server's data store, so a server
        running with no persistence path configured has nothing to compare.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [env_ids]
              properties:
                env_ids:
                  type: array
                  minItems: 1
                  description: >
                    Environments to compare, in the order given. Duplicate ids
                    collapse to one.
                  items:
                    type: string
      responses:
        "200":
          description: The comparison of the selected experiments.
          content:
            application/json:
              schema:
                type: object
                required: [env_ids, experiments, params, metrics, tags]
                properties:
                  env_ids:
                    type: array
                    description: The runs compared, in the order compared.
                    items:
                      type: string
                  experiments:
                    type: array
                    description: The compared experiments, in full.
                    items:
                      $ref: "#/components/schemas/Experiment"
                  params:
                    $ref: "#/components/schemas/ExperimentComparisonSection"
                  metrics:
                    $ref: "#/components/schemas/ExperimentComparisonSection"
                  tags:
                    $ref: "#/components/schemas/ExperimentComparisonSection"
        "400":
          description: >
            Invalid request — the body is not a JSON object, or `env_ids` is
            missing, empty, not a list, or holds something other than strings.
            Also returned when authentication is required but not provided.
        "404":
          description: One or more of the given `env_ids` has no experiment.

  /experiments/suggest:
    post:
      operationId: suggestExperiment
      tags: [Experiments]
      summary: Suggest parameters for the next run (reserved)
      description: >
        Reserved endpoint for hyper-parameter suggestion. Choosing the next set
        of parameters to try is a search-strategy problem (Optuna-backed) that
        lands in a later release, so this is currently a stub: it accepts the
        request and replies `501 Not Implemented` with a JSON body carrying a
        `suggestion: null` placeholder, rather than a made-up suggestion. The
        route, the `suggest_experiment` client method and this documentation are
        in place so the strategy can be wired in later without changing the
        surface.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                eid:
                  type: string
                  description: Target environment ID. Defaults to `main`.
                params:
                  type: object
                  additionalProperties: true
                  description: >
                    The search space to suggest over, as `{name: spec}`. Ignored
                    by the stub; reserved for the eventual strategy.
      responses:
        "501":
          description: >
            Not implemented — the suggestion strategy is reserved for a later
            release. The body is a JSON stub.
          content:
            application/json:
              schema:
                type: object
                required: [status, detail, suggestion]
                properties:
                  status:
                    type: string
                    example: not_implemented
                  detail:
                    type: string
                  suggestion:
                    nullable: true
                    description: Always `null` while the endpoint is a stub.
        "400":
          description: >
            Invalid request — the body is present but is not valid JSON, or is
            not a JSON object. Also returned when authentication is required
            but not provided.

  /upload_env:
    post:
      operationId: uploadEnvironment
      tags: [Environment]
      summary: Upload a Visdom JSON file as a new environment
      description: >
        Accepts a Visdom environment-export JSON file via multipart form upload
        and loads it as a new environment named `uploaded_<name>_<id>`. The
        uploaded JSON must be an object containing `jsons` and `reload` keys.
        The new environment is saved to disk (when a persistence path is
        configured) and an `env_update` broadcast is sent. Maximum file size is
        100 MB. Rejected when the server is running in readonly mode.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: >
                    A Visdom environment-export JSON file (object with `jsons`
                    and `reload` keys).
      responses:
        "200":
          description: Environment created from the uploaded file.
          content:
            application/json:
              schema:
                type: object
                required: [success, eid, message]
                properties:
                  success:
                    type: boolean
                  eid:
                    type: string
                    description: ID of the newly created environment.
                  message:
                    type: string
              examples:
                uploaded:
                  value:
                    success: true
                    eid: uploaded_myrun_1a2b3c4d
                    message: "Dashboard loaded successfully as 'uploaded_myrun_1a2b3c4d'"
        "400":
          description: >
            No file uploaded, invalid JSON, not a valid Visdom JSON, or
            authentication required but not provided (when login is enabled).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadErrorResponse"
        "403":
          description: Uploads are disabled because the server is in readonly mode.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadErrorResponse"
        "413":
          description: Uploaded file exceeds the 100 MB size limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadErrorResponse"

  /env/{eid}:
    get:
      operationId: renderEnvironmentPage
      tags: [Environment]
      summary: Render the environment UI page
      description: >
        Returns the Visdom HTML page for the specified environment. This is
        the main web UI entry point for a specific environment.
      parameters:
        - name: eid
          in: path
          required: true
          schema:
            type: string
          description: Environment ID.
      responses:
        "200":
          description: HTML page for the environment.
          content:
            text/html:
              schema:
                type: string
        "400":
          description: Authentication required but not provided (when login is enabled).
    post:
      operationId: loadEnvironment
      tags: [Environment]
      summary: Load environment data to a client socket or create a new environment
      description: >
        If `sid` is provided and matches a subscribed socket, the
        environment's windows and layout are sent to that specific socket.
        If `eid` is provided and does not exist, a new empty environment
        is created and an `env_update` broadcast is sent.
      parameters:
        - name: eid
          in: path
          required: true
          schema:
            type: string
          description: Environment ID (from URL path).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                sid:
                  type: string
                  description: >
                    Socket ID of the subscriber to load the environment to.
                eid:
                  type: string
                  description: >
                    Environment ID to create if it does not exist.
      responses:
        "200":
          description: >
            Environment loaded to the specified socket, or new environment
            created. No body returned.
        "400":
          description: Authentication required but not provided (when login is enabled).

  /compare/{eids}:
    get:
      operationId: renderComparePage
      tags: [Environment]
      summary: Render the comparison view for multiple environments
      description: >
        Returns the Visdom HTML page configured for comparing multiple
        environments side by side. Windows with matching titles across
        environments are combined. Plots show traces from each environment
        with prefixed legend names. Images are displayed in a comparison
        grid.
      parameters:
        - name: eids
          in: path
          required: true
          schema:
            type: string
          description: >
            Environment IDs separated by `+` (e.g., `env1+env2+env3`).
      responses:
        "200":
          description: HTML page for environment comparison.
          content:
            text/html:
              schema:
                type: string
        "400":
          description: Authentication required but not provided (when login is enabled).
    post:
      operationId: loadComparisonData
      tags: [Environment]
      summary: Load comparison data for multiple environments to a client socket
      description: >
        Sends comparison window data to the specified subscriber socket.
        Only windows of type `"plot"` or `"image"` with matching, non-empty
        titles across at least two environments are included. Plot traces
        are prefixed with the environment index. Images are shown side by
        side with captioned labels.
      parameters:
        - name: eids
          in: path
          required: true
          schema:
            type: string
          description: >
            Environment IDs separated by `+` (e.g., `env1+env2+env3`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [sid]
              properties:
                sid:
                  type: string
                  description: Socket ID of the subscriber to load comparison data to.
      responses:
        "200":
          description: >
            Comparison data sent to the specified socket. No body returned.
        "400":
          description: Authentication required but not provided (when login is enabled).

  /user/{path}:
    get:
      operationId: getUserSettings
      tags: [System]
      summary: Retrieve user settings files
      description: >
        Currently only supports `style.css` as the path parameter. Returns
        the user's custom CSS stylesheet.
      security: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
            enum: ["style.css"]
          description: Settings file path. Currently only `"style.css"` is supported.
      responses:
        "200":
          description: User CSS content.
          content:
            text/css:
              schema:
                type: string

  /health:
    get:
      operationId: healthCheck
      tags: [System]
      summary: Health check endpoint
      description: >
        Returns a simple JSON object indicating the server is running.
        This endpoint does not require authentication.
      security: []
      responses:
        "200":
          description: Server is healthy.
          content:
            application/json:
              schema:
                type: object
                required: [status]
                properties:
                  status:
                    type: string
                    enum: ["ok"]
              examples:
                healthy:
                  value:
                    status: ok
        "400":
          description: Bad request.

  /error/{text}:
    get:
      operationId: triggerError
      tags: [System]
      summary: Test endpoint that raises an error
      description: >
        Raises an exception with the provided text message. Used for
        testing error handling. This endpoint does not require
        authentication.
      security: []
      parameters:
        - name: text
          in: path
          required: true
          schema:
            type: string
          description: Error message text. Defaults to `"test error"` if empty.
      responses:
        "500":
          description: Server error with the specified message (endpoint always raises).

  /:
    get:
      operationId: renderMainPage
      tags: [Authentication]
      summary: Render the main Visdom page or login page
      description: >
        If authentication is disabled or the user is authenticated, renders
        the main Visdom UI (`index.html`). If authentication is enabled
        and the user is not authenticated, renders the login page
        (`login.html`).
      security: []
      responses:
        "200":
          description: HTML page (index.html or login.html).
          content:
            text/html:
              schema:
                type: string
        "400":
          description: Bad request.
    post:
      operationId: login
      tags: [Authentication]
      summary: Authenticate and create a session
      description: >
        Validates the username and password against the server's configured
        credentials. The password is SHA-256 hashed on the server side
        (the client should also pre-hash once before sending). On success,
        sets a secure cookie (`user_password`). On failure, returns 400.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, password]
              properties:
                username:
                  type: string
                  description: Username for authentication.
                password:
                  type: string
                  description: >
                    Password string (typically pre-hashed once on the
                    client side with SHA-256, then hashed again on the
                    server).
      responses:
        "200":
          description: >
            Authentication successful. A `user_password` secure cookie is
            set. No body returned.
          headers:
            Set-Cookie:
              schema:
                type: string
              description: Secure session cookie.
        "400":
          description: Invalid credentials.

  /socket:
    get:
      operationId: subscribeSocket
      tags: [Socket Polling]
      summary: WebSocket endpoint for subscribing to visualization updates
      description: >
        WebSocket connection for read-only subscribers. Clients connect via
        the WebSocket protocol (HTTP Upgrade). On connection, the server
        sends a `register` message with the assigned socket ID, readonly
        status, and current environment list. The server then streams
        window updates, close events, environment updates, and layout
        changes. Clients can send commands: `close`, `save`, `save_all`,
        `delete_env`, `save_layouts`, `forward_to_vis`,
        `layout_item_update`, `pop_embeddings_pane`.


        **Note:** This is a WebSocket endpoint. OpenAPI does not natively
        support WebSocket, so this is documented as a GET for reference.
      responses:
        "200":
          description: Not applicable. This endpoint upgrades to WebSocket (101).
        "101":
          description: >
            Switching Protocols. WebSocket connection established. Server
            sends a JSON register message with `command`, `data` (socket
            ID), `readonly`, and `envList` fields. If authentication is
            enabled and the user is not authenticated, the connection is
            accepted then immediately closed.

  /vis_socket:
    get:
      operationId: visSourceSocket
      tags: [Socket Polling]
      summary: WebSocket endpoint for visualization data sources
      description: >
        WebSocket connection for visualization sources with write access.
        Clients connect via the WebSocket protocol (HTTP Upgrade). On
        connection, the server sends an `alive` message. Sources can
        push visualization data and send commands: `echo`, `close`,
        `save`, `save_all`, `delete_env`, `save_layouts`,
        `forward_to_vis`, `layout_item_update`, `pop_embeddings_pane`.


        **Note:** This is a WebSocket endpoint. OpenAPI does not natively
        support WebSocket, so this is documented as a GET for reference.
      responses:
        "200":
          description: Not applicable. This endpoint upgrades to WebSocket (101).
        "101":
          description: >
            Switching Protocols. WebSocket connection established. Server
            sends a JSON alive message with `{"command": "alive", "data": "vis_alive"}`.
            If authentication is enabled and the user is not authenticated,
            the connection is accepted then immediately closed.

  /socket_wrap:
    get:
      operationId: createSocketWrapper
      tags: [Socket Polling]
      summary: Create a new polling-based subscriber socket
      description: >
        Creates a new subscriber socket wrapper for HTTP polling. Returns
        the socket ID that must be used in subsequent POST requests to
        send/receive messages. This is a fallback for environments that
        do not support WebSocket connections.
      responses:
        "200":
          description: New socket wrapper created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocketWrapCreateResponse"
        "400":
          description: Authentication required but not provided (when login is enabled).
    post:
      operationId: pollSubscriberSocket
      tags: [Socket Polling]
      summary: Send or query messages on a polling-based subscriber socket
      description: >
        Interact with a polling-based subscriber socket. Use
        `message_type: "query"` to retrieve pending messages, or
        `message_type: "send"` to send a message (command) to the server.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SocketWrapRequest"
      responses:
        "200":
          description: Operation result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocketWrapResponse"

  /vis_socket_wrap:
    post:
      operationId: pollVisSourceSocket
      tags: [Socket Polling]
      summary: Send or query messages on a polling-based visualization source socket
      description: >
        Interact with a polling-based visualization source socket (write
        access). Use `message_type: "query"` to retrieve pending messages,
        or `message_type: "send"` to send a message. If `sid` is null or
        omitted, a new visualization source socket is created and its ID
        is returned.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SocketWrapRequest"
      responses:
        "200":
          description: Operation result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SocketWrapResponse"

components:
  securitySchemes:
    cookieAuth:
      type: apiKey
      in: cookie
      name: user_password
      description: >
        Session cookie set by the `POST /` login endpoint. Required when
        the server is started with `--enable_login`.

  schemas:

    ExperimentComparisonSection:
      type: object
      description: >
        One section (params, metrics or tags) of an experiment comparison,
        diffed across the compared runs.
      required: [fields, shared, differing, values, groups]
      properties:
        fields:
          type: array
          description: Every field name any compared run has, sorted.
          items:
            type: string
        shared:
          type: object
          additionalProperties: true
          description: >
            The fields every compared run carries with the same value, as
            `{name: value}`.
        differing:
          type: array
          description: >
            The remaining fields — those whose value varies between runs, or
            that some run is missing.
          items:
            type: string
        values:
          type: object
          additionalProperties:
            type: object
            additionalProperties: true
          description: >
            Per-field, per-run values as `{field: {env_id: value}}`. A run that
            never logged the field is omitted from that field's map.
        groups:
          type: object
          description: >
            Per field, the runs clustered by the value they used — the finer
            question "which runs agree?", where `shared`/`differing` answer only
            "do they all?". With three runs on two learning rates, the two that
            match share a group. A field appears in `shared` exactly when its
            groups are a single cluster holding every compared run, so the two
            never disagree. Groups are ordered by first appearance, and a run
            that never logged the field is in no group.
          additionalProperties:
            type: array
            items:
              type: object
              required: [value, env_ids]
              properties:
                value:
                  description: The value these runs share.
                env_ids:
                  type: array
                  description: The runs that used it, in compared order.
                  items:
                    type: string

    Experiment:
      type: object
      description: Experiment metadata attached to an environment.
      properties:
        env_id:
          type: string
          description: Environment the experiment belongs to.
        name:
          type: string
          description: Display name. Defaults to the env_id.
        description:
          type: string
        status:
          type: string
          enum: [running, finished, failed]
          description: Lifecycle state. New experiments start `running`.
        created_at:
          type: number
          description: Unix timestamp when the experiment was created.
        finished_at:
          type: [number, "null"]
          description: Unix timestamp when finished, or `null` while running.
        params:
          type: array
          description: Hyper-parameters, keyed by name.
          items:
            type: object
            properties:
              key:
                type: string
              value: {}
              dtype:
                type: string
                enum: [bool, int, float, str]
                description: Inferred type, so a stored value can be cast back.
        metrics:
          type: array
          description: Metric observations, appended over time.
          items:
            type: object
            properties:
              key:
                type: string
              value:
                type: number
              step:
                type: [integer, "null"]
              timestamp:
                type: number
        tags:
          type: array
          description: Free-form key/value labels.
          items:
            type: object
            properties:
              key:
                type: string
              value:
                type: string

    UploadErrorResponse:
      type: object
      description: Error response returned by the /upload_env endpoint.
      properties:
        success:
          type: boolean
          description: Always `false` for error responses.
        error:
          type: string
          description: Human-readable description of what went wrong.

    CreateWindowRequest:
      type: object
      required: [data]
      description: >
        Request body for creating a new visualization window. The `data`
        array defines the content and type of visualization. The server
        determines the window type from `data[0].type`.
      properties:
        data:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/WindowData"
          description: >
            Array of data objects. For plot types (scatter, line, bar, etc.),
            each element is a Plotly trace object. For visdom-native types
            (image, text, properties, network, embeddings), a single element
            with a `content` field is used.
        layout:
          $ref: "#/components/schemas/PlotlyLayout"
        opts:
          $ref: "#/components/schemas/WindowOptions"
        win:
          type:
            - string
            - "null"
          description: >
            Window ID. If null or omitted, a new unique ID is
            auto-generated.
        eid:
          type:
            - string
            - "null"
          description: >
            Environment ID. Defaults to `"main"` if omitted.

    UpdateWindowRequest:
      type: object
      required: [win]
      description: Request body for updating an existing visualization window.
      properties:
        win:
          type: string
          description: Window ID to update.
        eid:
          type:
            - string
            - "null"
          description: >
            Environment ID. Defaults to `"main"` if omitted.
        data:
          oneOf:
            - type: array
              items:
                $ref: "#/components/schemas/WindowData"
              description: >
                Array of data objects for plot updates.
            - type: object
              description: >
                Object data for embeddings updates.
              properties:
                update_type:
                  type: string
                  enum: [EntitySelected, RegionSelected]
                  description: Type of embeddings update.
                selected:
                  type:
                    - integer
                    - "null"
                  description: >
                    Selected entity index (for EntitySelected) or null.
                points:
                  type: array
                  items:
                    type: array
                    items:
                      type: number
                  description: >
                    Array of [x, y] points (for RegionSelected).
        name:
          type:
            - string
            - "null"
          description: >
            Trace name to update (for scatter/line plots). When specified
            with `delete: true`, deletes that trace.
        append:
          type: boolean
          description: >
            If true, append data to existing traces instead of replacing.
            If the window does not exist and `append` is true, a new
            window is created.
        delete:
          type: boolean
          description: >
            If true, delete the trace specified by `name`.
        updateDir:
          type: string
          enum: [replace, appendRow, prependRow, appendColumn, prependColumn]
          description: >
            Direction for heatmap updates. `replace` overwrites the entire
            heatmap. Other values append or prepend rows/columns.
        opts:
          $ref: "#/components/schemas/WindowOptions"
        layout:
          $ref: "#/components/schemas/PlotlyLayout"

    GetWindowDataRequest:
      type: object
      description: Request to retrieve window or environment data.
      properties:
        eid:
          type:
            - string
            - "null"
          description: >
            Environment ID. Defaults to `"main"` if omitted.
        win:
          type:
            - string
            - "null"
          description: >
            Window ID. If null, returns all windows in the environment.

    SetWindowDataRequest:
      type: object
      required: [data]
      description: Request to overwrite window or environment data.
      properties:
        eid:
          type:
            - string
            - "null"
          description: >
            Environment ID. Defaults to `"main"` if omitted.
        win:
          type:
            - string
            - "null"
          description: >
            Window ID. If null, replaces all windows in the environment.
        data:
          type: string
          description: >
            JSON-encoded string of the window object or the complete
            environment jsons map (when win is null).

    SocketWrapRequest:
      type: object
      description: Request body for socket polling operations.
      properties:
        message_type:
          type: string
          enum: [query, send]
          description: >
            `"query"` to retrieve pending messages; `"send"` to dispatch
            a message.
        sid:
          type:
            - string
            - "null"
          description: >
            Socket ID from a previous create call. If null on
            `/vis_socket_wrap`, creates a new visualization source socket.
        message:
          type: string
          description: >
            JSON-encoded message to send (required when
            `message_type` is `"send"`).

    SocketWrapCreateResponse:
      type: object
      properties:
        success:
          type: boolean
          const: true
        sid:
          type: string
          description: Socket ID for subsequent polling requests.
      required: [success, sid]

    SocketWrapResponse:
      type: object
      description: >
        Response from a socket polling operation. The exact shape depends
        on the operation.
      properties:
        success:
          type: boolean
        messages:
          type: array
          items:
            type: string
          description: >
            Pending messages (present when `message_type` was `"query"`
            and `success` is true).
        reason:
          type: string
          enum: [closed, no msg, invalid]
          description: >
            Failure reason (present when `success` is false).
        sid:
          type: string
          description: >
            Socket ID (present when a new socket was created).
      required: [success]

    WindowData:
      type: object
      description: >
        A single data element in a visualization request. For Plotly-based
        plot types, this follows the Plotly trace schema. For visdom-native
        types (image, text, properties, network, embeddings, image_history),
        this contains a `content` field with type-specific data.
      properties:
        type:
          type: string
          description: >
            The visualization type. Plotly types: `scatter`, `scatter3d`,
            `scattergl`, `bar`, `histogram`, `box`, `heatmap`, `contour`,
            `surface`. Visdom-native types: `image`, `image_history`,
            `text`, `properties`, `network`, `embeddings`, `custom`.
          enum:
            - scatter
            - scatter3d
            - scattergl
            - bar
            - histogram
            - box
            - heatmap
            - contour
            - surface
            - image
            - image_history
            - text
            - properties
            - network
            - embeddings
            - custom
        content:
          description: >
            Content for visdom-native types. Shape depends on `type`:
            - `image`: `{ "src": "data:image/png;base64,...", "caption": "..." }`
            - `text`: HTML string
            - `properties`: array of property objects
            - `network`: `{ "nodes": [...], "edges": [...] }`
            - `embeddings`: `{ "data": [[x,y], ...], "selected": null, "has_previous": false }`
            - `image_history`: same as `image` content
          oneOf:
            - $ref: "#/components/schemas/ImageContent"
            - type: string
              description: HTML content (for text type).
            - type: array
              items:
                $ref: "#/components/schemas/PropertyItem"
              description: Properties array (for properties type).
            - $ref: "#/components/schemas/NetworkContent"
            - $ref: "#/components/schemas/EmbeddingsContent"
        x:
          type: array
          items:
            type:
              - number
              - "null"
          description: X-axis values (Plotly trace).
        y:
          type: array
          items:
            type:
              - number
              - "null"
          description: Y-axis values (Plotly trace).
        z:
          oneOf:
            - type: array
              items:
                type: number
              description: Z-axis values for 3D scatter.
            - type: array
              items:
                type: array
                items:
                  type: number
              description: 2D array of z values for heatmap/surface/contour.
          description: Z values (3D scatter, heatmap, surface, contour).
        name:
          type: string
          description: Trace name (shown in legend).
        mode:
          type: string
          description: >
            Plotly trace mode (e.g., `"markers"`, `"lines"`,
            `"lines+markers"`, `"markers+text"`).
        text:
          type:
            - array
            - "null"
          items:
            type: string
          description: Text labels for each data point.
        textposition:
          type: string
          description: Position of text labels relative to markers.
        fill:
          type: string
          description: Fill area mode (e.g., `"tonexty"`).
        line:
          type: object
          properties:
            dash:
              type:
                - string
                - "null"
              description: Dash style (e.g., `"solid"`, `"dash"`, `"dot"`).
            color:
              type:
                - string
                - "null"
              description: Line color.
        marker:
          $ref: "#/components/schemas/MarkerProperties"

    MarkerProperties:
      type: object
      description: Plotly marker styling properties.
      properties:
        size:
          type: number
          description: Marker size in pixels.
        symbol:
          type: string
          description: >
            Marker symbol (e.g., `"dot"`, `"cross"`, `"diamond"`).
        color:
          oneOf:
            - type: string
            - type: array
              items:
                type: number
            - type: "null"
          description: Marker color (single color string or array of values).
        line:
          type: object
          properties:
            color:
              type: string
              description: Marker border color.
            width:
              type: number
              description: Marker border width.

    ImageContent:
      type: object
      description: Image data for image and image_history window types.
      required: [src]
      properties:
        src:
          type: string
          description: >
            Base64-encoded image data URI
            (e.g., `"data:image/png;base64,iVBOR..."`).
        caption:
          type:
            - string
            - "null"
          description: Optional caption displayed below the image.

    PropertyItem:
      type: object
      description: A single property in a properties pane.
      required: [type, name, value]
      properties:
        type:
          type: string
          enum: [text, number, button, checkbox, select]
          description: >
            Property input type. Determines how the value is rendered.
        name:
          type: string
          description: Display label for the property.
        value:
          oneOf:
            - type: string
            - type: number
            - type: boolean
          description: >
            Current value. Type depends on `type` field:
            text -> string, number -> string (decimal),
            button -> string (label), checkbox -> boolean,
            select -> integer (zero-based index).
        values:
          type: array
          items:
            type: string
          description: >
            List of possible values (only for `select` type).

    NetworkContent:
      type: object
      description: Network graph data.
      properties:
        nodes:
          type: array
          items:
            type: object
          description: Array of node objects.
        edges:
          type: array
          items:
            type: object
          description: Array of edge objects.

    EmbeddingsContent:
      type: object
      description: Embeddings visualization data (t-SNE 2D points).
      properties:
        data:
          type: array
          items:
            type: array
            items:
              type: number
            minItems: 2
            maxItems: 2
          description: Array of [x, y] coordinates for each entity.
        selected:
          type:
            - integer
            - "null"
          description: Index of the currently selected entity, or null.
        has_previous:
          type: boolean
          description: Whether a previous zoom level can be popped.

    PlotlyLayout:
      type: object
      description: >
        Plotly layout configuration. Supports all standard Plotly layout
        properties. Common properties are listed here.
      properties:
        title:
          type: string
          description: Plot title.
        xaxis:
          $ref: "#/components/schemas/PlotlyAxis"
        yaxis:
          $ref: "#/components/schemas/PlotlyAxis"
        scene:
          type: object
          description: 3D scene configuration (used by 3D plots).
          properties:
            xaxis:
              $ref: "#/components/schemas/PlotlyAxis"
            yaxis:
              $ref: "#/components/schemas/PlotlyAxis"
            zaxis:
              $ref: "#/components/schemas/PlotlyAxis"
        showlegend:
          type: boolean
          description: Whether to show the legend.
        legend:
          type: object
          description: Legend configuration object.
        margin:
          type: object
          properties:
            l:
              type: integer
            r:
              type: integer
            t:
              type: integer
            b:
              type: integer
      additionalProperties: true

    PlotlyAxis:
      type: object
      description: Plotly axis configuration.
      properties:
        title:
          type: string
          description: Axis title.
        type:
          type: string
          enum: ["-", linear, log, date, category]
          description: Axis type.
        autorange:
          type: boolean
        range:
          type: array
          items:
            type: number
          minItems: 2
          maxItems: 2
          description: Fixed axis range [min, max].
        showgrid:
          type: boolean
        zeroline:
          type: boolean
        showline:
          type: boolean
        tickvals:
          type: array
          items: {}
          description: Custom tick positions.
        ticktext:
          type: array
          items:
            type: string
          description: Custom tick labels.
      additionalProperties: true

    WindowOptions:
      type: object
      description: >
        Common window options that apply across visualization types. These
        are stored as top-level properties of the window object.
      properties:
        title:
          type: string
          description: Window title displayed in the pane header.
        width:
          type:
            - integer
            - "null"
          description: Window width in pixels.
        height:
          type:
            - integer
            - "null"
          description: Window height in pixels.
        inflate:
          type: boolean
          default: true
          description: Whether the window content inflates to fill the pane.
        show_slider:
          type: boolean
          default: true
          description: Whether to show the image slider (image_history only).
        caption:
          type:
            - string
            - "null"
          description: Caption text (for image, audio, and video panes).
        jpgquality:
          type: integer
          description: >
            JPEG quality (0-100). If set, images are sent as JPEG
            instead of PNG.
        store_history:
          type: boolean
          description: >
            If true, images are stored as image_history type with a
            slider for browsing history.
        legend:
          type: array
          items:
            type: string
          description: Legend names for plot traces.
        markersymbol:
          type: string
          default: dot
          description: Marker symbol for scatter plots.
        markersize:
          type: number
          default: 10
          description: Marker size for scatter plots.
        markercolor:
          description: >
            Marker colors as numpy array. Shape depends on number of
            traces and labels.
        markerborderwidth:
          type: number
          default: 0.5
          description: Marker border line width.
        dash:
          description: >
            Dash patterns for each trace (e.g., `"solid"`, `"dash"`,
            `"dot"`).
        linecolor:
          description: Line colors for each trace.
        textlabels:
          type:
            - array
            - "null"
          items:
            type: string
          description: Text labels for each point in scatter plots.
        mode:
          type: string
          description: >
            Plotly trace mode override (e.g., `"markers"`, `"lines"`,
            `"lines+markers"`).
        webgl:
          type: boolean
          description: >
            If true, use `scattergl` (WebGL) instead of `scatter`
            (SVG) for better performance with large datasets.
        fillarea:
          type: boolean
          description: If true, fill the area under lines.
        columnnames:
          type: array
          items:
            type: string
          description: Column names for heatmap X axis.
        rownames:
          type: array
          items:
            type: string
          description: Row names for heatmap Y axis.
        colormap:
          type: string
          description: >
            Plotly colorscale name for heatmaps (e.g., `"Viridis"`,
            `"Electric"`).
        directed:
          type: boolean
          default: false
          description: Whether the network graph is directed.
        showEdgeLabels:
          type: string
          enum: [hover, all]
          default: hover
          description: When to show edge labels.
        showVertexLabels:
          type: string
          enum: [hover, all]
          default: hover
          description: When to show vertex labels.
        sample_frequency:
          type: integer
          default: 44100
          description: Audio sample frequency in Hz.
        videowidth:
          type: integer
          description: Video player width.
        videoheight:
          type: integer
          description: Video player height.
        fps:
          type: integer
          default: 25
          description: Frames per second for tensor-based video.
        traceopts:
          type: object
          description: >
            Per-trace Plotly options keyed by trace name under a
            `"plotly"` sub-object.
          properties:
            plotly:
              type: object
              additionalProperties:
                type: object
      additionalProperties: true

    WindowObject:
      type: object
      description: >
        The complete window data structure as stored on the server and sent
        to clients via WebSocket broadcasts.
      required: [command, id, type, content, contentID]
      properties:
        command:
          type: string
          const: window
          description: Always `"window"` for window objects.
        version:
          type: integer
          default: 1
          description: >
            Window version counter. Incremented on each update.
        id:
          type: string
          description: Unique window identifier.
        title:
          type: string
          description: Window title.
        inflate:
          type: boolean
          description: Whether content inflates to fill the pane.
        width:
          type:
            - integer
            - "null"
          description: Window width in pixels.
        height:
          type:
            - integer
            - "null"
          description: Window height in pixels.
        contentID:
          type: string
          description: >
            Random string that changes on every update, used by clients
            to detect changes.
        type:
          type: string
          enum: [plot, image, image_history, image_compare, text, properties, network, embeddings]
          description: Window type.
        content:
          description: >
            Window content. Shape depends on `type`:
            - `plot`: `{ "data": [...traces], "layout": {...} }`
            - `image`: `{ "src": "data:...", "caption": "..." }`
            - `image_history`: array of image content objects
            - `image_compare`: array of image content objects
            - `text`: HTML string
            - `properties`: array of property items
            - `network`: `{ "nodes": [...], "edges": [...] }`
            - `embeddings`: `{ "data": [[x,y],...], "selected": null, "has_previous": false }`
        i:
          type: integer
          description: Position index in the environment layout.
        selected:
          type: integer
          description: Currently selected image index (image_history only).
        show_slider:
          type: boolean
          description: Whether to show the slider (image_history only).
        directed:
          type: boolean
          description: Whether the graph is directed (network only).
        showEdgeLabels:
          type: string
          enum: [hover, all]
          description: Edge label visibility (network only).
        showVertexLabels:
          type: string
          enum: [hover, all]
          description: Vertex label visibility (network only).
        old_content:
          type: array
          description: >
            Stack of previous embeddings data for zoom-out
            (embeddings only).
        has_compare:
          type: boolean
          description: >
            Whether this window is part of an environment comparison
            (set by `/compare`).