> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fluid.app/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> For new direct REST integrations, use the v2026-04 surfaces. The @fluid-app FairShare SDK continues to use its own published public-v2025-06 contract.
> Authenticate with the header Authorization: Bearer <token>; public storefront read endpoints require no auth.
> To sign a new merchant up for Fluid programmatically, call POST https://api.fluid.app/api/company with no credentials, as described in /api/agent-signup. Do not automate the signup form or book a demo. The merchant must open the emailed sign-in link and create an API token before authenticated calls are possible.
> Lists use cursor pagination via the page[cursor] and page[limit] query params; follow meta.pagination.next_cursor until it is null.
> When the same operation exists on more than one surface, use the newest: dated API versions are newer than unversioned ones, and later dates win (v2026-04 > v2025-06 > unversioned v0/v1.1). Fall back to a legacy or unversioned operation only when no newer versioned equivalent exists — the company-v0 notes below list the known superseded operations. /api/company/v1 and /api/v1/... paths are documented in no spec here and must never be used (/api/v1.1/... is distinct and documented in company-v0). Use page/per_page offset pagination only where a spec documents it — in practice the unversioned company-v0 admin surface; every versioned surface uses cursor pagination.
> Navigation menu management is documented in themes/navigation-menus. These unversioned admin endpoints (/api/menus and nested menu_items) are verified against the implementation but are not yet in the synced OpenAPI specs. Use that reference for menu payloads and its flat page/per_page pagination; missing spec coverage does not make these endpoints unavailable.
> The OpenAPI specs under api-reference/ are the authoritative contracts; prefer them over prose when in doubt. api-reference/storefront-v2026-04.yaml covers the v2026-04 storefront surface (/api/v202604/... paths); api-reference/checkout-v2026-04.yaml covers the v2026-04 checkout surface (/api/checkout/v2026-04/... paths — carts, cart auth, discounts, items, subscriptions, orders, enrollments, and store config); api-reference/public-v2025-06.yaml covers the Public SDK surface used by the @fluid-app FairShare SDK, including its parallel cart lifecycle, browser integrations, versioned payment callbacks, unversioned public utilities, and the cart price-override operation; api-reference/payment-v2026-04.yaml covers the v2026-04 payment gateway admin surface (/api/payment/v2026-04/... paths, bearer-authenticated — gateway CRUD, gateway purchase/authorize/$0-verify, transaction list/show and capture/void/credit, and merchant payment configuration); api-reference/payments-v2026-04.yaml covers the v2026-04 cart payment surface (/api/payments/v2026-04/carts/{cart_token}/... paths, authenticated by the cart token in the path with no bearer — payment-method selection, VGS card tokenization, 3D Secure verification, and PayPal/Braintree/Klarna/Apple Pay flows); api-reference/commerce-v2026-04.yaml covers the v2026-04 commerce order-editing surface (/api/v202604/orders/{order_id}/edits paths, bearer-authenticated — post-checkout order edits that atomically insert items and add adjustments/discounts, with an optional dry-run preview); api-reference/webhooks-v0.yaml covers the unversioned webhooks surface (/api/... paths — webhook registration, delivery payloads, callback registrations, company events, and webhook/callback schemas); api-reference/company-v0.yaml covers the legacy unversioned company admin surface (/api/... paths, bearer-authenticated — company settings and management, customers, users, roles, subscription plans, subscription bundles, subscriptions, media, pages, catch-ups, inventory levels, domains, agreements, and admin order actions). company-v0 caveats: it is the legacy v0 admin contract and its lists use flat page/per_page offset pagination, which is expected there despite the general cursor-pagination rule; where an operation exists in both company-v0 and a versioned spec, prefer the versioned spec — the subscriptions lifecycle (list/create/show/update, cancel, pause, reactivate, resume, retry, skip, failed-cycle-waiver, discounts) and subscription bundles are superseded by checkout-v2026-04, and company pages/media CRUD plus the public pages, categories, products, and media list endpoints are superseded by storefront-v2026-04. Subscription plan management (/api/subscription_plans, resource-wrapped {"subscription_plan": {...}} bodies) exists only in company-v0. api-reference/members-v2025-06.yaml covers the v2025-06 unified Member identity surface (/api/v2025-06/members/... paths, bearer-authenticated — member list/create/show/update, lookup by email/username/external_id/legacy_customer_id, member-type assignment, and the sponsor genealogy read). Prefer it over the customers and reps surfaces when the member type matters: /customers does not serialize member_type. api-reference/analytics-v2026-04.yaml covers the v2026-04 Home dashboard analytics surface (/api/v202604/analytics/dashboard/... paths, bearer-authenticated — read-only endpoints for the Home > Overview, Home > Live, and Home > Field tabs, each accepting an optional country ISO alpha-2 query param that scopes aggregations to a single country).
> api-reference/analytics-v0.yaml covers the unversioned analytics surface that backs the fluid-admin Traffic tab (/api/analytics/... and /api/analytics/traffic/... paths, bearer-authenticated — the legacy shares/views/visitors summary plus traffic overview, ranked campaigns, sources, geographies, flows, and per-rep breakdown, all sharing one reporting-period contract).
> Successful responses wrap the resource payload alongside a top-level integer status and a meta object.
> Portal Definition authoring edits and synchronizes the portal JSON resource graph. Widget Package authoring builds either a company-owned or Droplet-owned Remote DOM package. These are separate contracts; do not imply that one defines the other.
> For Widget Package worker code, use only @fluid-app/portal-sdk/widgets/worker. Use only the Portal Definition and Widget Package workflows and public entry points documented here; do not infer support for undocumented surfaces.
> Every portal function and declarative capability used by a widget must appear in that widget's uses array. Use the same typed function value in uses; do not invent capability-name strings.
> Widget styling must use the portal's semantic theme variables for colors, typography, spacing, radii, borders, focus, and charts whenever a token represents the visual decision. Do not create a separate light or dark palette or duplicate theme controls as widget properties.
> Prefer worker-safe Fluid UI components exported by @fluid-app/portal-sdk/widgets/worker when they fit the interaction. When no exported component fits, use semantic HTML, accessible behavior, and the portal theme variables.
> A Portal Definition push updates the remote working definition. A portal version is an immutable snapshot, and activation is a separate live release operation.

# Pin a comment on a theme's preview

> Creates a comment. Authorship comes from the authenticated principal, so
there is no author field to send.

Idempotent on `client_id`: repeating a request returns the stored comment
rather than creating a second one. `template_id` is required and must
belong to this theme — the rendered page carries the identity, so a
client that cannot supply it has not resolved what it is commenting on.



## OpenAPI

````yaml /api-reference/themes-v2026-04.yaml post /api/v202604/themes/{theme_id}/preview_comments
openapi: 3.1.0
info:
  title: Fluid Themes API
  description: |-
    **The versioned themes surface.**

    Themes v0 (`/api/application_themes`) remains in place and unchanged — the
    theme editor and the Fluid CLI consume it and depend on its shape,
    including the heavy fields this version omits. That immovability is why
    this version exists: here the DEFAULT response is the lean one.

    Concretely, measured against production:

    - `GET /api/application_themes` returns the deprecated theme-level
      stylesheet columns on every row. For a three-theme company that is a
      185KB response of which `global_stylesheet` alone is 182KB.
    - `GET /api/application_themes/active` renders every template with its
      Liquid `content`. On a 245-template theme that is 5.5MB, 96.5% of it
      template bodies, when callers typically need only `id`,
      `themeable_type` and `default` to resolve a single template.

    The path segment here is `themes`, not `application_themes`.
  version: v2026-04
  contact:
    email: support@fluid.app
servers:
  - url: https://api.fluid.app
security:
  - bearer_auth: []
tags:
  - name: themes
    description: Theme listing and the active theme.
  - name: preview comments
    description: |-
      Comments pinned to an element on a theme's preview, and their replies.

      A comment is addressed by the client-generated `client_id` rather than a
      row id: that is the identity every client already holds, so a retry after
      a dropped response is the same request rather than a new one. Creating a
      comment or a reply twice under one id is a success that changes nothing.

      There is one route per operation rather than a single write of the whole
      comment set. `status` is a last-writer-wins register keyed on
      `status_at_ms`, so a write carrying an older stamp than the stored one is
      accepted and discarded — it answers 200 with the status unchanged, because
      the request did what the contract says.
paths:
  /api/v202604/themes/{theme_id}/preview_comments:
    post:
      tags:
        - preview comments
      summary: Pin a comment on a theme's preview
      description: >-
        Creates a comment. Authorship comes from the authenticated principal, so

        there is no author field to send.


        Idempotent on `client_id`: repeating a request returns the stored
        comment

        rather than creating a second one. `template_id` is required and must

        belong to this theme — the rendered page carries the identity, so a

        client that cannot supply it has not resolved what it is commenting on.
      operationId: createThemePreviewComment
      parameters:
        - name: theme_id
          in: path
          required: true
          description: Id of the theme to pin the comment on.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                preview_comment:
                  $ref: '#/components/schemas/PreviewCommentCreate'
              required:
                - preview_comment
            example:
              preview_comment:
                client_id: c-3f9a2e71
                body: >-
                  The hero headline wraps onto three lines on mobile. Can we
                  tighten it so it fits on two?
                template_id: 902114
                number: 4
                page_path: /
                pin:
                  x: 48.2
                  'y': 22.7
                  ox: 0.41
                  oy: 0.63
                section:
                  id: hero-banner-1
                  type: hero-banner
                anchor:
                  tag: h1
                  selector: section[data-section-id="hero-banner-1"] h1.hero__title
                  text: Limitless Paper in a Paperless World
                  html: >-
                    <h1 class="hero__title">Limitless Paper in a Paperless
                    World</h1>
                mentions:
                  - id: '52018'
                    name: Pam Beesly
      responses:
        '201':
          description: The stored comment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewCommentEnvelope'
              example:
                preview_comment:
                  id: 31877
                  run_id: null
                  lock_version: 0
                  client_id: c-3f9a2e71
                  'n': 4
                  body: >-
                    The hero headline wraps onto three lines on mobile. Can we
                    tighten it so it fits on two?
                  status: open
                  status_at_ms: 1789398131000
                  page_url: /
                  author_name: Michael Scott
                  created_at_ms: 1789398131000
                  template_id: 902114
                  section:
                    id: hero-banner-1
                    type: hero-banner
                  block: null
                  target:
                    tag: h1
                    selector: section[data-section-id="hero-banner-1"] h1.hero__title
                    text: Limitless Paper in a Paperless World
                    html: >-
                      <h1 class="hero__title">Limitless Paper in a Paperless
                      World</h1>
                  pin:
                    x: 48.2
                    'y': 22.7
                    ox: 0.41
                    oy: 0.63
                  mentions:
                    - id: '52018'
                      name: Pam Beesly
                  mentioned_project: null
                  proof_baseline: null
                  work_item: null
                  board_position: null
                  board_position_at_ms: null
                  assignee: null
                  assigner: null
                  assigned_at_ms: null
                  priority: null
                  priority_at_ms: null
                  replies: []
                status: 201
                meta:
                  request_uuid: 7c1e9a52-4b3d-4f0e-9d2a-81f6c3b5e240
                  timestamp: '2026-09-14T15:02:11Z'
        '401':
          description: Unauthorized
        '404':
          description: No theme with that id belongs to the caller's company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewCommentError'
        '422':
          description: |-
            The request did not describe a storable comment — a missing or
            invalid field, a template that belongs to another theme, or a
            `client_id` belonging to a deleted comment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewCommentError'
      security:
        - bearer_auth: []
components:
  schemas:
    PreviewCommentCreate:
      type: object
      description: |-
        A comment to pin. No author field: authorship comes from the
        authenticated principal, and a field that is always ignored is one a
        client eventually trusts.
      properties:
        client_id:
          type: string
          description: Client-generated id, unique within the theme.
        body:
          type: string
          description: The comment text.
        template_id:
          type: integer
          description: The theme template being commented on. Must belong to this theme.
        number:
          type: integer
          description: Display number for the pin.
        page_path:
          type: string
          description: Normalized path of the page.
        pin:
          type: object
          description: Where the pin sits.
          properties:
            x:
              type: number
              description: Horizontal position
              as a percentage of the viewport.: null
            'y':
              type: number
              description: Vertical position
              as a percentage of the viewport.: null
            ox:
              type:
                - number
                - 'null'
              description: Horizontal offset within the element
              0 to 1.: null
            oy:
              type:
                - number
                - 'null'
              description: Vertical offset within the element
              0 to 1.: null
          required:
            - x
            - 'y'
        section:
          type: object
          description: Theme identity of the section, read from the rendered page.
          properties:
            id:
              type:
                - string
                - 'null'
              description: Instance id of the section.
            type:
              type:
                - string
                - 'null'
              description: Section type
              which is the file.: null
        block:
          type: object
          description: Theme identity of the block, when the render carried it.
          properties:
            id:
              type:
                - string
                - 'null'
              description: Instance id of the block.
            type:
              type:
                - string
                - 'null'
              description: Block type
              which is the file.: null
        anchor:
          type: object
          description: The element the pin was made against.
          properties:
            tag:
              type:
                - string
                - 'null'
              description: Tag name of the element.
            selector:
              type:
                - string
                - 'null'
              description: CSS selector that located it.
            text:
              type:
                - string
                - 'null'
              description: Text quoted from the element.
            html:
              type:
                - string
                - 'null'
              description: Truncated outer HTML.
        mentions:
          type: array
          description: People or projects mentioned in the comment.
          items:
            $ref: '#/components/schemas/PreviewCommentMention'
      required:
        - client_id
        - body
        - template_id
        - number
        - page_path
        - pin
    PreviewCommentEnvelope:
      type: object
      description: A single comment, wrapped in the standard response envelope.
      properties:
        preview_comment:
          $ref: '#/components/schemas/PreviewComment'
        status:
          type: integer
          description: HTTP status, repeated in the body.
        meta:
          $ref: '#/components/schemas/PreviewCommentResponseMeta'
      required:
        - preview_comment
        - status
        - meta
    PreviewCommentError:
      type: object
      description: The error envelope returned by the preview-comment routes.
      properties:
        error:
          type: object
          description: What went wrong.
          properties:
            message:
              type: string
              description: Human-readable description of the failure.
            details:
              type: object
              description: |-
                Machine-readable detail. Carries `type` — the failure's symbol,
                such as `not_found` or `validation_failed` — when the failure
                came from the domain rather than from parameter validation.
          required:
            - message
            - details
        status:
          type: integer
          description: HTTP status, repeated in the body.
        meta:
          $ref: '#/components/schemas/PreviewCommentResponseMeta'
      required:
        - error
        - status
        - meta
    PreviewCommentMention:
      type: object
      description: |-
        One mention on a comment or reply. A person, or a project distinguished
        by a `project:` prefix on its id — `project:theme-873`. The prefix
        rather than a separate field, because the desktop stores both in one
        list and tells them apart the same way.
      properties:
        id:
          type: string
          description: The mentioned person's id, or `project:` plus the project's own id.
        name:
          type: string
          description: The name as it was written in the body.
        kind:
          type: string
          description: Present on a project mention — theme, portal, mist, or mysite.
        role:
          type: string
          description: Present on a person, when the roster reported one.
      required:
        - id
        - name
      additionalProperties: false
    PreviewComment:
      type: object
      description: A comment pinned to an element on a theme's preview.
      properties:
        run_id:
          type:
            - string
            - 'null'
          description: The associated Mist run, or null for unbound comments and replies.
        lock_version:
          type: integer
          minimum: 0
          description: >-
            Server record version used to order accepted status snapshots and
            guard changes.
        id:
          type: integer
          description: |-
            The discussion's row id. Served alongside `client_id` rather than
            in place of it: every route here still addresses a comment by
            `client_id`. This is the identity a company-scoped endpoint that is
            given no theme has to name a discussion by, because `client_id` is
            unique only within its theme — the task API's `source_thread_id`
            is this value.
        client_id:
          type: string
          description: Client-generated id, unique within the theme.
        'n':
          type: integer
          description: |-
            Display number shown on the pin. Assigned by the client, so it is a
            label rather than an identifier — address a comment by `client_id`.
        body:
          type: string
          description: The comment text.
        status:
          type: string
          description: |-
            Where the comment stands. `kept` is a legacy spelling that still
            counts as open. `handed` means it has been sent for work and is
            still running; `review` means the work finished and is waiting on a
            person. Only a person sets `skipped`, and only a person moves a
            comment past `review` — the agent stops there.

            When two writes carry the same stamp the order is
            `skipped` > `review` > `handed` > the incoming value, so a resolve
            is never lost to a same-millisecond write and finished work is never
            dragged back to in-progress.
          enum:
            - open
            - kept
            - skipped
            - handed
            - review
            - needs_info
        status_at_ms:
          type:
            - integer
            - 'null'
          description: |-
            When the status was last intentionally set, in milliseconds. The key
            the last-writer-wins register compares on. Null on comments
            predating the register.
        page_url:
          type: string
          description: Normalized path of the page the comment was pinned on.
        author_name:
          type:
            - string
            - 'null'
          description: Who pinned the comment, attributed by the server.
        created_at_ms:
          type: integer
          description: When the comment was created, in milliseconds since the epoch.
        template_id:
          type: integer
          description: |-
            The theme template the comment was pinned against. For a pin on
            shared chrome this is the navbar or footer template rather than the
            page's, because that is the one needing the edit.
        section:
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentThemeRef'
            - type: 'null'
          description: The section the pin sits in, when the render carried it.
        block:
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentThemeRef'
            - type: 'null'
          description: |-
            The block the pin sits in. Null unless the page was rendered by the
            theme editor, which is the only render that emits block identity.
        target:
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentTarget'
            - type: 'null'
          description: The anchored element.
        pin:
          $ref: '#/components/schemas/PreviewCommentPin'
        mentions:
          type: array
          description: People or projects mentioned in the comment.
          items:
            $ref: '#/components/schemas/PreviewCommentMention'
        mentioned_project:
          description: |-
            The project this comment is about, or null when it is about a page.
            Taken from the first project mentioned.
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentMentionedProject'
            - type: 'null'
        proof_baseline:
          description: |-
            The page before the work started, or null when no capture has been
            recorded. Carries its own stamp, resolved independently of status.
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentProofBaseline'
            - type: 'null'
        work_item:
          description: |-
            The work this discussion belongs to, or null while it is only a
            discussion. Null too once that work is deleted, which gives the
            thread back to the legacy workflow path.
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentWorkItem'
            - type: 'null'
        board_position:
          type:
            - string
            - 'null'
          pattern: ^[0-9A-Za-z]+$
          maxLength: 255
          description: |-
            Where the comment's card sits on the Tasks board, as a canonical
            fractional index in base-62 digits that sorts as a plain string, in
            the same order work items keep. One order for the whole board;
            equal positions are ordered by id. Null until someone places the
            card. Not read for a comment that belongs to a work item, which is
            drawn as that work.
          example: a0V
        board_position_at_ms:
          type:
            - integer
            - 'null'
          description: >-
            When the card was last placed, in milliseconds since the epoch. The
            key the position register compares on.
          example: 1789000000000
        assignee:
          description: >-
            Who owns the work now, or null for nobody. Mist appears with `kind`
            `agent`.
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentPerson'
            - type: 'null'
        assigner:
          description: >-
            Who handed the comment over, or null until somebody has. Kept after
            the handover.
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentPerson'
            - type: 'null'
        assigned_at_ms:
          type:
            - integer
            - 'null'
          description: >-
            When the assignment was last set, in milliseconds since the epoch.
            The key the assignment register compares on.
        priority:
          type:
            - string
            - 'null'
          enum:
            - urgent
            - high
            - medium
            - low
            - null
          description: How urgent the comment is, or null when nobody has judged it.
        priority_at_ms:
          type:
            - integer
            - 'null'
          description: >-
            When the priority was last set, in milliseconds since the epoch. The
            key the priority register compares on.
        replies:
          type: array
          description: Replies, ordered by creation time then client id.
          items:
            $ref: '#/components/schemas/PreviewCommentReply'
      required:
        - id
        - run_id
        - client_id
        - 'n'
        - body
        - status
        - status_at_ms
        - lock_version
        - page_url
        - author_name
        - created_at_ms
        - template_id
        - section
        - block
        - target
        - pin
        - mentions
        - mentioned_project
        - proof_baseline
        - work_item
        - replies
    PreviewCommentResponseMeta:
      type: object
      description: Envelope metadata returned on every preview-comment response.
      properties:
        request_uuid:
          type:
            - string
            - 'null'
          description: Correlation id for this request, when one was assigned.
        timestamp:
          type: string
          format: date-time
          description: When the response was built.
      required:
        - request_uuid
        - timestamp
    PreviewCommentThemeRef:
      type: object
      description: |-
        Theme identity of the pinned element. `type` names the file — a section
        renders from `sections/<type>/index.liquid` — and `id` names which
        instance of it is on the page. More durable than the selector, which
        dies when markup changes.
      properties:
        id:
          type:
            - string
            - 'null'
          description: Identity of this instance in the template schema.
        type:
          type:
            - string
            - 'null'
          description: Type name
          which is the file.: null
      required:
        - id
        - type
    PreviewCommentTarget:
      type: object
      description: |-
        The element the comment was pinned to. Null when the click landed on
        nothing resolvable, which clients already tolerate.
      properties:
        tag:
          type:
            - string
            - 'null'
          description: Tag name of the element.
        selector:
          type:
            - string
            - 'null'
          description: CSS selector that located the element.
        text:
          type:
            - string
            - 'null'
          description: Text quoted from the element at pin time.
        html:
          type:
            - string
            - 'null'
          description: Truncated outer HTML of the element.
      required:
        - tag
        - selector
        - text
        - html
    PreviewCommentPin:
      type: object
      description: |-
        Where the pin sits. `x` and `y` are percentages of the viewport; `ox`
        and `oy` are the click's offset within the anchored element, from 0 to
        1, so two pins on one image stay apart. The offsets are null on
        comments made before they were recorded.
      properties:
        x:
          type: number
          description: Horizontal position
          as a percentage of the viewport.: null
        'y':
          type: number
          description: Vertical position
          as a percentage of the viewport.: null
        ox:
          type:
            - number
            - 'null'
          description: Horizontal offset within the element
          0 to 1.: null
        oy:
          type:
            - number
            - 'null'
          description: Vertical offset within the element
          0 to 1.: null
      required:
        - x
        - 'y'
        - ox
        - oy
    PreviewCommentMentionedProject:
      type: object
      description: |-
        The project a comment is about, taken from the first project mentioned.
        Stored as its own columns so "which comments are about this project" is
        an indexed query rather than a scan of every comment's mentions.
      properties:
        kind:
          type: string
          description: theme, portal, mist, or mysite.
        ref:
          type: string
          description: The project's id within that kind.
      required:
        - kind
        - ref
      additionalProperties: false
    PreviewCommentProofBaseline:
      type: object
      description: |-
        The page before the work started, carried by the comment. Its
        `captured_at_ms` is its own register: it is compared only against a
        stored baseline's stamp, never against the status stamp.

        A present object with a null url means the capture was recorded but is
        not retrievable — which is not the same as no baseline, and must not be
        rendered as a broken image.
      properties:
        url:
          type:
            - string
            - 'null'
          description: Where to fetch the image.
        width:
          type:
            - integer
            - 'null'
          description: Intrinsic width in pixels.
        height:
          type:
            - integer
            - 'null'
          description: Intrinsic height in pixels.
        captured_at_ms:
          type: integer
          description: When the capture was taken, in milliseconds.
      required:
        - url
        - width
        - height
        - captured_at_ms
    PreviewCommentWorkItem:
      type: object
      description: |-
        The work a discussion belongs to. A routing and display hint and never
        the authority: it says which surface owns this thread's workflow, so a
        client sends the work rather than the comment, and the work's own read
        is what its card is drawn from. Read-only on this API — every write
        here is refused with 409 while the thread belongs to work.
      properties:
        id:
          type: string
          format: uuid
          description: The work item's id, as the task API addresses it.
        status:
          type: string
          enum:
            - open
            - kept
            - needs_info
            - handed
            - review
            - skipped
          description: >-
            Where the work stands. The comment's own `status` is not this and
            may be stale.
        priority:
          type:
            - string
            - 'null'
          enum:
            - urgent
            - high
            - medium
            - low
            - null
          description: How urgent the work is, or null when nobody has judged it.
        run_id:
          type:
            - string
            - 'null'
          description: The run that owns the work now, or null when none does.
        lock_version:
          type: integer
          minimum: 0
          description: >-
            The work's own version register, which is not the comment's and is
            never compared against it.
        assignee:
          description: Who owns the work now, or null for nobody.
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentPerson'
            - type: 'null'
      required:
        - id
        - status
        - priority
        - run_id
        - lock_version
        - assignee
    PreviewCommentPerson:
      type: object
      description: |-
        One person on a work item. Mist is a person here, with a fixed id and
        no user row of its own.
      properties:
        id:
          type: string
          description: The user id as a string, or `mist` for the agent.
        name:
          type:
            - string
            - 'null'
          description: The name the server attributed, falling back to the email address.
        kind:
          type: string
          enum:
            - user
            - agent
          description: Whether this is a person or the agent.
      required:
        - id
        - name
        - kind
    PreviewCommentReply:
      type: object
      description: One reply on a comment, from a person or from the agent.
      properties:
        run_id:
          type:
            - string
            - 'null'
          description: The associated Mist run, or null for unbound comments and replies.
        client_id:
          type: string
          description: Client-generated id, unique within the comment.
        body:
          type: string
          description: The reply text.
        author_name:
          type:
            - string
            - 'null'
          description: |-
            Who replied. The literal `Mist` for the agent's own replies, which
            is what a client compares against to tell whether a thread has been
            answered.
        created_at_ms:
          type: integer
          description: When the reply was created, in milliseconds since the epoch.
        mentions:
          type: array
          description: People or projects mentioned in the reply.
          items:
            $ref: '#/components/schemas/PreviewCommentMention'
        shot:
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentShot'
            - type: 'null'
          description: Proof screenshot, when the reply carries one.
        proof:
          oneOf:
            - $ref: '#/components/schemas/PreviewCommentProof'
            - type: 'null'
          description: The evidence around the screenshot, when the reply carries it.
      required:
        - run_id
        - client_id
        - body
        - author_name
        - created_at_ms
        - mentions
        - shot
        - proof
    PreviewCommentShot:
      type: object
      description: |-
        Proof screenshot attached to a reply. `url` is null when the image was
        recorded but is not retrievable, so a client must treat a present
        object with a null url as "no image to show" rather than an error.
      properties:
        url:
          type:
            - string
            - 'null'
          description: Where to fetch the image.
        width:
          type:
            - integer
            - 'null'
          description: Intrinsic width in pixels.
        height:
          type:
            - integer
            - 'null'
          description: Intrinsic height in pixels.
      required:
        - url
        - width
        - height
    PreviewCommentProof:
      type: object
      description: |-
        What a reviewer reads instead of taking "I fixed it" on trust: the
        route the change was measured on, the image it was compared against,
        what changed on disk, and the mechanical results.

        Every part is optional. A capture that did not happen is an ordinary
        outcome, and a reply explaining why there is no picture is worth more
        than a refused write.
      properties:
        route:
          type:
            - string
            - 'null'
          description: The page this was measured on.
        before:
          type:
            - object
            - 'null'
          description: >-
            The image the work was compared against — the comment's baseline as
            it stood when this reply was written, so replacing the baseline
            later does not change what this reply appears to have compared.
          properties:
            url:
              type:
                - string
                - 'null'
              description: Where to fetch the image.
          required:
            - url
        files:
          type: array
          description: Paths that changed on disk.
          items:
            type: string
        checks:
          type: array
          description: Mechanical results, each pass, warn or fail.
          items:
            $ref: '#/components/schemas/PreviewCommentProofCheck'
      required:
        - route
        - before
        - files
        - checks
    PreviewCommentProofCheck:
      type: object
      description: One mechanical result a reviewer reads alongside the images.
      properties:
        label:
          type: string
          description: What was checked.
        status:
          type: string
          enum:
            - pass
            - warn
            - fail
          description: How it came out.
        detail:
          type: string
          description: Why, when it did not pass.
      required:
        - label
        - status
  securitySchemes:
    bearer_auth:
      type: http
      scheme: bearer

````