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

# Replace a playbook

> Replace a playbook's name, description and positions.

A clause type absent from `positions` is removed. Within a clause type that
is present, the authoring fields this API does not publish are preserved, so
a write through the API cannot delete work done in the product.



## OpenAPI

````yaml https://api.vaquill.ai/workspace/openapi/v1.json put /v1/playbooks/{playbookId}
openapi: 3.1.0
info:
  title: Vaquill Legal Workspace API
  description: >-
    Organization-scoped API for driving your legal workspace from your own
    backend: matters, documents, drafting, review, compare and matrices.


    **Authentication**: `Authorization: Bearer vq_ws_...`. Credentials are
    issued by an organization owner from the automation console at `/automation`
    and are shown once. The organization is resolved from the credential and can
    never be named in a request, so there is no `organizationId` field anywhere
    in this API and sending one is refused.


    This is NOT the Vaquill Data API. That one holds a `vq_key_` credential and
    serves the public legal corpus; the two share no credential, host or
    endpoint, and sending a `vq_key_` here is refused with an error naming the
    other product.


    ## Long-running work


    Anything that costs real work answers `202` with an **operation**. Poll `GET
    /v1/operations/{operationId}` until `status` is `succeeded`, `failed` or
    `cancelled`, honouring `Retry-After` between polls. There are no webhooks,
    so polling is the only completion signal. The five statuses are `queued`,
    `running`, `succeeded`, `failed`, `cancelled`, and there is no sixth: every
    capability reports through the same envelope.


    Send an `Idempotency-Key` on any launch. A retry with the same key and the
    same body returns the FIRST operation rather than starting a second billable
    job, which is what makes recovering from a timeout free.


    ## Identifiers


    Every public id is `<prefix>_<32 lowercase hex>`, for example
    `mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6`. The prefix names the resource type.
    Ids are opaque: take them from responses rather than building them, and
    never pass a bare database uuid.


    ## Lists


    Every collection answers `{data, pagination}`, never a bare array. Page with
    `limit` and `offset`, and read `pagination.total` to size a job before
    running it. `pagination.hasMore` is the reliable signal that rows remain.


    ## Errors


    Errors are RFC 9457 problem documents (`application/problem+json`),
    including for a wrong path and a wrong method. Branch on `type`; it is the
    stable identifier and it resolves to a page describing the failure. `title`
    and `detail` are written for people and may be reworded at any time. Every
    response carries `X-Request-ID`, and every error repeats it as `requestId`:
    quote it when contacting support.


    A `404` is returned identically for a resource that does not exist, one
    belonging to another organization, and one outside your installation's
    matter allowlist. That is deliberate, so the status code cannot be used to
    discover which ids exist elsewhere.


    ## Rate limits


    Limits are per credential and are set by what an operation COSTS rather than
    by its HTTP method, so a `GET` that runs a retrieval is not a read. Each
    operation's tier is fixed; a `429` carries `Retry-After`.


    | Tier | Per minute | Per hour | Per day |

    |---|---|---|---|

    | `poll` | 120 | 3000 | 30000 |

    | `read` | 60 | 1200 | 15000 |

    | `write` | 30 | 600 | 5000 |

    | `launch` | 10 | 200 | 2000 |

    | `download` | 20 | 300 | 3000 |

    | `upload` | 5 | 100 | 500 |


    ## Scopes


    A credential carries an explicit scope set, chosen when it is issued. A call
    outside them is `403` with `insufficient-scope`, naming the scopes the
    operation needed. Read and run are separate throughout, so a credential can
    be allowed to read results without being allowed to spend money producing
    them.


    `chronology:read`, `chronology:write`, `clients:read`, `clients:write`,
    `compare:read`, `compare:run`, `compliance:read`, `compliance:run`,
    `credentials:rotate`, `documents:download`, `documents:read`,
    `documents:write`, `drafting:read`, `drafting:run`, `exports:create`,
    `exports:read`, `facts:read`, `facts:run`, `matrices:read`, `matrices:run`,
    `matrices:write`, `matters:read`, `matters:write`, `nda:read`, `nda:run`,
    `operations:read`, `playbooks:read`, `playbooks:run`, `playbooks:write`,
    `research:read`, `research:run`, `review:read`, `review:run`,
    `summaries:read`, `summaries:run`, `webhooks:read`, `webhooks:write`,
    `workflows:read`, `workflows:run`, `workflows:write`
  version: 1.0.0
servers:
  - url: https://api.vaquill.ai/workspace
    description: Vaquill Legal Workspace API (production)
  - url: /workspace
    description: Vaquill Legal Workspace API (relative to the mount)
security:
  - WorkspaceAuth: []
tags:
  - name: Operations
    description: >-
      The one job envelope. Every long-running call answers `202` with an
      operation, and polling this resource is the only way to learn it finished:
      there are no webhooks. Five statuses, no synonyms.
  - name: Clients
    description: The people and companies matters are filed under. Synchronous CRUD.
  - name: Matters
    description: >-
      The unit of work everything else hangs off. A matter scopes documents,
      drafts, reviews, comparisons and matrices, and it is what a credential's
      access is checked against.
  - name: Folders
    description: >-
      Organization inside and across matters. The same folders the web app
      shows, so one created here appears in the customer's browser.
  - name: Documents
    description: >-
      Files in a matter: their metadata, their ingested text, and the original
      bytes. A document is only usable by retrieval, drafting and review once
      its status is `succeeded`.
  - name: Uploads
    description: >-
      Getting files in. One presigned multipart flow at every size, with the
      bytes going straight to storage and never through this API. Initiate, PUT
      each part, then complete.
  - name: Research
    description: >-
      Asking legal questions and getting grounded, cited answers. This API does
      NOT stream: an ask answers `202` and you poll the operation, then read the
      answer off the message it points at. Conversation state is kept here, so
      you hold a chat id rather than replaying a transcript. Also covers the
      matter settings behind an answer, the skills you can ask through, and the
      standalone web-research tools.
  - name: Drafting
    description: >-
      Generating, revising and exporting draft documents. Bodies come out as
      sections and go in as markdown; the editor's own format is never on the
      wire. Rendered files come from the export route.
  - name: Playbooks
    description: >-
      The organization's negotiating positions, per contract type, and the
      starter templates to build them from. A playbook is the input a contract
      review is measured against.
  - name: Reviews
    description: >-
      Reviewing one contract against a playbook: clause analysis, redlines,
      flags, liability exposure and a reported sign-off gate. Export applies the
      redlines as native Word tracked changes.
  - name: Facts
    description: >-
      The cross-document fact ledger for a matter: what every document in it
      asserts, coalesced and cited back to its source passages.
  - name: Matter summary
    description: >-
      A citation-backed summary of everything filed to a matter, generated on
      demand and readable claim by claim.
  - name: Chronology
    description: >-
      The dated events across a matter's documents, as one timeline, with
      duplicate and date-conflict flags.
  - name: NDA triage
    description: >-
      Screening one inbound NDA against ten standard criteria and, where you
      name one, your own NDA playbook. Answers `green`, `yellow` or `red` with
      the reasoning per criterion, plus a report you can forward.
  - name: Compliance
    description: >-
      Checking one document against one regulation's requirement checklist: a
      verdict per requirement with the article it comes from, the gaps, and what
      to do about them. Only regulations with a real checklist behind them are
      accepted.
  - name: Comparisons
    description: >-
      Diffing two documents in a matter into structural changes, with a
      substantive-versus-cosmetic judgment and a downloadable redline.
  - name: Matrices
    description: >-
      Spreadsheet-style extraction across many documents: rows are documents,
      columns are questions, cells are answers with verified citations. Building
      a grid is free; running it is what costs.
  - name: Workflows
    description: >-
      Multi-step analyses from a fixed catalogue. Read a definition to learn
      what documents and inputs it takes, launch a run inside a matter, then
      download the artifacts it produces.
externalDocs:
  description: Getting started guide and error reference
  url: https://vaquill.ai/docs/workspace-api
paths:
  /v1/playbooks/{playbookId}:
    put:
      tags:
        - Playbooks
      summary: Replace a playbook
      description: >-
        Replace a playbook's name, description and positions.


        A clause type absent from `positions` is removed. Within a clause type
        that

        is present, the authoring fields this API does not publish are
        preserved, so

        a write through the API cannot delete work done in the product.
      operationId: playbooks.update
      parameters:
        - name: playbookId
          in: path
          required: true
          schema:
            type: string
            title: Playbookid
          description: >-
            `pbk_` identifier of the playbook. Take it from `GET /v1/playbooks`.
            A starter template is addressed by its `slug` instead, not by this.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlaybookUpdateRequest'
            example:
              name: MSA, buyer side
              description: >-
                Master services agreement with Acme for the 2026 platform
                rollout.
              positions:
                limitation_of_liability:
                  standardPosition: >-
                    Liability is capped at the fees paid in the preceding twelve
                    months.
                  acceptableRange: >-
                    Between one and two times the fees paid in the preceding
                    twelve months.
                  escalationTriggers:
                    - value
                  fallbackLadder:
                    - value
                  dealBreaker: value
                  priority: must_have
                  approvalLevel: none
                  escalationConditions:
                    - attribute: clause_severity
                      operator: eq
                      value: '1000000'
                      escalateTo: manager
                      note: 'Escalated to GC: deal value over $1M.'
                  rationale: >-
                    Our standard position caps liability at fees paid in the
                    preceding 12 months.
                  riskWeight: 4
                  enabled: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Playbook'
              example:
                id: pbk_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
                name: MSA, buyer side
                description: >-
                  Master services agreement with Acme for the 2026 platform
                  rollout.
                contractType: msa
                positions:
                  limitation_of_liability:
                    standardPosition: >-
                      Liability is capped at the fees paid in the preceding
                      twelve months.
                    acceptableRange: >-
                      Between one and two times the fees paid in the preceding
                      twelve months.
                    escalationTriggers:
                      - value
                    fallbackLadder:
                      - value
                    dealBreaker: value
                    priority: must_have
                    approvalLevel: none
                    escalationConditions:
                      - attribute: clause_severity
                        operator: eq
                        value: '1000000'
                        escalateTo: manager
                        note: 'Escalated to GC: deal value over $1M.'
                    rationale: >-
                      Our standard position caps liability at fees paid in the
                      preceding 12 months.
                    riskWeight: 4
                    enabled: true
                isDefault: false
                sourceFilename: msa-acme-v3.docx
                createdAt: '2026-08-19T14:32:10Z'
                updatedAt: '2026-08-19T14:32:10Z'
        '401':
          description: >-
            The credential is missing, malformed, unknown, revoked or expired.
            `type` is `invalid-credential`, or `wrong-product-credential` when a
            `vq_key_` Data API key was sent to this API.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
            WWW-Authenticate:
              description: RFC 9110 authentication challenge.
              schema:
                type: string
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '403':
          description: >-
            The credential does not carry a scope this operation requires
            (`insufficient-scope`), or the organization's installation is
            suspended (`installation-inactive`).
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
            WWW-Authenticate:
              description: RFC 9110 authentication challenge.
              schema:
                type: string
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '404':
          description: >-
            The resource does not exist, is not this organization's, or is
            outside this installation's matter allowlist. The three are
            deliberately indistinguishable, so the status code cannot be used to
            discover which ids exist in another organization. Each resource has
            its own `type`.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '422':
          description: >-
            The request does not match the published schema. `errors` names each
            rejected field and why. The submitted value is never echoed back.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ValidationProblem'
        '429':
          description: Too many requests for this credential's tier. Honour `Retry-After`.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '500':
          description: >-
            An unexpected error on our side. The body carries a stable `type`
            and the request id and nothing else; the cause is in our logs.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '503':
          description: >-
            A dependency this request needs is unavailable, so nothing was done.
            Retryable. Authentication fails closed rather than admitting the
            request, so this is never a statement about your credential.
          headers:
            X-Request-ID:
              description: >-
                The id of this request. The same value appears as `requestId` in
                the body. Quote it when contacting support.
              schema:
                type: string
                examples:
                  - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
components:
  schemas:
    PlaybookUpdateRequest:
      properties:
        name:
          type: string
          maxLength: 200
          minLength: 1
          title: Name
          description: 'Replacement display name. Required: this is a PUT, not a patch.'
          examples:
            - MSA, buyer side
        description:
          anyOf:
            - type: string
              maxLength: 1000
            - type: 'null'
          title: Description
          description: Replacement description, or null to clear it.
          examples:
            - Master services agreement with Acme for the 2026 platform rollout.
        positions:
          additionalProperties:
            $ref: '#/components/schemas/PlaybookPosition'
          propertyNames:
            maxLength: 80
            minLength: 1
          type: object
          maxProperties: 200
          title: Positions
          description: >-
            The COMPLETE set of positions after the write. Replacement happens
            at the map level: a clause type you omit is removed from the
            playbook. Within a clause type you do send, unpublished authoring
            fields set in the web app are preserved.
      additionalProperties: false
      type: object
      required:
        - name
      title: PlaybookUpdateRequest
      description: >-
        Replace a playbook's name, description and positions.


        A PUT, and it replaces at the MAP level: a clause type absent from

        `positions` is removed from the playbook. Within a clause type that IS

        present, the five unpublished authoring fields are preserved (see

        `playbook_positions.PRESERVED_ON_WRITE`), because the alternative is
        deleting

        a lawyer's work with no error and no way to notice.


        `contractType` is absent on purpose, and so is `isDefault`. Both are

        explained in the module docstring.
    Playbook:
      properties:
        id:
          type: string
          title: Id
          description: Public identifier, `pbk_` followed by 32 hex characters.
          examples:
            - pbk_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
        name:
          type: string
          title: Name
          description: The playbook's display name.
          examples:
            - MSA, buyer side
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: Free-text description of what this playbook covers.
          examples:
            - Master services agreement with Acme for the 2026 platform rollout.
        contractType:
          type: string
          title: Contracttype
          description: >-
            Which contract type this playbook governs. Fixed at creation:
            changing it would silently redirect which reviews resolve it.
          examples:
            - msa
        positions:
          additionalProperties:
            $ref: '#/components/schemas/PlaybookPosition'
          type: object
          title: Positions
          description: >-
            Every negotiating position the playbook holds, keyed by clause-type
            slug. Returned in full, because a caller that cannot read the
            positions cannot tell what its reviews are measured against.
        isDefault:
          type: boolean
          title: Isdefault
          description: >-
            True when reviews of this contract type resolve to this playbook if
            none is named. Read-only here: exactly one default per contract type
            is enforced by the database, and this API cannot set it.
          default: false
          examples:
            - false
        sourceFilename:
          anyOf:
            - type: string
            - type: 'null'
          title: Sourcefilename
          description: >-
            Filename of the exemplar this playbook was extracted from, when it
            was imported rather than authored by hand.
          examples:
            - msa-acme-v3.docx
        createdAt:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Createdat
          description: When the playbook was created (RFC 3339).
          examples:
            - '2026-08-19T14:32:10Z'
        updatedAt:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Updatedat
          description: When the playbook was last modified (RFC 3339).
          examples:
            - '2026-08-19T14:32:10Z'
      additionalProperties: false
      type: object
      required:
        - id
        - name
        - contractType
      title: Playbook
      description: >-
        One organization playbook, with every position it holds.


        Positions are returned in full rather than summarized. A playbook is the

        input to a review, so a caller that cannot read the positions cannot
        tell

        what its reviews are being measured against, and a separate positions

        endpoint would make the common case two calls.
    Problem:
      type: object
      title: Problem
      description: >-
        An RFC 9457 problem document. Branch on `type`, which is stable; `title`
        and `detail` are written for people and may be reworded. Some problems
        carry extra members (`requiredScopes`, `limit`, `expectedVersion`),
        which is why this object is open.
      required:
        - type
        - title
        - status
        - detail
        - instance
      properties:
        type:
          type: string
          format: uri
          description: >-
            The stable identifier for this error, and the one field to branch
            on. Resolves to a page describing it.
          examples:
            - https://vaquill.ai/docs/workspace-api/errors/insufficient-scope
        title:
          type: string
          description: A short human-readable summary.
          examples:
            - Insufficient scope
        status:
          type: integer
          description: The HTTP status code, repeated.
          examples:
            - 403
        detail:
          type: string
          description: >-
            What went wrong on this specific request. May be reworded at any
            time.
          examples:
            - >-
              This credential carries matters:read. This operation needs
              matters:write.
        instance:
          type: string
          description: The path this problem occurred on.
          examples:
            - /workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
        requestId:
          type: string
          description: >-
            The id of this request, identical to the `X-Request-ID` response
            header. Quote it when contacting support.
          examples:
            - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
      additionalProperties: true
    ValidationProblem:
      type: object
      title: ValidationProblem
      description: >-
        A problem document for a schema rejection. `errors` lists the fields
        that were refused. The value you submitted is deliberately not echoed,
        so a validation failure cannot copy your content into an error response
        or into either side's logs.
      required:
        - type
        - title
        - status
        - detail
        - instance
        - errors
      properties:
        type:
          type: string
          format: uri
          description: >-
            The stable identifier for this error, and the one field to branch
            on. Resolves to a page describing it.
          examples:
            - https://vaquill.ai/docs/workspace-api/errors/insufficient-scope
        title:
          type: string
          description: A short human-readable summary.
          examples:
            - Insufficient scope
        status:
          type: integer
          description: The HTTP status code, repeated.
          examples:
            - 403
        detail:
          type: string
          description: >-
            What went wrong on this specific request. May be reworded at any
            time.
          examples:
            - >-
              This credential carries matters:read. This operation needs
              matters:write.
        instance:
          type: string
          description: The path this problem occurred on.
          examples:
            - /workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
        requestId:
          type: string
          description: >-
            The id of this request, identical to the `X-Request-ID` response
            header. Quote it when contacting support.
          examples:
            - req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6
        errors:
          type: array
          description: One entry per rejected field.
          items:
            type: object
            required:
              - location
              - message
              - type
            properties:
              location:
                type: string
                description: >-
                  Dotted path to the rejected field, for example
                  `body.contentMarkdown`.
              message:
                type: string
                description: Why it was rejected.
              type:
                type: string
                description: The validation rule that failed.
      additionalProperties: true
    PlaybookPosition:
      properties:
        standardPosition:
          type: string
          maxLength: 8000
          title: Standardposition
          description: >-
            The position the organization opens with on this clause type.
            Required: a position with no standard is not a position.
          examples:
            - >-
              Liability is capped at the fees paid in the preceding twelve
              months.
        acceptableRange:
          type: string
          maxLength: 4000
          title: Acceptablerange
          description: >-
            Free-text summary of the acceptable zone. Complements
            `fallbackLadder` rather than duplicating it: this is the summary,
            the ladder is the ordered retreat.
          examples:
            - >-
              Between one and two times the fees paid in the preceding twelve
              months.
        escalationTriggers:
          items:
            type: string
          type: array
          maxItems: 50
          title: Escalationtriggers
          description: >-
            Free-text conditions that should raise a human's attention.
            Descriptive only; the machine-evaluated rules are
            `escalationConditions`.
          examples:
            - - value
        fallbackLadder:
          items:
            type: string
          type: array
          maxItems: 10
          title: Fallbackladder
          description: >-
            Ordered concrete retreat positions, BEST acceptable first. The
            reviewer steps down this ladder when the standard position is
            rejected.
          examples:
            - - value
        dealBreaker:
          anyOf:
            - type: string
              maxLength: 4000
            - type: 'null'
          title: Dealbreaker
          description: >-
            The walk-away floor. A term at or below this is forced to red
            regardless of what the model reads. Absent means there is no hard
            floor, which is not the same as a floor of nothing.
          examples:
            - Any uncapped liability for indirect or consequential damages.
        priority:
          anyOf:
            - type: string
              enum:
                - must_have
                - should_have
                - nice_to_have
            - type: 'null'
          title: Priority
          description: How much this clause matters relative to the others in the playbook.
          examples:
            - must_have
        approvalLevel:
          anyOf:
            - type: string
              enum:
                - none
                - manager
                - partner
                - gc
            - type: 'null'
          title: Approvallevel
          description: >-
            The sign-off a deviation from this position needs. Feeds a review's
            `approvalGate`, which is REPORTED and never enforced.
          examples:
            - none
        escalationConditions:
          items:
            $ref: '#/components/schemas/EscalationCondition'
          type: array
          maxItems: 20
          title: Escalationconditions
          description: >-
            Machine-evaluated rules that raise the required sign-off when they
            hold.
        rationale:
          anyOf:
            - type: string
              maxLength: 2000
            - type: 'null'
          title: Rationale
          description: >-
            Why the organization takes this position. Carried into review
            findings so a reviewer can see the reasoning.
          examples:
            - >-
              Our standard position caps liability at fees paid in the preceding
              12 months.
        riskWeight:
          anyOf:
            - type: integer
              maximum: 5
              minimum: 0
            - type: 'null'
          title: Riskweight
          description: >-
            0 to 5, multiplied by severity to weight the compliance score.
            Absent falls through to the clause-type default rather than to zero.
          examples:
            - 4
        enabled:
          type: boolean
          title: Enabled
          description: >-
            False keeps the position on the playbook but excludes it from review
            and drafting. This is how an alternative is parked without being
            deleted.
          default: true
          examples:
            - true
      additionalProperties: false
      type: object
      required:
        - standardPosition
        - acceptableRange
      title: PlaybookPosition
      description: >-
        The organization's negotiating position on one clause type.


        Both a request and a response shape, which is a deliberate exception to
        the

        "read models are looser than write models" rule the resources follow. A

        position is authored by the customer rather than produced by a model, so

        there is no dirty-legacy-value problem to be loose about, and one shape
        means

        a caller can read a playbook, edit one field and write it straight back.
    EscalationCondition:
      properties:
        attribute:
          type: string
          enum:
            - clause_severity
            - counterparty_paper
            - contract_value
            - governing_law
          title: Attribute
          description: >-
            What to test. `clause_severity` is the review's own finding and
            needs no input; the other three come from the review request's
            `paperSide` and `dealContext`. A rule naming one you did not supply
            simply does not fire.
          examples:
            - clause_severity
        operator:
          type: string
          enum:
            - eq
            - neq
            - gt
            - gte
            - lt
            - lte
            - in
          title: Operator
          description: >-
            How to compare `attribute` against `value`. Use `in` with a
            comma-separated `value`.
          default: eq
          examples:
            - eq
        value:
          type: string
          maxLength: 200
          title: Value
          description: >-
            What to compare against, always a string and parsed per attribute:
            `red`, `true`, `1000000`, `CA,NY`.
          examples:
            - '1000000'
        escalateTo:
          type: string
          enum:
            - manager
            - partner
            - gc
          title: Escalateto
          description: The sign-off level this rule raises the clause to when it fires.
          default: partner
          examples:
            - manager
        note:
          anyOf:
            - type: string
              maxLength: 280
            - type: 'null'
          title: Note
          description: >-
            Short explanation of why this escalation exists, shown to whoever
            reviews the finding.
          examples:
            - 'Escalated to GC: deal value over $1M.'
      additionalProperties: false
      type: object
      required:
        - attribute
        - value
      title: EscalationCondition
      description: >-
        A rule that RAISES a clause's required sign-off when it holds.


        This is what lets a playbook say "partner normally, but GC if the deal
        is

        over a million, or if it is on their paper". The value stays a string
        across

        every attribute so the stored shape is uniform; it is parsed per
        attribute

        when the rule is evaluated.
  securitySchemes:
    WorkspaceAuth:
      type: http
      scheme: bearer
      bearerFormat: vq_ws_*
      description: >-
        Workspace credential issued from the automation console at
        `/automation`. Send it as `Authorization: Bearer vq_ws_...`. This is NOT
        a Data API key: a `vq_key_` credential is refused here and names the
        other product in the error.

````