{
  "openapi": "3.0.3",
  "info": {
    "title": "Knowledge Base API",
    "version": "1.0.0",
    "description": "The Versus Knowledge Base / Wiki \u2014 spaces, pages, content, and collaboration."
  },
  "servers": [
    {
      "url": "https://versus-demo.com"
    }
  ],
  "paths": {
    "/api/kb/agents/": {
      "get": {
        "operationId": "kb_agents_list",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbAgentList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List agents"
      },
      "post": {
        "operationId": "kb_agents_create",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create agent"
      }
    },
    "/api/kb/agents/{key}/": {
      "get": {
        "operationId": "kb_agents_retrieve",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve agent"
      },
      "put": {
        "operationId": "kb_agents_update",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update agent"
      },
      "patch": {
        "operationId": "kb_agents_partial_update",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbAgentRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update agent"
      },
      "delete": {
        "operationId": "kb_agents_destroy",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete agent"
      }
    },
    "/api/kb/agents/{key}/act/": {
      "post": {
        "operationId": "kb_agents_act_create",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Act: agent"
      }
    },
    "/api/kb/agents/{key}/actions/": {
      "get": {
        "operationId": "kb_agents_actions_retrieve",
        "description": "GET the agent's enabled actions, or POST to REPLACE them with a list of\n``{action, config, require_confirm}`` items (the allow-list act/ enforces).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Actions: agent"
      },
      "post": {
        "operationId": "kb_agents_actions_create",
        "description": "GET the agent's enabled actions, or POST to REPLACE them with a list of\n``{action, config, require_confirm}`` items (the allow-list act/ enforces).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Actions: agent"
      }
    },
    "/api/kb/agents/{key}/ask/": {
      "post": {
        "operationId": "kb_agents_ask_create",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Ask: agent"
      }
    },
    "/api/kb/agents/{key}/asks/": {
      "get": {
        "operationId": "kb_agents_asks_retrieve",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Asks: agent"
      }
    },
    "/api/kb/agents/{key}/asks/{ask_id}/feedback/": {
      "post": {
        "operationId": "kb_agents_asks_feedback_create",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "ask_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]+$"
            },
            "required": true
          },
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Feedback: ask"
      }
    },
    "/api/kb/agents/{key}/chat/": {
      "post": {
        "operationId": "kb_agents_chat_create",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Chat: agent"
      }
    },
    "/api/kb/agents/{key}/preview/": {
      "post": {
        "operationId": "kb_agents_preview_create",
        "description": "CRUD + operations for Wiki Agents. Looked up by ``key`` (unique per tenant)\nso routes read ``agents/<key>/ask`` etc., mirroring the spec (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Preview: agent"
      }
    },
    "/api/kb/agents/{key}/scope/": {
      "get": {
        "operationId": "kb_agents_scope_retrieve",
        "description": "GET the agent's scope rows, or POST to REPLACE them with a list of\n``{kind, space?|label?|page?, mode}`` items.",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Scope: agent"
      },
      "post": {
        "operationId": "kb_agents_scope_create",
        "description": "GET the agent's scope rows, or POST to REPLACE them with a list of\n``{kind, space?|label?|page?, mode}`` items.",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbAgentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbAgent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Scope: agent"
      }
    },
    "/api/kb/assist/{request_id}/apply/": {
      "post": {
        "operationId": "kb_assist_apply_create",
        "description": "POST /api/kb/assist/<request_id>/apply \u2014 record that the client accepted &\nmerged the patch (the merge itself rides the existing draft PUT). Idempotent\naudit/provenance only; nothing new touches KbPageVersion until publish.",
        "parameters": [
          {
            "in": "path",
            "name": "request_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Apply: assist"
      }
    },
    "/api/kb/assist/{request_id}/discard/": {
      "post": {
        "operationId": "kb_assist_discard_create",
        "description": "POST /api/kb/assist/<request_id>/discard \u2014 user rejected the suggestion.\nPer \u00a715 the full body is dropped (provenance hash/metadata retained).",
        "parameters": [
          {
            "in": "path",
            "name": "request_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Discard: assist"
      }
    },
    "/api/kb/assist/{request_id}/feedback/": {
      "post": {
        "operationId": "kb_assist_feedback_create",
        "description": "POST /api/kb/assist/<request_id>/feedback \u2014 {rating, reason} (P2).",
        "parameters": [
          {
            "in": "path",
            "name": "request_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Feedback: assist"
      }
    },
    "/api/kb/assist/augment/": {
      "post": {
        "operationId": "kb_assist_augment_create",
        "description": "POST /api/kb/assist/augment/ \u2014 page-aware AI authoring. Grounds on the CURRENT\npage (+ optional images via vision + RAG across the space) and returns NEW blocks to\nappend, or (``mode='diagram'``) a generated diagram as an image block.\n\nBody: {page?, prompt, mode: 'write'|'diagram', images?: [dataURL], grounded?: bool}.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create augment"
      }
    },
    "/api/kb/assist/compose/": {
      "post": {
        "operationId": "kb_assist_compose_create",
        "description": "POST /api/kb/assist/compose \u2014 generate a whole page from a prompt\n(+ optional template / grounding / context refs) as native block-JSON.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create compose"
      }
    },
    "/api/kb/assist/ops/": {
      "get": {
        "operationId": "kb_assist_ops_retrieve",
        "description": "GET /api/kb/assist/ops \u2014 the reshape op catalog (server-driven menu).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List ops"
      }
    },
    "/api/kb/assist/reshape/": {
      "post": {
        "operationId": "kb_assist_reshape_create",
        "description": "POST /api/kb/assist/reshape \u2014 transform a selection of blocks; returns\nreplacement blocks + a replace patch over the selected range.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create reshape"
      }
    },
    "/api/kb/assist/stream/{request_id}/": {
      "get": {
        "operationId": "kb_assist_stream_retrieve",
        "description": "GET /api/kb/assist/stream/<request_id> \u2014 SSE stream for an in-flight\nrequest (token/block/done/error events, \u00a78).",
        "parameters": [
          {
            "in": "path",
            "name": "request_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Retrieve stream"
      }
    },
    "/api/kb/attachments/{attach_id}/": {
      "get": {
        "operationId": "kb_attachments_retrieve",
        "description": "GET  /api/kb/attachments/<uuid:attach_id>/ \u2014 proxy-stream the current bytes\n      from the store (or the local blob), page-restriction enforced (spec \u00a73/\u00a79).\nDELETE \u2014 soft-delete + best-effort object reclaim (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "attach_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Retrieve attachment"
      },
      "delete": {
        "operationId": "kb_attachments_destroy",
        "description": "GET  /api/kb/attachments/<uuid:attach_id>/ \u2014 proxy-stream the current bytes\n      from the store (or the local blob), page-restriction enforced (spec \u00a73/\u00a79).\nDELETE \u2014 soft-delete + best-effort object reclaim (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "attach_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete attachment"
      }
    },
    "/api/kb/attachments/{attach_id}/preview/": {
      "get": {
        "operationId": "kb_attachments_preview_retrieve",
        "description": "GET /api/kb/attachments/<uuid:attach_id>/thumb/ | /preview/ \u2014 lazily\ngenerated, cached derived image (spec \u00a75/\u00a76/\u00a715).",
        "parameters": [
          {
            "in": "path",
            "name": "attach_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Preview: attachment"
      }
    },
    "/api/kb/attachments/{attach_id}/restore/": {
      "post": {
        "operationId": "kb_attachments_restore_create",
        "description": "POST /api/kb/attachments/<uuid:attach_id>/restore/ {number} \u2014 promote an old\nversion to current (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "attach_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Restore: attachment"
      }
    },
    "/api/kb/attachments/{attach_id}/thumb/": {
      "get": {
        "operationId": "kb_attachments_thumb_retrieve",
        "description": "GET /api/kb/attachments/<uuid:attach_id>/thumb/ | /preview/ \u2014 lazily\ngenerated, cached derived image (spec \u00a75/\u00a76/\u00a715).",
        "parameters": [
          {
            "in": "path",
            "name": "attach_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Thumb: attachment"
      }
    },
    "/api/kb/attachments/{attach_id}/versions/": {
      "get": {
        "operationId": "kb_attachments_versions_retrieve",
        "description": "GET /api/kb/attachments/<uuid:attach_id>/versions/ \u2014 list versions (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "attach_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Versions: attachment"
      }
    },
    "/api/kb/attachments/{attach_id}/versions/{number}/": {
      "get": {
        "operationId": "kb_attachments_versions_retrieve_2",
        "description": "GET /api/kb/attachments/<uuid:attach_id>/versions/<int:number>/ \u2014 download\na specific version's bytes (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "attach_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          },
          {
            "in": "path",
            "name": "number",
            "schema": {
              "type": "integer"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Retrieve version"
      }
    },
    "/api/kb/clips/": {
      "get": {
        "operationId": "kb_clips_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbClipList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List clips"
      },
      "post": {
        "operationId": "kb_clips_create",
        "description": "POST /api/kb/clips/ \u2014 create a clip + open its resumable upload session\n(spec \u00a77). Returns {clip_id, clip, upload}.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create clip"
      }
    },
    "/api/kb/clips/{clip_id}/captions": {
      "get": {
        "operationId": "kb_clips_captions_retrieve",
        "description": "GET /api/kb/clips/<uuid:clip_id>/captions \u2014 the transcript as text/vtt (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "clip_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Captions: clip"
      }
    },
    "/api/kb/clips/{clip_id}/poster": {
      "get": {
        "operationId": "kb_clips_poster_retrieve",
        "description": "GET /api/kb/clips/<uuid:clip_id>/poster \u2014 the poster JPEG (DB blob).",
        "parameters": [
          {
            "in": "path",
            "name": "clip_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Poster: clip"
      }
    },
    "/api/kb/clips/{clip_id}/stream": {
      "get": {
        "operationId": "kb_clips_stream_retrieve",
        "description": "GET /api/kb/clips/<uuid:clip_id>/stream \u2014 range-aware byte serving (spec \u00a77).\nHonours ``Range:`` \u2192 206 Partial Content with Content-Range / Accept-Ranges;\na full request gets 200 + Accept-Ranges. Same-origin proxy \u2014 no bucket CORS.",
        "parameters": [
          {
            "in": "path",
            "name": "clip_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Stream: clip"
      }
    },
    "/api/kb/clips/{id}/": {
      "get": {
        "operationId": "kb_clips_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve clip"
      },
      "put": {
        "operationId": "kb_clips_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update clip"
      },
      "patch": {
        "operationId": "kb_clips_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update clip"
      },
      "delete": {
        "operationId": "kb_clips_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete clip"
      }
    },
    "/api/kb/clips/{id}/abort/": {
      "post": {
        "operationId": "kb_clips_abort_create",
        "description": "POST /api/kb/clips/<id>/abort/ \u2014 abort multipart, mark failed (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Abort: clip"
      }
    },
    "/api/kb/clips/{id}/complete/": {
      "post": {
        "operationId": "kb_clips_complete_create",
        "description": "POST /api/kb/clips/<id>/complete/ \u2014 finalise + flip to ready (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Complete: clip"
      }
    },
    "/api/kb/clips/{id}/part/": {
      "put": {
        "operationId": "kb_clips_part_update",
        "description": "PUT /api/kb/clips/<id>/part/?n=<k> \u2014 upload chunk k (raw body). Idempotent\nby part number; resumable from bytes_received (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Part: clip"
      }
    },
    "/api/kb/clips/{id}/poster/": {
      "post": {
        "operationId": "kb_clips_poster_create",
        "description": "POST /api/kb/clips/<id>/poster/ \u2014 store the client-captured JPEG poster\n(multipart 'poster' file or raw body); < 256 KB DB blob (spec \u00a74/\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Poster: clip"
      }
    },
    "/api/kb/clips/{id}/transcribe/": {
      "post": {
        "operationId": "kb_clips_transcribe_create",
        "description": "POST /api/kb/clips/<id>/transcribe/ \u2014 kick the optional AI transcript\n(spec \u00a76); async-lite, polled. 501 when no audio\u2192text provider exists.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb clip.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Transcribe: clip"
      }
    },
    "/api/kb/clips/from-session/{session_id}/": {
      "post": {
        "operationId": "kb_clips_from_session_create",
        "description": "POST /api/kb/clips/from-session/<session_id>/ \u2014 NEW seam: PSM RDP/HTTPS\ngateway handoff. Registers a source='psm' clip + upload session (spec \u00a76/\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbClip"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create from session"
      }
    },
    "/api/kb/comments/": {
      "get": {
        "operationId": "kb_comments_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbPageCommentList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List comments"
      },
      "post": {
        "operationId": "kb_comments_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPageComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create comment"
      }
    },
    "/api/kb/comments/{id}/": {
      "get": {
        "operationId": "kb_comments_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPageComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve comment"
      },
      "put": {
        "operationId": "kb_comments_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPageComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update comment"
      },
      "patch": {
        "operationId": "kb_comments_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbPageCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbPageCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbPageCommentRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPageComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update comment"
      },
      "delete": {
        "operationId": "kb_comments_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete comment"
      }
    },
    "/api/kb/datasets/": {
      "get": {
        "operationId": "kb_datasets_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbDatasetList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List datasets"
      },
      "post": {
        "operationId": "kb_datasets_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create dataset"
      }
    },
    "/api/kb/datasets/{id}/": {
      "get": {
        "operationId": "kb_datasets_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDatasetDetail"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve dataset"
      },
      "put": {
        "operationId": "kb_datasets_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update dataset"
      },
      "patch": {
        "operationId": "kb_datasets_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update dataset"
      },
      "delete": {
        "operationId": "kb_datasets_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete dataset"
      }
    },
    "/api/kb/datasets/{id}/export/": {
      "get": {
        "operationId": "kb_datasets_export_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Export: dataset"
      }
    },
    "/api/kb/datasets/{id}/fields/": {
      "get": {
        "operationId": "kb_datasets_fields_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Fields: dataset"
      },
      "post": {
        "operationId": "kb_datasets_fields_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Fields: dataset"
      }
    },
    "/api/kb/datasets/{id}/fields/{field_id}/": {
      "patch": {
        "operationId": "kb_datasets_fields_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "field_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          },
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update field"
      },
      "delete": {
        "operationId": "kb_datasets_fields_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "field_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          },
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete field"
      }
    },
    "/api/kb/datasets/{id}/fields/{field_id}/retype/": {
      "post": {
        "operationId": "kb_datasets_fields_retype_create",
        "description": "Change a field's type and run a best-effort cell coercion pass (\u00a74.4/\u00a75#13).",
        "parameters": [
          {
            "in": "path",
            "name": "field_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          },
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retype: field"
      }
    },
    "/api/kb/datasets/{id}/fields/reorder/": {
      "post": {
        "operationId": "kb_datasets_fields_reorder_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Reorder: field"
      }
    },
    "/api/kb/datasets/{id}/import/": {
      "post": {
        "operationId": "kb_datasets_import_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Import: dataset"
      }
    },
    "/api/kb/datasets/{id}/resolve/": {
      "get": {
        "operationId": "kb_datasets_resolve_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Resolve: dataset"
      }
    },
    "/api/kb/datasets/{id}/rows/": {
      "get": {
        "operationId": "kb_datasets_rows_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Rows: dataset"
      },
      "post": {
        "operationId": "kb_datasets_rows_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Rows: dataset"
      }
    },
    "/api/kb/datasets/{id}/rows/{row_id}/": {
      "patch": {
        "operationId": "kb_datasets_rows_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          },
          {
            "in": "path",
            "name": "row_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update row"
      },
      "delete": {
        "operationId": "kb_datasets_rows_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          },
          {
            "in": "path",
            "name": "row_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete row"
      }
    },
    "/api/kb/datasets/{id}/rows/bulk/": {
      "post": {
        "operationId": "kb_datasets_rows_bulk_create",
        "description": "Batch upsert (import/paste). Items: ``{id?, cells, row_order?}`` \u2014\nlast-writer-wins (no per-row version check on bulk). (spec \u00a77)",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Bulk: row"
      }
    },
    "/api/kb/datasets/{id}/subscribe/": {
      "post": {
        "operationId": "kb_datasets_subscribe_create",
        "description": "Toggle a subscription to this dataset (spec \u00a76.1) \u2014 reuses the KbWatch\ngrain with target_type='dataset'; subscribers are notified on changes.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Subscribe: dataset"
      }
    },
    "/api/kb/datasets/{id}/views/": {
      "get": {
        "operationId": "kb_datasets_views_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Views: dataset"
      },
      "post": {
        "operationId": "kb_datasets_views_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Views: dataset"
      }
    },
    "/api/kb/datasets/{id}/views/{view_id}/": {
      "patch": {
        "operationId": "kb_datasets_views_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          },
          {
            "in": "path",
            "name": "view_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbDatasetRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update view"
      },
      "delete": {
        "operationId": "kb_datasets_views_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          },
          {
            "in": "path",
            "name": "view_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete view"
      }
    },
    "/api/kb/datasets/{id}/views/{view_id}/duplicate/": {
      "post": {
        "operationId": "kb_datasets_views_duplicate_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb dataset.",
            "required": true
          },
          {
            "in": "path",
            "name": "view_id",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F-]{36}$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbDatasetRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbDataset"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Duplicate: view"
      }
    },
    "/api/kb/diagram-sources/": {
      "get": {
        "operationId": "kb_diagram_sources_retrieve",
        "description": "GET /api/kb/diagram-sources/?type=&q= \u2014 list selectable objects for the\n\"Insert from Versus\" picker. Without ``?type`` it returns the source-type menu;\nwith a type it returns matching objects ``[{id,name,subtitle}]`` (tenant-scoped).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List diagram sources"
      }
    },
    "/api/kb/events/": {
      "get": {
        "operationId": "kb_events_list",
        "description": "kb/events/<id>/ \u2014 PATCH/DELETE a native event; override a single\noccurrence. Creation is via kb/timelines/<id>/events (POST), but a direct\ncreate with an explicit ``timeline`` is also accepted.",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedTimelineEventList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List events"
      },
      "post": {
        "operationId": "kb_events_create",
        "description": "kb/events/<id>/ \u2014 PATCH/DELETE a native event; override a single\noccurrence. Creation is via kb/timelines/<id>/events (POST), but a direct\ncreate with an explicit ``timeline`` is also accepted.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimelineEvent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create event"
      }
    },
    "/api/kb/events/{id}/": {
      "get": {
        "operationId": "kb_events_retrieve",
        "description": "kb/events/<id>/ \u2014 PATCH/DELETE a native event; override a single\noccurrence. Creation is via kb/timelines/<id>/events (POST), but a direct\ncreate with an explicit ``timeline`` is also accepted.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline event.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimelineEvent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve event"
      },
      "put": {
        "operationId": "kb_events_update",
        "description": "Optimistic concurrency (spec \u00a77/\u00a79): a stale base_updated_at returns 409\nwith the server's current state to merge, matching the page publish path.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline event.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimelineEvent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update event"
      },
      "patch": {
        "operationId": "kb_events_partial_update",
        "description": "kb/events/<id>/ \u2014 PATCH/DELETE a native event; override a single\noccurrence. Creation is via kb/timelines/<id>/events (POST), but a direct\ncreate with an explicit ``timeline`` is also accepted.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline event.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineEventRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineEventRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineEventRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimelineEvent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update event"
      },
      "delete": {
        "operationId": "kb_events_destroy",
        "description": "kb/events/<id>/ \u2014 PATCH/DELETE a native event; override a single\noccurrence. Creation is via kb/timelines/<id>/events (POST), but a direct\ncreate with an explicit ``timeline`` is also accepted.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline event.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete event"
      }
    },
    "/api/kb/events/{id}/override/": {
      "post": {
        "operationId": "kb_events_override_create",
        "description": "Create/patch/cancel a single recurrence occurrence (spec \u00a77,\nTimelineEventOverride). Body: {occurrence_start, cancelled?, patch?}.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline event.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineEventRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimelineEvent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Override: event"
      },
      "patch": {
        "operationId": "kb_events_override_partial_update",
        "description": "Create/patch/cancel a single recurrence occurrence (spec \u00a77,\nTimelineEventOverride). Body: {occurrence_start, cancelled?, patch?}.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline event.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineEventRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineEventRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineEventRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimelineEvent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Override: event"
      },
      "delete": {
        "operationId": "kb_events_override_destroy",
        "description": "Create/patch/cancel a single recurrence occurrence (spec \u00a77,\nTimelineEventOverride). Body: {occurrence_start, cancelled?, patch?}.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline event.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Override: event"
      }
    },
    "/api/kb/find/": {
      "get": {
        "operationId": "kb_find_retrieve",
        "description": "GET /api/kb/find/ \u2014 ranked, faceted, snippeted search (spec \u00a77).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List finds"
      }
    },
    "/api/kb/find/reindex/": {
      "post": {
        "operationId": "kb_find_reindex_create",
        "description": "POST /api/kb/find/reindex/ \u2014 rebuild the index for a space or the tenant\n(admin/flag). Backs the ``kb_reindex`` backfill (spec \u00a77/\u00a710).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create reindex"
      }
    },
    "/api/kb/find/suggest/": {
      "get": {
        "operationId": "kb_find_suggest_retrieve",
        "description": "GET /api/kb/find/suggest/ \u2014 type-ahead titles, \u22648 rows (spec \u00a73 #9).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List suggests"
      }
    },
    "/api/kb/inline-comments/": {
      "get": {
        "operationId": "kb_inline_comments_list",
        "description": "``/api/kb/inline-comments/`` \u2014 create an anchored comment from a selection,\nlist anchored roots for the rail (``?page=&anchored=1``) or the Unanchored\ntray (``?status=orphaned``), reply, resolve/reopen, and convert a thread to a\nDesk Issue (\u00a76/\u00a77). Operates on the shipped ``KbPageComment`` model.",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbInlineCommentList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List inline comments"
      },
      "post": {
        "operationId": "kb_inline_comments_create",
        "description": "Create a comment. When an ``anchor`` object is supplied on a root\ncomment, the server re-derives + validates its offsets against the target\nversion's plain-text projection (\u00a75/\u00a77); an un-locatable selection is a\n400. Replies (``parent`` set) inherit no anchor.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create inline comment"
      }
    },
    "/api/kb/inline-comments/{id}/": {
      "get": {
        "operationId": "kb_inline_comments_retrieve",
        "description": "``/api/kb/inline-comments/`` \u2014 create an anchored comment from a selection,\nlist anchored roots for the rail (``?page=&anchored=1``) or the Unanchored\ntray (``?status=orphaned``), reply, resolve/reopen, and convert a thread to a\nDesk Issue (\u00a76/\u00a77). Operates on the shipped ``KbPageComment`` model.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve inline comment"
      },
      "put": {
        "operationId": "kb_inline_comments_update",
        "description": "``/api/kb/inline-comments/`` \u2014 create an anchored comment from a selection,\nlist anchored roots for the rail (``?page=&anchored=1``) or the Unanchored\ntray (``?status=orphaned``), reply, resolve/reopen, and convert a thread to a\nDesk Issue (\u00a76/\u00a77). Operates on the shipped ``KbPageComment`` model.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update inline comment"
      },
      "patch": {
        "operationId": "kb_inline_comments_partial_update",
        "description": "``/api/kb/inline-comments/`` \u2014 create an anchored comment from a selection,\nlist anchored roots for the rail (``?page=&anchored=1``) or the Unanchored\ntray (``?status=orphaned``), reply, resolve/reopen, and convert a thread to a\nDesk Issue (\u00a76/\u00a77). Operates on the shipped ``KbPageComment`` model.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbInlineCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbInlineCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbInlineCommentRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update inline comment"
      },
      "delete": {
        "operationId": "kb_inline_comments_destroy",
        "description": "``/api/kb/inline-comments/`` \u2014 create an anchored comment from a selection,\nlist anchored roots for the rail (``?page=&anchored=1``) or the Unanchored\ntray (``?status=orphaned``), reply, resolve/reopen, and convert a thread to a\nDesk Issue (\u00a76/\u00a77). Operates on the shipped ``KbPageComment`` model.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete inline comment"
      }
    },
    "/api/kb/inline-comments/{id}/convert_to_issue/": {
      "post": {
        "operationId": "kb_inline_comments_convert_to_issue_create",
        "description": "Promote an inline thread to a Desk Issue (\u00a76). Body ``{project,\nissue_type?}``. Seeds the issue description with the anchor quote + a deep\nlink, stores the key back on the thread, and appends it to\n``KbPage.issue_links`` (the same append ``link_issue`` performs).\nIdempotent \u2014 a thread already carrying a key returns it.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Convert to issue: inline comment"
      }
    },
    "/api/kb/inline-comments/{id}/reopen/": {
      "post": {
        "operationId": "kb_inline_comments_reopen_create",
        "description": "``/api/kb/inline-comments/`` \u2014 create an anchored comment from a selection,\nlist anchored roots for the rail (``?page=&anchored=1``) or the Unanchored\ntray (``?status=orphaned``), reply, resolve/reopen, and convert a thread to a\nDesk Issue (\u00a76/\u00a77). Operates on the shipped ``KbPageComment`` model.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Reopen: inline comment"
      }
    },
    "/api/kb/inline-comments/{id}/resolve/": {
      "post": {
        "operationId": "kb_inline_comments_resolve_create",
        "description": "``/api/kb/inline-comments/`` \u2014 create an anchored comment from a selection,\nlist anchored roots for the rail (``?page=&anchored=1``) or the Unanchored\ntray (``?status=orphaned``), reply, resolve/reopen, and convert a thread to a\nDesk Issue (\u00a76/\u00a77). Operates on the shipped ``KbPageComment`` model.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page comment.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Resolve: inline comment"
      }
    },
    "/api/kb/inline-comments/reanchor/": {
      "post": {
        "operationId": "kb_inline_comments_reanchor_create",
        "description": "Re-anchor every open root inline thread on a page against its CURRENT\nversion (\u00a75). Normally this runs transactionally inside the page save path\n(``kb_service.save_page`` \u2014 wired by the integrator, see manifest); this\nendpoint exposes the same engine for manual/backfill re-anchoring without\nediting the shipped save path. Requires page edit permission.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbInlineCommentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbInlineComment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create reanchor"
      }
    },
    "/api/kb/insights/overview/": {
      "get": {
        "operationId": "kb_insights_overview_retrieve",
        "description": "GET /api/kb/insights/overview/ \u2014 tenant-wide rollup across spaces (admin)\n(spec \u00a77).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List overviews"
      }
    },
    "/api/kb/insights/rollup/": {
      "post": {
        "operationId": "kb_insights_rollup_create",
        "description": "POST /api/kb/insights/rollup/ \u2014 run the analytics rollup + popularity\nrecompute for the tenant (admin). Operability shim for the nightly cadence\nwhen no management command is wired (spec \u00a715).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create rollup"
      }
    },
    "/api/kb/labels/": {
      "get": {
        "operationId": "kb_labels_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbLabelList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List labels"
      },
      "post": {
        "operationId": "kb_labels_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create label"
      }
    },
    "/api/kb/labels/{slug}/": {
      "get": {
        "operationId": "kb_labels_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve label"
      },
      "patch": {
        "operationId": "kb_labels_partial_update",
        "description": "PATCH label metadata (description / color / kind / target_ref). Admin\nonly; ``slug``/``display``/``usage_count`` are read-only (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbLabelRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbLabelRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbLabelRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update label"
      },
      "delete": {
        "operationId": "kb_labels_destroy",
        "description": "DELETE a label \u2014 strips it from every page that carries it, then removes\nthe catalog row (admin, \u00a77/\u00a79).",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete label"
      }
    },
    "/api/kb/labels/{slug}/issues/": {
      "get": {
        "operationId": "kb_labels_issues_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Issues: label"
      }
    },
    "/api/kb/labels/{slug}/pages/": {
      "get": {
        "operationId": "kb_labels_pages_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Pages: label"
      }
    },
    "/api/kb/labels/{slug}/related/": {
      "get": {
        "operationId": "kb_labels_related_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Related: label"
      }
    },
    "/api/kb/labels/{slug}/rename/": {
      "post": {
        "operationId": "kb_labels_rename_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "schema": {
              "type": "string",
              "pattern": "^[^/]+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Rename: label"
      }
    },
    "/api/kb/labels/merge/": {
      "post": {
        "operationId": "kb_labels_merge_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbLabelRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create merge"
      }
    },
    "/api/kb/labels/popular/": {
      "get": {
        "operationId": "kb_labels_popular_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbLabel"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List populars"
      }
    },
    "/api/kb/oncall/rotations/": {
      "get": {
        "operationId": "kb_oncall_rotations_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedOncallRotationList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List rotations"
      },
      "post": {
        "operationId": "kb_oncall_rotations_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OncallRotationRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OncallRotationRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/OncallRotationRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OncallRotation"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create rotation"
      }
    },
    "/api/kb/oncall/rotations/{id}/": {
      "get": {
        "operationId": "kb_oncall_rotations_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this oncall rotation.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OncallRotation"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve rotation"
      },
      "put": {
        "operationId": "kb_oncall_rotations_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this oncall rotation.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OncallRotationRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OncallRotationRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/OncallRotationRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OncallRotation"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update rotation"
      },
      "patch": {
        "operationId": "kb_oncall_rotations_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this oncall rotation.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedOncallRotationRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedOncallRotationRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedOncallRotationRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OncallRotation"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update rotation"
      },
      "delete": {
        "operationId": "kb_oncall_rotations_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this oncall rotation.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete rotation"
      }
    },
    "/api/kb/oncall/rotations/{id}/whois/": {
      "get": {
        "operationId": "kb_oncall_rotations_whois_retrieve",
        "description": "Current (or ?at=) on-call member for this rotation (spec \u00a76).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this oncall rotation.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OncallRotation"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Whois: rotation"
      }
    },
    "/api/kb/pages/": {
      "get": {
        "operationId": "kb_pages_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbPageList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List pages"
      },
      "post": {
        "operationId": "kb_pages_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create page"
      }
    },
    "/api/kb/pages/{page_id}/ai-diagram/": {
      "post": {
        "operationId": "kb_pages_ai_diagram_create",
        "description": "POST /api/kb/pages/<uuid:page_id>/ai-diagram/ {prompt} \u2014 turn a natural-\nlanguage request into a diagram. The model only PLANS what to draw (source type\n+ a search query); every node still comes from real inventory via the trusted\nbuilders, so it can't invent devices/IPs. Returns {nodes, edges, drawn, title,\nexplanation} to merge into the board. Edit-gated.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Ai diagram: page"
      }
    },
    "/api/kb/pages/{page_id}/annotations/": {
      "get": {
        "operationId": "kb_pages_annotations_retrieve",
        "description": "``GET /api/kb/pages/<id>/annotations/`` \u2192 the active anchor set for the\ncurrent version: ``[{thread_id, block, start, end, resolved}]`` \u2014 the input to\nthe highlight-injection render path (\u00a77). Orphaned/page-level threads are\nexcluded (they live in the Unanchored tray).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Annotations: page"
      }
    },
    "/api/kb/pages/{page_id}/canvas-draft/": {
      "put": {
        "operationId": "kb_pages_canvas_draft_update",
        "description": "PUT /api/kb/pages/<uuid:page_id>/canvas-draft/ \u2014 element-delta autosave (\u00a77).\n\nAdditive companion to the existing ``pages/<id>/draft/`` endpoint (which we do\nnot modify): accepts ``{ops:[...]}`` folded last-writer-wins per element into\n``draft_body`` (two authors editing different nodes don't clobber each other),\nor a full ``{body}`` PUT. Returns the merged, sanitised scene. Requires edit\npermission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Canvas draft: page"
      }
    },
    "/api/kb/pages/{page_id}/canvas.png": {
      "get": {
        "operationId": "kb_pages_canvas.png_retrieve",
        "description": "GET /api/kb/pages/<uuid:page_id>/canvas.png (?fmt=pdf) \u2014 rasterised export (\u00a77).\n\nUses the cached KbCanvasThumb for the current version when possible; otherwise\nrasterises on the fly. When no SVG rasteriser is installed the endpoint falls\nback to serving the inert SVG (still a valid image), so export never hard-fails.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Canvas.png: page"
      }
    },
    "/api/kb/pages/{page_id}/canvas.svg": {
      "get": {
        "operationId": "kb_pages_canvas.svg_retrieve",
        "description": "GET /api/kb/pages/<uuid:page_id>/canvas.svg \u2014 inert server-rendered SVG (\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Canvas.svg: page"
      }
    },
    "/api/kb/pages/{page_id}/deck/": {
      "get": {
        "operationId": "kb_pages_deck_retrieve",
        "description": "``GET  /api/kb/pages/<id>/deck/``  \u2192 normalized deck payload (spec \u00a77):\n``{page, meta, slides:[{id,layout,transition,background,rendered_html,\nnotes_html,smartblock_refs}]}``. ``notes_html`` is populated only for\nedit/present-capable callers.\n\n``POST /api/kb/pages/<id>/deck/`` \u2192 update the deck's version-independent\n``content_meta`` (theme / aspect / default_transition), allow-listed.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Deck: page"
      },
      "post": {
        "operationId": "kb_pages_deck_create",
        "description": "``GET  /api/kb/pages/<id>/deck/``  \u2192 normalized deck payload (spec \u00a77):\n``{page, meta, slides:[{id,layout,transition,background,rendered_html,\nnotes_html,smartblock_refs}]}``. ``notes_html`` is populated only for\nedit/present-capable callers.\n\n``POST /api/kb/pages/<id>/deck/`` \u2192 update the deck's version-independent\n``content_meta`` (theme / aspect / default_transition), allow-listed.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Deck: page"
      }
    },
    "/api/kb/pages/{page_id}/deck/export/": {
      "get": {
        "operationId": "kb_pages_deck_export_retrieve",
        "description": "``GET /api/kb/pages/<id>/deck/export/?fmt=html|pdf|png[&notes=1][&persist=1]``\n(spec \u00a77). ``html`` = self-contained offline deck (buildable now). ``pdf``/\n``png`` require the NEW headless renderer (Playwright); when it is absent the\nendpoint returns 501 with a clear message rather than failing opaquely. With\n``persist=1`` the blob is written through the ``backup_storage`` object-store\nseam (R2/S3, local fallback) and a ``KbDeckExport`` locator recorded; the file\nstill streams back inline.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Export: deck"
      }
    },
    "/api/kb/pages/{page_id}/deck/insert-diagram/": {
      "post": {
        "operationId": "kb_pages_deck_insert_diagram_create",
        "description": "POST /api/kb/pages/<uuid:page_id>/deck/insert-diagram/\n{source_type, source_id, slide_id?} \u2014 render an object diagram to an inert SVG\nand append it as an ``image`` block on the target slide (draft). A slides page\nhas no freeform surface, so the diagram lands as a rendered picture. Edit-gated.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Insert diagram: deck"
      }
    },
    "/api/kb/pages/{page_id}/deck/slides/reorder/": {
      "post": {
        "operationId": "kb_pages_deck_slides_reorder_create",
        "description": "``POST /api/kb/pages/<id>/deck/slides/reorder/`` \u2014 body ``{order:[id,\u2026]}``.\nReorders the slide array in the DRAFT body (autosave), returning the new order\n(spec \u00a77). Add/duplicate/delete slides ride the normal ``draft`` PUT.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Reorder: slide"
      }
    },
    "/api/kb/pages/{page_id}/import-drawio/": {
      "post": {
        "operationId": "kb_pages_import_drawio_create",
        "description": "POST /api/kb/pages/<uuid:page_id>/import-drawio/ {xml} \u2014 parse a draw.io /\nmxGraph export into an editable scene graph (\u00a710). Returns the sanitised scene\nfor the client to merge/publish (nominative interop only, \u00a712). Requires edit\npermission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Import drawio: page"
      }
    },
    "/api/kb/pages/{page_id}/live-card/": {
      "get": {
        "operationId": "kb_pages_live_card_retrieve",
        "description": "GET /api/kb/pages/<uuid:page_id>/live-card/?kind=&id= \u2014 resolve one Live Card\nto a secret-free summary, re-checking tenant + object ACL on every call (\u00a77/\u00a79).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Live card: page"
      }
    },
    "/api/kb/pages/{page_id}/live/ai-suggest/": {
      "post": {
        "operationId": "kb_pages_live_ai_suggest_create",
        "description": "``POST /api/kb/pages/<id>/live/ai-suggest/`` \u2014 OPTIONAL: return an AI co-\nauthor *suggestion* (prose) grounded in the page (\u00a76). A thin, best-effort\nseam over the existing ``UnifiedAIClient``; turning suggestions into applied\nCRDT ops is deferred. Requires edit permission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Ai suggest: live"
      }
    },
    "/api/kb/pages/{page_id}/live/flush/": {
      "post": {
        "operationId": "kb_pages_live_flush_create",
        "description": "``POST /api/kb/pages/<id>/live/flush/`` \u2014 flush the converged CRDT into the\npage's EXISTING ``draft_body`` (non-live viewers + the ``has_draft`` dot keep\nworking). Does not create a version. Requires edit permission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Flush: live"
      }
    },
    "/api/kb/pages/{page_id}/live/journal/": {
      "get": {
        "operationId": "kb_pages_live_journal_retrieve",
        "description": "``GET /api/kb/pages/<id>/live/journal/?since=<seq>&verify=1`` \u2014 the ordered,\nattributed op journal (a keystroke-grain audit record, \u00a79). ``verify=1``\nadditionally walks the compliance hash-chain and reports integrity.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Journal: live"
      }
    },
    "/api/kb/pages/{page_id}/live/ops/": {
      "get": {
        "operationId": "kb_pages_live_ops_retrieve",
        "description": "``GET  live/ops/?since=<seq>`` replay; ``POST live/ops/`` submit a batch.\nSubmitting requires edit permission (\u00a79).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Ops: live"
      },
      "post": {
        "operationId": "kb_pages_live_ops_create",
        "description": "``GET  live/ops/?since=<seq>`` replay; ``POST live/ops/`` submit a batch.\nSubmitting requires edit permission (\u00a79).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Ops: live"
      }
    },
    "/api/kb/pages/{page_id}/live/presence/": {
      "post": {
        "operationId": "kb_pages_live_presence_create",
        "description": "``POST /api/kb/pages/<id>/live/presence/`` \u2014 cursor/selection heartbeat\n(fallback path; the WS carries it inline otherwise). Body:\n``{site, cursor:{bid,offset,anchor?,head?}, mode:viewing|editing}``.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Presence: live"
      }
    },
    "/api/kb/pages/{page_id}/live/publish/": {
      "post": {
        "operationId": "kb_pages_live_publish_create",
        "description": "``POST /api/kb/pages/<id>/live/publish/`` \u2014 flatten the CRDT and promote it\nto a new immutable ``KbPageVersion`` via ``kb_service.save_page``; the\n``base_version`` optimistic-concurrency guard is preserved (\u00a75/\u00a78). Requires\nedit permission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Publish: live"
      }
    },
    "/api/kb/pages/{page_id}/live/session/": {
      "get": {
        "operationId": "kb_pages_live_session_retrieve",
        "description": "``GET /api/kb/pages/<id>/live/session/?from_issue=<KEY>&site=<id>`` \u2014 join.\nView-restriction inheritance gates joining (\u00a79).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Session: live"
      },
      "post": {
        "operationId": "kb_pages_live_session_create",
        "description": "``GET /api/kb/pages/<id>/live/session/?from_issue=<KEY>&site=<id>`` \u2014 join.\nView-restriction inheritance gates joining (\u00a79).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Session: live"
      }
    },
    "/api/kb/pages/{page_id}/live/snapshot/": {
      "post": {
        "operationId": "kb_pages_live_snapshot_create",
        "description": "``POST /api/kb/pages/<id>/live/snapshot/`` \u2014 force compaction (\u00a77). Also run\nby the existing scheduler as a periodic sweep. Requires edit permission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Snapshot: live"
      }
    },
    "/api/kb/pages/{page_id}/live/stream/": {
      "get": {
        "operationId": "kb_pages_live_stream_retrieve",
        "description": "``GET /api/kb/pages/<id>/live/stream/?since=<seq>&site=<id>`` \u2014 a bounded\nServer-Sent-Events stream of ops + presence for WSGI / hardened on-prem (\u00a77).\nThe durable ``KbLiveOp`` journal is the fan-out bus: each poll pulls rows\n``> last_seq`` and emits them; the tenant scope is re-opened per poll so no DB\ntransaction is held across a sleep.\n\nNB: ``EventSource`` cannot set an Authorization header; a token query-param\nshim or a same-origin cookie is a transport-wiring concern (frontend/deploy),\nnot this backend module. Auth stays ``IsAuthenticated`` here.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Stream: live"
      }
    },
    "/api/kb/pages/{page_id}/presence/": {
      "get": {
        "operationId": "kb_pages_presence_retrieve",
        "description": "GET (roster) / PUT (heartbeat) /api/kb/pages/<uuid:page_id>/presence/ (\u00a77).\nSuperseded by the Live Co-Editing channel in Phase 2 (\u00a711).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Presence: page"
      },
      "put": {
        "operationId": "kb_pages_presence_update",
        "description": "GET (roster) / PUT (heartbeat) /api/kb/pages/<uuid:page_id>/presence/ (\u00a77).\nSuperseded by the Live Co-Editing channel in Phase 2 (\u00a711).",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Presence: page"
      }
    },
    "/api/kb/pages/{page_id}/present/": {
      "get": {
        "operationId": "kb_pages_present_retrieve",
        "description": "``GET /api/kb/pages/<id>/present/`` \u2014 the full-screen player bundle (spec\n\u00a77): ordered slide HTML + theme tokens + transition defaults. Present requires\nVIEW; ``notes_html`` is included only for edit/present-capable callers.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Present: page"
      }
    },
    "/api/kb/pages/{page_id}/promote-stickies/": {
      "post": {
        "operationId": "kb_pages_promote_stickies_create",
        "description": "POST /api/kb/pages/<uuid:page_id>/promote-stickies/ {node_ids,[project]} \u2014\nspawn a Desk issue per sticky and back-link them as issue Live Cards (\u00a76).\nRequires edit permission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Promote stickies: page"
      }
    },
    "/api/kb/pages/{page_id}/render/": {
      "get": {
        "operationId": "kb_pages_render_retrieve",
        "description": "``GET /api/kb/pages/<id>/render/?highlights=1`` \u2192 ``rendered_html`` produced\nvia the safe highlight-injection renderer (\u00a75/\u00a77), so the viewer receives\n``<mark data-thread>`` pre-injected and XSS-safe. Without ``highlights=1`` it\nreturns the cached ``rendered_html`` unchanged.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Render: page"
      }
    },
    "/api/kb/pages/{page_id}/seed-object/": {
      "post": {
        "operationId": "kb_pages_seed_object_create",
        "description": "POST /api/kb/pages/<uuid:page_id>/seed-object/ {source_type, source_id} \u2014\nbuild diagram nodes + connectors for an existing object (ADC VIP / cloud app /\ncertificate / WideIP / network map / diagnostic) to merge into the whiteboard\nscene \u2014 the object-picker analogue of ``seed-topology``. Requires edit permission.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Seed object: page"
      }
    },
    "/api/kb/pages/{page_id}/seed-topology/": {
      "post": {
        "operationId": "kb_pages_seed_topology_create",
        "description": "POST /api/kb/pages/<uuid:page_id>/seed-topology/ {ticket_number} \u2014 returns\ndevice/vip Live nodes + connectors to merge into the scene (the \"Add impacted\nCIs\" action, \u00a76). Requires edit permission on the board.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Seed topology: page"
      }
    },
    "/api/kb/pages/{page_id}/uploads/": {
      "post": {
        "operationId": "kb_pages_uploads_create",
        "description": "POST /api/kb/pages/<uuid:page_id>/uploads/ \u2014 init a resumable session.",
        "parameters": [
          {
            "in": "path",
            "name": "page_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Uploads: page"
      }
    },
    "/api/kb/pages/{id}/": {
      "get": {
        "operationId": "kb_pages_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve page"
      },
      "put": {
        "operationId": "kb_pages_update",
        "description": "Save a new version. Optimistic concurrency (\u00a78): a stale base_version\nreturns 409 with the current version so the editor can merge.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update page"
      },
      "patch": {
        "operationId": "kb_pages_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbPageRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update page"
      },
      "delete": {
        "operationId": "kb_pages_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete page"
      }
    },
    "/api/kb/pages/{id}/attachments/": {
      "get": {
        "operationId": "kb_pages_attachments_retrieve",
        "description": "GET the page's attachments (metadata + download url), or POST a file\n(multipart 'file') to attach it (\u00a710).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Attachments: page"
      },
      "post": {
        "operationId": "kb_pages_attachments_create",
        "description": "GET the page's attachments (metadata + download url), or POST a file\n(multipart 'file') to attach it (\u00a710).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Attachments: page"
      }
    },
    "/api/kb/pages/{id}/backlinks/": {
      "get": {
        "operationId": "kb_pages_backlinks_retrieve",
        "description": "Pages that link TO this page (VS-SPEC-WIKI-BACKLINKS) \u2014 powers the\n'Referenced by' panel. Deduped; archived source pages excluded. The edges\nare same-tenant by construction (see kb_service.rebuild_page_links).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Backlinks: page"
      }
    },
    "/api/kb/pages/{id}/diff/": {
      "get": {
        "operationId": "kb_pages_diff_retrieve",
        "description": "Line-level diff between two versions (VS-SPEC-WIKI-VERSION-DIFF).\nQuery: ?a=<n>&b=<n>; b defaults to the current version, a to b-1. Returns\n{a, b, a_title, b_title, lines:[{type: equal|add|remove, text}]}.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Diff: page"
      }
    },
    "/api/kb/pages/{id}/draft/": {
      "put": {
        "operationId": "kb_pages_draft_update",
        "description": "Autosave (PUT) or discard (DELETE) the page's unpublished working copy.\nA PUT stores draft_body without creating a version, so edits survive\nnavigation and reloads; DELETE reverts to the published version.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Draft: page"
      },
      "delete": {
        "operationId": "kb_pages_draft_destroy",
        "description": "Autosave (PUT) or discard (DELETE) the page's unpublished working copy.\nA PUT stores draft_body without creating a version, so edits survive\nnavigation and reloads; DELETE reverts to the published version.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Draft: page"
      }
    },
    "/api/kb/pages/{id}/export/": {
      "get": {
        "operationId": "kb_pages_export_retrieve",
        "description": "Export the page as Markdown or HTML (\u00a77/\u00a715). ?format=md|html.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Export: page"
      }
    },
    "/api/kb/pages/{id}/link_issue/": {
      "post": {
        "operationId": "kb_pages_link_issue_create",
        "description": "Link a Desk issue to this page (validated); idempotent.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Link issue: page"
      }
    },
    "/api/kb/pages/{id}/move/": {
      "post": {
        "operationId": "kb_pages_move_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Move: page"
      }
    },
    "/api/kb/pages/{id}/publish/": {
      "post": {
        "operationId": "kb_pages_publish_create",
        "description": "Promote the working copy (the posted body, else the stored draft) to a\nnew published version, clear the draft, and notify watchers.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Publish: page"
      }
    },
    "/api/kb/pages/{id}/restore/": {
      "post": {
        "operationId": "kb_pages_restore_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Restore: page"
      }
    },
    "/api/kb/pages/{id}/restrictions/": {
      "get": {
        "operationId": "kb_pages_restrictions_retrieve",
        "description": "GET the page's restrictions, or POST to replace them:\n{\"view\": [\"network_admin\", ...], \"edit\": [...]} (role slugs).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Restrictions: page"
      },
      "post": {
        "operationId": "kb_pages_restrictions_create",
        "description": "GET the page's restrictions, or POST to replace them:\n{\"view\": [\"network_admin\", ...], \"edit\": [...]} (role slugs).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Restrictions: page"
      }
    },
    "/api/kb/pages/{id}/run_runbook/": {
      "post": {
        "operationId": "kb_pages_run_runbook_create",
        "description": "Launch the page's runbook WorkflowDefinition (a documented runbook\nbecomes an executable one). Records the run id back on the response.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Run runbook: page"
      }
    },
    "/api/kb/pages/{id}/set_labels/": {
      "post": {
        "operationId": "kb_pages_set_labels_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Set labels: page"
      }
    },
    "/api/kb/pages/{id}/stats/": {
      "get": {
        "operationId": "kb_pages_stats_retrieve",
        "description": "GET /api/kb/pages/<id>/stats/ \u2014 the page's rollup (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Stats: page"
      }
    },
    "/api/kb/pages/{id}/unlink_issue/": {
      "post": {
        "operationId": "kb_pages_unlink_issue_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Unlink issue: page"
      }
    },
    "/api/kb/pages/{id}/versions/": {
      "get": {
        "operationId": "kb_pages_versions_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Versions: page"
      }
    },
    "/api/kb/pages/{id}/versions/{number}/": {
      "get": {
        "operationId": "kb_pages_versions_retrieve_2",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          },
          {
            "in": "path",
            "name": "number",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve version"
      }
    },
    "/api/kb/pages/{id}/view/": {
      "post": {
        "operationId": "kb_pages_view_create",
        "description": "POST /api/kb/pages/<id>/view/ \u2014 record a view (deduped per user/session/\nday); returns the updated ``views_total`` (spec \u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "View: page"
      }
    },
    "/api/kb/pages/{id}/watch/": {
      "post": {
        "operationId": "kb_pages_watch_create",
        "description": "Toggle watching this page (\u00a73 #14) \u2014 watchers are notified on comments.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb page.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Watch: page"
      }
    },
    "/api/kb/pages/for_issue/": {
      "get": {
        "operationId": "kb_pages_for_issue_retrieve",
        "description": "Pages linking a given issue key \u2014 powers the issue's 'referenced in' panel.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List for issues"
      }
    },
    "/api/kb/pages/search/": {
      "get": {
        "operationId": "kb_pages_search_retrieve",
        "description": "Full-text-ish search over title + rendered body (P1). Optional\nspace/label scoping.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List searchs"
      }
    },
    "/api/kb/pages/templates/": {
      "get": {
        "operationId": "kb_pages_templates_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List templates"
      }
    },
    "/api/kb/pagetree/audit/": {
      "get": {
        "operationId": "kb_pagetree_audit_retrieve",
        "description": "GET kb/pagetree/audit/?space=<uuid|key> \u2014 the queryable structural\naudit projection (\u00a74/\u00a79), newest first.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List audits"
      }
    },
    "/api/kb/pagetree/bulk_labels/": {
      "post": {
        "operationId": "kb_pagetree_bulk_labels_create",
        "description": "POST kb/pagetree/bulk_labels/ \u2014 {ids, labels: [...], mode: add|replace|remove}.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create bulk label"
      }
    },
    "/api/kb/pagetree/bulk_move/": {
      "post": {
        "operationId": "kb_pagetree_bulk_move_create",
        "description": "POST kb/pagetree/bulk_move/ \u2014 {ids, parent|null, position?, space?}.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create bulk move"
      }
    },
    "/api/kb/pagetree/bulk_restrict/": {
      "post": {
        "operationId": "kb_pagetree_bulk_restrict_create",
        "description": "POST kb/pagetree/bulk_restrict/ \u2014 {ids, mode: view|edit, role_slug?,\nuser?, clear?}.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create bulk restrict"
      }
    },
    "/api/kb/pagetree/bulk_status/": {
      "post": {
        "operationId": "kb_pagetree_bulk_status_create",
        "description": "POST kb/pagetree/bulk_status/ \u2014 {ids, status: archived|current,\nchildren: reparent|cascade}.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create bulk statu"
      }
    },
    "/api/kb/pagetree/move/": {
      "post": {
        "operationId": "kb_pagetree_move_create",
        "description": "POST kb/pagetree/move/ \u2014 transactional single-node move (\u00a75). Body:\n{id, parent|null, position?, base_path?, space?}.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create move"
      }
    },
    "/api/kb/pagetree/move_undo/": {
      "post": {
        "operationId": "kb_pagetree_move_undo_create",
        "description": "POST kb/pagetree/move_undo/ \u2014 {audit_id}. Replays a KbTreeAudit.before\n(single-level, best-effort); 409 if the tree moved on (\u00a77).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create move undo"
      }
    },
    "/api/kb/pagetree/tree/": {
      "get": {
        "operationId": "kb_pagetree_tree_retrieve",
        "description": "GET kb/pagetree/tree/?space=<uuid|key> \u2014 the extended node payload\n(position/parent/status/restricted/child_count/\u2026) for the DnD tree.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List trees"
      }
    },
    "/api/kb/pagetree/tree_pref/": {
      "get": {
        "operationId": "kb_pagetree_tree_pref_retrieve",
        "description": "GET/PUT kb/pagetree/tree_pref/?space=<uuid> \u2014 the caller's per-space\ncollapsed-set (\u00a77). Server-persisted alternative to localStorage (\u00a711).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List tree prefs"
      },
      "put": {
        "operationId": "kb_pagetree_tree_pref_update",
        "description": "GET/PUT kb/pagetree/tree_pref/?space=<uuid> \u2014 the caller's per-space\ncollapsed-set (\u00a77). Server-persisted alternative to localStorage (\u00a711).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbPageRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbPage"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update tree pref"
      }
    },
    "/api/kb/reactions/": {
      "get": {
        "operationId": "kb_reactions_list",
        "description": "GET ?target_type=&target_id= \u2192 the target's reaction aggregate.",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbReactionList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List reactions"
      }
    },
    "/api/kb/reactions/toggle/": {
      "post": {
        "operationId": "kb_reactions_toggle_create",
        "description": "POST {target_type, target_id, emoji} \u2192 create-or-remove the caller's\nreaction; returns the fresh aggregate for that target (\u00a77). Validates the\nemoji allow-list and that the target is tenant-visible (\u00a79).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbReactionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbReactionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbReactionRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbReaction"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create toggle"
      }
    },
    "/api/kb/smartlinks/domain-rules/": {
      "get": {
        "operationId": "kb_smartlinks_domain_rules_list",
        "description": "CRUD the tenant's external allow/deny list (spec \u00a77). Reads for any\nauthenticated user; mutations are space/tenant-admin only.",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSmartLinkDomainRuleList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List domain rules"
      },
      "post": {
        "operationId": "kb_smartlinks_domain_rules_create",
        "description": "CRUD the tenant's external allow/deny list (spec \u00a77). Reads for any\nauthenticated user; mutations are space/tenant-admin only.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmartLinkDomainRuleRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SmartLinkDomainRuleRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SmartLinkDomainRuleRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmartLinkDomainRule"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create domain rule"
      }
    },
    "/api/kb/smartlinks/domain-rules/{id}/": {
      "get": {
        "operationId": "kb_smartlinks_domain_rules_retrieve",
        "description": "CRUD the tenant's external allow/deny list (spec \u00a77). Reads for any\nauthenticated user; mutations are space/tenant-admin only.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this smart link domain rule.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmartLinkDomainRule"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve domain rule"
      },
      "put": {
        "operationId": "kb_smartlinks_domain_rules_update",
        "description": "CRUD the tenant's external allow/deny list (spec \u00a77). Reads for any\nauthenticated user; mutations are space/tenant-admin only.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this smart link domain rule.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmartLinkDomainRuleRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SmartLinkDomainRuleRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SmartLinkDomainRuleRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmartLinkDomainRule"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update domain rule"
      },
      "patch": {
        "operationId": "kb_smartlinks_domain_rules_partial_update",
        "description": "CRUD the tenant's external allow/deny list (spec \u00a77). Reads for any\nauthenticated user; mutations are space/tenant-admin only.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this smart link domain rule.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedSmartLinkDomainRuleRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedSmartLinkDomainRuleRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedSmartLinkDomainRuleRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmartLinkDomainRule"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update domain rule"
      },
      "delete": {
        "operationId": "kb_smartlinks_domain_rules_destroy",
        "description": "CRUD the tenant's external allow/deny list (spec \u00a77). Reads for any\nauthenticated user; mutations are space/tenant-admin only.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this smart link domain rule.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete domain rule"
      }
    },
    "/api/kb/smartlinks/preview/": {
      "get": {
        "operationId": "kb_smartlinks_preview_retrieve",
        "description": "GET /api/kb/smartlinks/preview/?target= \u2014 metadata-only, no cache write.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List previews"
      }
    },
    "/api/kb/smartlinks/providers/": {
      "get": {
        "operationId": "kb_smartlinks_providers_retrieve",
        "description": "GET /api/kb/smartlinks/providers/ \u2014 registered providers for the insert menu.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List providers"
      }
    },
    "/api/kb/smartlinks/refresh/": {
      "post": {
        "operationId": "kb_smartlinks_refresh_create",
        "description": "POST /api/kb/smartlinks/refresh/ \u2014 force-revalidate one resolution.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create refresh"
      }
    },
    "/api/kb/smartlinks/resolve/": {
      "post": {
        "operationId": "kb_smartlinks_resolve_create",
        "description": "POST /api/kb/smartlinks/resolve/ \u2014 cache-first resolve (paste path).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create resolve"
      }
    },
    "/api/kb/spaces/": {
      "get": {
        "operationId": "kb_spaces_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbSpaceList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List spaces"
      },
      "post": {
        "operationId": "kb_spaces_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbSpaceRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbSpaceRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbSpaceRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbSpace"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create space"
      }
    },
    "/api/kb/spaces/{key}/activity/": {
      "get": {
        "operationId": "kb_spaces_activity_retrieve",
        "description": "GET /api/kb/spaces/<key>/activity/?limit= \u2014 merged recent-activity feed\n(spec \u00a76/\u00a77).",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Activity: space"
      }
    },
    "/api/kb/spaces/{key}/insights/": {
      "get": {
        "operationId": "kb_spaces_insights_retrieve",
        "description": "GET /api/kb/spaces/<key>/insights/?window=7d|30d|90d \u2014 dashboard payload\n(spec \u00a77). Requires the caller be able to see the space.",
        "parameters": [
          {
            "in": "path",
            "name": "key",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Insights: space"
      }
    },
    "/api/kb/spaces/{id}/": {
      "get": {
        "operationId": "kb_spaces_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb space.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbSpace"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve space"
      },
      "put": {
        "operationId": "kb_spaces_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb space.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbSpaceRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbSpaceRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbSpaceRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbSpace"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update space"
      },
      "patch": {
        "operationId": "kb_spaces_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb space.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbSpaceRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbSpaceRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbSpaceRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbSpace"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update space"
      },
      "delete": {
        "operationId": "kb_spaces_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb space.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete space"
      }
    },
    "/api/kb/spaces/{id}/tree/": {
      "get": {
        "operationId": "kb_spaces_tree_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb space.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbSpace"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Tree: space"
      }
    },
    "/api/kb/spaces/{space_id}/access/": {
      "get": {
        "operationId": "kb_spaces_access_retrieve",
        "description": "Read (GET) or set (PATCH) a space's access settings (spec \u00a77). Switching\nto 'anonymous' requires tenant-admin/superuser co-sign (`cosign: true`).",
        "parameters": [
          {
            "in": "path",
            "name": "space_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Access: space"
      },
      "patch": {
        "operationId": "kb_spaces_access_partial_update",
        "description": "Read (GET) or set (PATCH) a space's access settings (spec \u00a77). Switching\nto 'anonymous' requires tenant-admin/superuser co-sign (`cosign: true`).",
        "parameters": [
          {
            "in": "path",
            "name": "space_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Access: space"
      }
    },
    "/api/kb/spaces/{space_id}/members/": {
      "get": {
        "operationId": "kb_spaces_members_retrieve",
        "description": "List (GET) or add/replace (POST) grants on a space. space_admin gated.",
        "parameters": [
          {
            "in": "path",
            "name": "space_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Members: space"
      },
      "post": {
        "operationId": "kb_spaces_members_create",
        "description": "List (GET) or add/replace (POST) grants on a space. space_admin gated.",
        "parameters": [
          {
            "in": "path",
            "name": "space_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Members: space"
      }
    },
    "/api/kb/spaces/{space_id}/members/{member_id}/": {
      "delete": {
        "operationId": "kb_spaces_members_destroy",
        "description": "Revoke a single grant (DELETE). space_admin gated.",
        "parameters": [
          {
            "in": "path",
            "name": "member_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          },
          {
            "in": "path",
            "name": "space_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete member"
      }
    },
    "/api/kb/spaces/{space_id}/perm-audit/": {
      "get": {
        "operationId": "kb_spaces_perm_audit_retrieve",
        "description": "Paginated audit feed for a space (spec \u00a77/\u00a78). space_admin gated.",
        "parameters": [
          {
            "in": "path",
            "name": "space_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Perm audit: space"
      }
    },
    "/api/kb/spaces/{space_id}/permissions/": {
      "get": {
        "operationId": "kb_spaces_permissions_retrieve",
        "description": "Effective-permission preview for the settings UI (spec \u00a77):\n{me: {...}, roles: {...}, access: {...}}.",
        "parameters": [
          {
            "in": "path",
            "name": "space_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Permissions: space"
      }
    },
    "/api/kb/stencils/": {
      "get": {
        "operationId": "kb_stencils_retrieve",
        "description": "GET /api/kb/stencils/ \u2014 the vendor stencil library (label / brand colour /\nfunction per vendor) + the palette grouping + device_type\u2192function map, so the\nboard and the insert palette can render vendor devices. Static reference data.",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List stencils"
      }
    },
    "/api/kb/templates/": {
      "get": {
        "operationId": "kb_templates_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedKbTemplateList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List templates"
      },
      "post": {
        "operationId": "kb_templates_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create template"
      }
    },
    "/api/kb/templates/{id}/": {
      "get": {
        "operationId": "kb_templates_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb template.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve template"
      },
      "put": {
        "operationId": "kb_templates_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb template.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update template"
      },
      "patch": {
        "operationId": "kb_templates_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb template.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedKbTemplateRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update template"
      },
      "delete": {
        "operationId": "kb_templates_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb template.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete template"
      }
    },
    "/api/kb/templates/{id}/instantiate/": {
      "post": {
        "operationId": "kb_templates_instantiate_create",
        "description": "Substitute variables + create a page (or a scaffold tree). Body:\n{space, parent?, values:{\u2026}} \u2192 returns the new KbPage (201).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb template.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Instantiate: template"
      }
    },
    "/api/kb/templates/{id}/preview/": {
      "get": {
        "operationId": "kb_templates_preview_retrieve",
        "description": "Server-rendered read-only preview of the body with defaults/dynamic\ntokens filled \u2014 the existing safe renderer, never client HTML.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this kb template.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Preview: template"
      }
    },
    "/api/kb/templates/field_types/": {
      "get": {
        "operationId": "kb_templates_field_types_retrieve",
        "description": "Template-variable type \u2192 App Builder FormField field type, so the\nwizard reuses the existing input widgets (spec \u00a76).",
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List field types"
      }
    },
    "/api/kb/templates/from_page/": {
      "post": {
        "operationId": "kb_templates_from_page_create",
        "description": "Clone a page's current body into a new template, auto-extracting\n{{\u2026}} tokens into a variable list. Body: {page, scope, space?, name, category?}.",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/KbTemplateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KbTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create from page"
      }
    },
    "/api/kb/timelines/": {
      "get": {
        "operationId": "kb_timelines_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedTimelineList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List timelines"
      },
      "post": {
        "operationId": "kb_timelines_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create timeline"
      }
    },
    "/api/kb/timelines/{id}/": {
      "get": {
        "operationId": "kb_timelines_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve timeline"
      },
      "put": {
        "operationId": "kb_timelines_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update timeline"
      },
      "patch": {
        "operationId": "kb_timelines_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedTimelineRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update timeline"
      },
      "delete": {
        "operationId": "kb_timelines_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete timeline"
      }
    },
    "/api/kb/timelines/{id}/events/": {
      "get": {
        "operationId": "kb_timelines_events_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Events: timeline"
      },
      "post": {
        "operationId": "kb_timelines_events_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Events: timeline"
      }
    },
    "/api/kb/timelines/{id}/export.ics/": {
      "get": {
        "operationId": "kb_timelines_export.ics_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Export.ics: timeline"
      }
    },
    "/api/kb/timelines/{id}/feed-url/": {
      "get": {
        "operationId": "kb_timelines_feed_url_retrieve",
        "description": "Return the caller's tokenized read-only feed URL (authenticated;\nthe token is never emitted in page HTML).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Feed url: timeline"
      }
    },
    "/api/kb/timelines/{id}/feed.ics": {
      "get": {
        "operationId": "kb_timelines_feed.ics_retrieve",
        "description": "Unauthenticated but token-signed, READ-ONLY ICS feed for external calendar\nclients (spec \u00a77/\u00a79). Authorizes solely on (timeline_id, token); revocable by\nrotating the token. Runs outside any tenant token \u2014 resolve_feed uses the\ndefault connection and layer queries stay scoped to the timeline's tenant.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Feed.ics: timeline"
      }
    },
    "/api/kb/timelines/{id}/import/": {
      "post": {
        "operationId": "kb_timelines_import_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Import: timeline"
      }
    },
    "/api/kb/timelines/{id}/rotate-feed-token/": {
      "post": {
        "operationId": "kb_timelines_rotate_feed_token_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Rotate feed token: timeline"
      }
    },
    "/api/kb/timelines/{id}/subscribe/": {
      "post": {
        "operationId": "kb_timelines_subscribe_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Timeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Subscribe: timeline"
      },
      "delete": {
        "operationId": "kb_timelines_subscribe_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "A UUID string identifying this timeline.",
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Subscribe: timeline"
      }
    },
    "/api/kb/uploads/{session_id}/": {
      "delete": {
        "operationId": "kb_uploads_destroy",
        "description": "DELETE /api/kb/uploads/<uuid:session_id>/ \u2014 abort + GC staging.",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete upload"
      }
    },
    "/api/kb/uploads/{session_id}/chunk/{index}/": {
      "put": {
        "operationId": "kb_uploads_chunk_update",
        "description": "PUT /api/kb/uploads/<uuid:session_id>/chunk/<int:index>/ \u2014 upload chunk N.",
        "parameters": [
          {
            "in": "path",
            "name": "index",
            "schema": {
              "type": "integer"
            },
            "required": true
          },
          {
            "in": "path",
            "name": "session_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Update chunk"
      }
    },
    "/api/kb/uploads/{session_id}/complete/": {
      "post": {
        "operationId": "kb_uploads_complete_create",
        "description": "POST /api/kb/uploads/<uuid:session_id>/complete/ \u2014 assemble + publish.",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true
          }
        ],
        "tags": [
          "kb"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Complete: upload"
      }
    }
  },
  "components": {
    "schemas": {
      "KbDatasetField": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "dataset": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "field_key": {
            "type": "string",
            "readOnly": true
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "type": {
            "$ref": "#/components/schemas/KbDatasetFieldTypeEnum"
          },
          "options": {},
          "required": {
            "type": "boolean"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "dataset",
          "field_key",
          "id",
          "name"
        ]
      },
      "PaginatedKbPageList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbPage"
            }
          }
        }
      },
      "KbDatasetFieldTypeEnum": {
        "enum": [
          "text",
          "long_text",
          "number",
          "select",
          "multi_select",
          "date",
          "user",
          "checkbox",
          "url",
          "relation"
        ],
        "type": "string",
        "description": "* `text` - text\n* `long_text` - long_text\n* `number` - number\n* `select` - select\n* `multi_select` - multi_select\n* `date` - date\n* `user` - user\n* `checkbox` - checkbox\n* `url` - url\n* `relation` - relation"
      },
      "KbLabelRequest": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "color": {
            "type": "string",
            "maxLength": 16
          },
          "kind": {
            "$ref": "#/components/schemas/KbLabelKindEnum"
          },
          "target_ref": {
            "type": "string",
            "maxLength": 200
          }
        }
      },
      "KbPageCommentRequest": {
        "type": "object",
        "properties": {
          "page": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "body": {
            "type": "string",
            "minLength": 1
          },
          "anchor": {},
          "resolved": {
            "type": "boolean"
          }
        },
        "required": [
          "body",
          "page"
        ]
      },
      "PatchedKbInlineCommentRequest": {
        "type": "object",
        "description": "Comment payload for the annotation rail \u2014 the existing page-comment fields\nplus inline-anchor status and the caller-aware reaction aggregate (\u00a77 embed).\n\n``anchor_status`` / ``promoted_issue_key`` are surfaced defensively via\n``getattr`` so the serializer is valid before those additive fields exist on\n``KbPageComment``; once the migration lands they read the real columns.",
        "properties": {
          "page": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "body": {
            "type": "string",
            "minLength": 1
          },
          "anchor": {},
          "resolved": {
            "type": "boolean"
          }
        }
      },
      "PatchedTimelineEventRequest": {
        "type": "object",
        "properties": {
          "timeline": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "description": {},
          "event_type": {
            "$ref": "#/components/schemas/EventTypeEnum"
          },
          "all_day": {
            "type": "boolean"
          },
          "start_at": {
            "type": "string",
            "format": "date-time"
          },
          "end_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "recurrence_rrule": {
            "type": "string",
            "maxLength": 500
          },
          "recurrence_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "location": {
            "type": "string",
            "maxLength": 300
          },
          "attendees": {},
          "color": {
            "type": "string",
            "maxLength": 9
          }
        }
      },
      "KbAgentScope": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "kind": {
            "$ref": "#/components/schemas/KbAgentScopeKindEnum"
          },
          "space": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "space_key": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "label": {
            "type": "string",
            "maxLength": 120
          },
          "page": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "page_title": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "mode": {
            "$ref": "#/components/schemas/KbAgentScopeModeEnum"
          }
        },
        "required": [
          "id",
          "kind",
          "page_title",
          "space_key"
        ]
      },
      "PaginatedKbSpaceList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbSpace"
            }
          }
        }
      },
      "KbTemplateSourceEnum": {
        "enum": [
          "builtin",
          "tenant"
        ],
        "type": "string",
        "description": "* `builtin` - Built-in\n* `tenant` - Tenant"
      },
      "KbLabelKindEnum": {
        "enum": [
          "plain",
          "device",
          "runbook",
          "issue_filter"
        ],
        "type": "string",
        "description": "* `plain` - Plain\n* `device` - Device group\n* `runbook` - Runbook\n* `issue_filter` - Issue filter"
      },
      "KbDataset": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "space_key": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "key": {
            "type": "string",
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "restricted": {
            "type": "boolean"
          },
          "compliance": {
            "type": "boolean"
          },
          "field_count": {
            "type": "string",
            "readOnly": true
          },
          "row_count": {
            "type": "string",
            "readOnly": true
          },
          "view_count": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "field_count",
          "id",
          "key",
          "name",
          "row_count",
          "space",
          "space_key",
          "updated_at",
          "view_count"
        ]
      },
      "TargetTypeEnum": {
        "enum": [
          "page",
          "comment"
        ],
        "type": "string",
        "description": "* `page` - Page\n* `comment` - Comment"
      },
      "PaginatedOncallRotationList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OncallRotation"
            }
          }
        }
      },
      "OncallRotationRequest": {
        "type": "object",
        "properties": {
          "timeline": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "members": {},
          "cadence_rrule": {
            "type": "string",
            "maxLength": 500
          },
          "shift_length_hours": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "starts_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "active": {
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "timeline"
        ]
      },
      "KbPage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "space_key": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "title": {
            "type": "string",
            "maxLength": 300
          },
          "slug": {
            "type": "string",
            "maxLength": 320,
            "pattern": "^[-a-zA-Z0-9_]+$"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "status": {
            "$ref": "#/components/schemas/KbPageStatusEnum"
          },
          "content_type": {
            "$ref": "#/components/schemas/ContentTypeEnum"
          },
          "labels": {},
          "issue_links": {},
          "runbook_ref": {
            "type": "string",
            "maxLength": 200
          },
          "restricted": {
            "type": "boolean"
          },
          "watching": {
            "type": "string",
            "readOnly": true
          },
          "version_number": {
            "type": "integer",
            "readOnly": true,
            "default": 0
          },
          "breadcrumb": {
            "type": "string",
            "readOnly": true
          },
          "body": {
            "type": "string",
            "readOnly": true
          },
          "rendered_html": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "has_draft": {
            "type": "string",
            "readOnly": true
          },
          "draft_body": {
            "type": "string",
            "readOnly": true
          },
          "draft_title": {
            "type": "string",
            "maxLength": 300
          },
          "draft_updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "body",
          "breadcrumb",
          "created_at",
          "draft_body",
          "draft_updated_at",
          "has_draft",
          "id",
          "rendered_html",
          "space",
          "space_key",
          "title",
          "updated_at",
          "version_number",
          "watching"
        ]
      },
      "KbSpace": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "key": {
            "type": "string",
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "type": {
            "$ref": "#/components/schemas/KbSpaceTypeEnum"
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "home_page": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "archived": {
            "type": "boolean"
          },
          "page_count": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "id",
          "key",
          "name",
          "page_count"
        ]
      },
      "PaginatedKbDatasetList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbDataset"
            }
          }
        }
      },
      "KbDatasetViewTypeEnum": {
        "enum": [
          "table",
          "board",
          "calendar",
          "gallery"
        ],
        "type": "string",
        "description": "* `table` - table\n* `board` - board\n* `calendar` - calendar\n* `gallery` - gallery"
      },
      "PatchedKbSpaceRequest": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "type": {
            "$ref": "#/components/schemas/KbSpaceTypeEnum"
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "home_page": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "archived": {
            "type": "boolean"
          }
        }
      },
      "KbSpaceRequest": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "type": {
            "$ref": "#/components/schemas/KbSpaceTypeEnum"
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "home_page": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "archived": {
            "type": "boolean"
          }
        },
        "required": [
          "key",
          "name"
        ]
      },
      "PaginatedKbClipList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbClip"
            }
          }
        }
      },
      "KbAgentScopeKindEnum": {
        "enum": [
          "space",
          "label",
          "page"
        ],
        "type": "string",
        "description": "* `space` - Space\n* `label` - Label\n* `page` - Page"
      },
      "KbTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "scope": {
            "$ref": "#/components/schemas/ScopeEnum"
          },
          "space": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "space_key": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "key": {
            "type": "string",
            "maxLength": 80
          },
          "content_type": {
            "type": "string",
            "maxLength": 20
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "category": {
            "type": "string",
            "maxLength": 100
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "body": {},
          "variables": {},
          "is_blueprint": {
            "type": "boolean"
          },
          "scaffold": {
            "nullable": true
          },
          "source": {
            "allOf": [
              {
                "$ref": "#/components/schemas/KbTemplateSourceEnum"
              }
            ],
            "readOnly": true
          },
          "is_builtin": {
            "type": "boolean",
            "readOnly": true
          },
          "enabled": {
            "type": "boolean"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "created_by_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "created_by_name",
          "id",
          "is_builtin",
          "name",
          "source",
          "space_key",
          "updated_at"
        ]
      },
      "ScopeEnum": {
        "enum": [
          "global",
          "space"
        ],
        "type": "string",
        "description": "* `global` - Global\n* `space` - Space"
      },
      "KbPageRequest": {
        "type": "object",
        "properties": {
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "slug": {
            "type": "string",
            "maxLength": 320,
            "pattern": "^[-a-zA-Z0-9_]+$"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "status": {
            "$ref": "#/components/schemas/KbPageStatusEnum"
          },
          "content_type": {
            "$ref": "#/components/schemas/ContentTypeEnum"
          },
          "labels": {},
          "issue_links": {},
          "runbook_ref": {
            "type": "string",
            "maxLength": 200
          },
          "restricted": {
            "type": "boolean"
          },
          "draft_title": {
            "type": "string",
            "maxLength": 300
          }
        },
        "required": [
          "space",
          "title"
        ]
      },
      "PaginatedKbLabelList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbLabel"
            }
          }
        }
      },
      "TimelineRequest": {
        "type": "object",
        "properties": {
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "color": {
            "type": "string",
            "maxLength": 9
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "default_view": {
            "$ref": "#/components/schemas/DefaultViewEnum"
          },
          "timezone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "enabled_layers": {},
          "source_board": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "archived": {
            "type": "boolean"
          }
        },
        "required": [
          "key",
          "name",
          "space"
        ]
      },
      "KbDatasetRequest": {
        "type": "object",
        "properties": {
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "restricted": {
            "type": "boolean"
          },
          "compliance": {
            "type": "boolean"
          }
        },
        "required": [
          "key",
          "name",
          "space"
        ]
      },
      "EventTypeEnum": {
        "enum": [
          "event",
          "leave",
          "oncall",
          "sprint",
          "release",
          "change",
          "freeze",
          "cab"
        ],
        "type": "string",
        "description": "* `event` - Event\n* `leave` - Leave / PTO / OOO\n* `oncall` - On-call\n* `sprint` - Sprint\n* `release` - Release\n* `change` - Change window\n* `freeze` - Freeze\n* `cab` - CAB"
      },
      "PatchedKbPageRequest": {
        "type": "object",
        "properties": {
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "slug": {
            "type": "string",
            "maxLength": 320,
            "pattern": "^[-a-zA-Z0-9_]+$"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "status": {
            "$ref": "#/components/schemas/KbPageStatusEnum"
          },
          "content_type": {
            "$ref": "#/components/schemas/ContentTypeEnum"
          },
          "labels": {},
          "issue_links": {},
          "runbook_ref": {
            "type": "string",
            "maxLength": 200
          },
          "restricted": {
            "type": "boolean"
          },
          "draft_title": {
            "type": "string",
            "maxLength": 300
          }
        }
      },
      "KbInlineComment": {
        "type": "object",
        "description": "Comment payload for the annotation rail \u2014 the existing page-comment fields\nplus inline-anchor status and the caller-aware reaction aggregate (\u00a77 embed).\n\n``anchor_status`` / ``promoted_issue_key`` are surfaced defensively via\n``getattr`` so the serializer is valid before those additive fields exist on\n``KbPageComment``; once the migration lands they read the real columns.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "page": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "author_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "body": {
            "type": "string"
          },
          "anchor": {},
          "resolved": {
            "type": "boolean"
          },
          "anchor_status": {
            "type": "string",
            "readOnly": true
          },
          "promoted_issue_key": {
            "type": "string",
            "readOnly": true
          },
          "reactions": {
            "type": "string",
            "readOnly": true
          },
          "reply_count": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "anchor_status",
          "author_name",
          "body",
          "created_at",
          "id",
          "page",
          "promoted_issue_key",
          "reactions",
          "reply_count"
        ]
      },
      "PatchedKbAgentRequest": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "purpose": {
            "type": "string",
            "maxLength": 300
          },
          "instructions": {
            "type": "string"
          },
          "provider": {
            "type": "integer",
            "nullable": true
          },
          "answer_style": {
            "$ref": "#/components/schemas/AnswerStyleEnum"
          },
          "temperature": {
            "type": "number",
            "format": "double"
          },
          "starter_prompts": {},
          "published": {
            "type": "boolean"
          }
        }
      },
      "PaginatedTimelineEventList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimelineEvent"
            }
          }
        }
      },
      "PaginatedKbInlineCommentList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbInlineComment"
            }
          }
        }
      },
      "OncallRotation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "timeline": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 160
          },
          "members": {},
          "cadence_rrule": {
            "type": "string",
            "maxLength": 500
          },
          "shift_length_hours": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "starts_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "id",
          "name",
          "timeline"
        ]
      },
      "DefaultViewEnum": {
        "enum": [
          "month",
          "week",
          "agenda"
        ],
        "type": "string",
        "description": "* `month` - Month\n* `week` - Week\n* `agenda` - Agenda"
      },
      "PaginatedSmartLinkDomainRuleList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SmartLinkDomainRule"
            }
          }
        }
      },
      "KbAgentAction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "action": {
            "$ref": "#/components/schemas/KbAgentActionActionEnum"
          },
          "config": {},
          "require_confirm": {
            "type": "boolean"
          }
        },
        "required": [
          "action",
          "id"
        ]
      },
      "PatchedSmartLinkDomainRuleRequest": {
        "type": "object",
        "properties": {
          "pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "action": {
            "$ref": "#/components/schemas/SmartLinkDomainRuleActionEnum"
          },
          "allow_embed": {
            "type": "boolean"
          },
          "note": {
            "type": "string",
            "maxLength": 300
          }
        }
      },
      "TimelineEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "timeline": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "maxLength": 300
          },
          "description": {},
          "event_type": {
            "$ref": "#/components/schemas/EventTypeEnum"
          },
          "all_day": {
            "type": "boolean"
          },
          "start_at": {
            "type": "string",
            "format": "date-time"
          },
          "end_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "recurrence_rrule": {
            "type": "string",
            "maxLength": 500
          },
          "recurrence_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "location": {
            "type": "string",
            "maxLength": 300
          },
          "attendees": {},
          "color": {
            "type": "string",
            "maxLength": 9
          },
          "source": {
            "type": "string",
            "readOnly": true
          },
          "source_ref": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "id",
          "source",
          "source_ref",
          "start_at",
          "title",
          "updated_at"
        ]
      },
      "PaginatedKbPageCommentList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbPageComment"
            }
          }
        }
      },
      "KbClip": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "page": {
            "type": "string",
            "format": "uuid",
            "readOnly": true,
            "nullable": true
          },
          "title": {
            "type": "string",
            "readOnly": true
          },
          "source": {
            "allOf": [
              {
                "$ref": "#/components/schemas/KbClipSourceEnum"
              }
            ],
            "readOnly": true
          },
          "session_ref": {
            "type": "string",
            "readOnly": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/KbClipStatusEnum"
              }
            ],
            "readOnly": true
          },
          "content_type": {
            "type": "string",
            "readOnly": true
          },
          "size": {
            "type": "integer",
            "readOnly": true
          },
          "duration": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "width": {
            "type": "integer",
            "readOnly": true
          },
          "height": {
            "type": "integer",
            "readOnly": true
          },
          "has_captions": {
            "type": "string",
            "readOnly": true
          },
          "poster_url": {
            "type": "string",
            "readOnly": true
          },
          "stream_url": {
            "type": "string",
            "readOnly": true
          },
          "captions_url": {
            "type": "string",
            "readOnly": true
          },
          "captured_by_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "captions_url",
          "captured_by_name",
          "content_type",
          "created_at",
          "duration",
          "has_captions",
          "height",
          "id",
          "page",
          "poster_url",
          "session_ref",
          "size",
          "source",
          "status",
          "stream_url",
          "title",
          "updated_at",
          "width"
        ]
      },
      "KbDatasetDetail": {
        "type": "object",
        "description": "Dataset with its fields + views inlined \u2014 the Grid page's initial payload.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "space_key": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "key": {
            "type": "string",
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "restricted": {
            "type": "boolean"
          },
          "compliance": {
            "type": "boolean"
          },
          "field_count": {
            "type": "string",
            "readOnly": true
          },
          "row_count": {
            "type": "string",
            "readOnly": true
          },
          "view_count": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbDatasetField"
            },
            "readOnly": true
          },
          "views": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbDatasetView"
            },
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "field_count",
          "fields",
          "id",
          "key",
          "name",
          "row_count",
          "space",
          "space_key",
          "updated_at",
          "view_count",
          "views"
        ]
      },
      "KbReaction": {
        "type": "object",
        "description": "Thin serializer for a single reaction row. The primary read surface is the\naggregate (``kb_reactions_service.aggregate_from_qs``), not this.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "target_type": {
            "$ref": "#/components/schemas/TargetTypeEnum"
          },
          "target_id": {
            "type": "string",
            "format": "uuid"
          },
          "emoji": {
            "type": "string",
            "maxLength": 24
          },
          "glyph": {
            "type": "string",
            "readOnly": true
          },
          "username": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "emoji",
          "glyph",
          "id",
          "target_id",
          "target_type",
          "username"
        ]
      },
      "TimelineEventRequest": {
        "type": "object",
        "properties": {
          "timeline": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "description": {},
          "event_type": {
            "$ref": "#/components/schemas/EventTypeEnum"
          },
          "all_day": {
            "type": "boolean"
          },
          "start_at": {
            "type": "string",
            "format": "date-time"
          },
          "end_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "recurrence_rrule": {
            "type": "string",
            "maxLength": 500
          },
          "recurrence_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "location": {
            "type": "string",
            "maxLength": 300
          },
          "attendees": {},
          "color": {
            "type": "string",
            "maxLength": 9
          }
        },
        "required": [
          "start_at",
          "title"
        ]
      },
      "KbSpaceTypeEnum": {
        "enum": [
          "team",
          "knowledge",
          "personal"
        ],
        "type": "string",
        "description": "* `team` - Team\n* `knowledge` - Knowledge Base\n* `personal` - Personal"
      },
      "KbClipStatusEnum": {
        "enum": [
          "recording",
          "uploading",
          "processing",
          "ready",
          "failed"
        ],
        "type": "string",
        "description": "* `recording` - Recording\n* `uploading` - Uploading\n* `processing` - Processing\n* `ready` - Ready\n* `failed` - Failed"
      },
      "KbAgentActionActionEnum": {
        "enum": [
          "launch_runbook",
          "create_issue",
          "link_issue",
          "summarise_device",
          "summarise_report"
        ],
        "type": "string",
        "description": "* `launch_runbook` - Launch runbook\n* `create_issue` - Create issue\n* `link_issue` - Link issue\n* `summarise_device` - Summarise device\n* `summarise_report` - Summarise report"
      },
      "PatchedKbTemplateRequest": {
        "type": "object",
        "properties": {
          "scope": {
            "$ref": "#/components/schemas/ScopeEnum"
          },
          "space": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "key": {
            "type": "string",
            "maxLength": 80
          },
          "content_type": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "category": {
            "type": "string",
            "maxLength": 100
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "body": {},
          "variables": {},
          "is_blueprint": {
            "type": "boolean"
          },
          "scaffold": {
            "nullable": true
          },
          "enabled": {
            "type": "boolean"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          }
        }
      },
      "PatchedOncallRotationRequest": {
        "type": "object",
        "properties": {
          "timeline": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "members": {},
          "cadence_rrule": {
            "type": "string",
            "maxLength": 500
          },
          "shift_length_hours": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "starts_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "KbAgentScopeModeEnum": {
        "enum": [
          "include",
          "exclude"
        ],
        "type": "string",
        "description": "* `include` - Include\n* `exclude` - Exclude"
      },
      "SmartLinkDomainRuleRequest": {
        "type": "object",
        "properties": {
          "pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "action": {
            "$ref": "#/components/schemas/SmartLinkDomainRuleActionEnum"
          },
          "allow_embed": {
            "type": "boolean"
          },
          "note": {
            "type": "string",
            "maxLength": 300
          }
        },
        "required": [
          "pattern"
        ]
      },
      "KbInlineCommentRequest": {
        "type": "object",
        "description": "Comment payload for the annotation rail \u2014 the existing page-comment fields\nplus inline-anchor status and the caller-aware reaction aggregate (\u00a77 embed).\n\n``anchor_status`` / ``promoted_issue_key`` are surfaced defensively via\n``getattr`` so the serializer is valid before those additive fields exist on\n``KbPageComment``; once the migration lands they read the real columns.",
        "properties": {
          "page": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "body": {
            "type": "string",
            "minLength": 1
          },
          "anchor": {},
          "resolved": {
            "type": "boolean"
          }
        },
        "required": [
          "body",
          "page"
        ]
      },
      "PatchedKbPageCommentRequest": {
        "type": "object",
        "properties": {
          "page": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "body": {
            "type": "string",
            "minLength": 1
          },
          "anchor": {},
          "resolved": {
            "type": "boolean"
          }
        }
      },
      "PatchedKbDatasetRequest": {
        "type": "object",
        "properties": {
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "restricted": {
            "type": "boolean"
          },
          "compliance": {
            "type": "boolean"
          }
        }
      },
      "KbTemplateRequest": {
        "type": "object",
        "properties": {
          "scope": {
            "$ref": "#/components/schemas/ScopeEnum"
          },
          "space": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "key": {
            "type": "string",
            "maxLength": 80
          },
          "content_type": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "category": {
            "type": "string",
            "maxLength": 100
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "body": {},
          "variables": {},
          "is_blueprint": {
            "type": "boolean"
          },
          "scaffold": {
            "nullable": true
          },
          "enabled": {
            "type": "boolean"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          }
        },
        "required": [
          "name"
        ]
      },
      "PaginatedKbAgentList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbAgent"
            }
          }
        }
      },
      "KbPageStatusEnum": {
        "enum": [
          "draft",
          "current",
          "archived"
        ],
        "type": "string",
        "description": "* `draft` - Draft\n* `current` - Current\n* `archived` - Archived"
      },
      "SmartLinkDomainRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "pattern": {
            "type": "string",
            "maxLength": 255
          },
          "action": {
            "$ref": "#/components/schemas/SmartLinkDomainRuleActionEnum"
          },
          "allow_embed": {
            "type": "boolean"
          },
          "note": {
            "type": "string",
            "maxLength": 300
          },
          "created_by_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "created_by_name",
          "id",
          "pattern"
        ]
      },
      "SmartLinkDomainRuleActionEnum": {
        "enum": [
          "allow",
          "deny"
        ],
        "type": "string",
        "description": "* `allow` - Allow\n* `deny` - Deny"
      },
      "PaginatedKbTemplateList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbTemplate"
            }
          }
        }
      },
      "PaginatedKbReactionList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbReaction"
            }
          }
        }
      },
      "Timeline": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "space_key": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "key": {
            "type": "string",
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "color": {
            "type": "string",
            "maxLength": 9
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "default_view": {
            "$ref": "#/components/schemas/DefaultViewEnum"
          },
          "timezone": {
            "type": "string",
            "maxLength": 64
          },
          "enabled_layers": {},
          "source_board": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "archived": {
            "type": "boolean"
          },
          "event_count": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "event_count",
          "id",
          "key",
          "name",
          "space",
          "space_key"
        ]
      },
      "PatchedKbLabelRequest": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "color": {
            "type": "string",
            "maxLength": 16
          },
          "kind": {
            "$ref": "#/components/schemas/KbLabelKindEnum"
          },
          "target_ref": {
            "type": "string",
            "maxLength": 200
          }
        }
      },
      "KbPageComment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "page": {
            "type": "string",
            "format": "uuid"
          },
          "parent": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "author_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "body": {
            "type": "string"
          },
          "anchor": {},
          "resolved": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "author_name",
          "body",
          "created_at",
          "id",
          "page"
        ]
      },
      "KbAgent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "key": {
            "type": "string",
            "maxLength": 60
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "purpose": {
            "type": "string",
            "maxLength": 300
          },
          "instructions": {
            "type": "string"
          },
          "provider": {
            "type": "integer",
            "nullable": true
          },
          "answer_style": {
            "$ref": "#/components/schemas/AnswerStyleEnum"
          },
          "temperature": {
            "type": "number",
            "format": "double"
          },
          "starter_prompts": {},
          "published": {
            "type": "boolean"
          },
          "created_by_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbAgentScope"
            },
            "readOnly": true
          },
          "agent_actions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KbAgentAction"
            },
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "agent_actions",
          "created_at",
          "created_by_name",
          "id",
          "key",
          "name",
          "scopes",
          "updated_at"
        ]
      },
      "KbDatasetView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "dataset": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "key": {
            "type": "string",
            "maxLength": 64
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "type": {
            "$ref": "#/components/schemas/KbDatasetViewTypeEnum"
          },
          "spec": {},
          "is_default": {
            "type": "boolean"
          },
          "position": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "dataset",
          "id",
          "key",
          "name",
          "updated_at"
        ]
      },
      "KbReactionRequest": {
        "type": "object",
        "description": "Thin serializer for a single reaction row. The primary read surface is the\naggregate (``kb_reactions_service.aggregate_from_qs``), not this.",
        "properties": {
          "target_type": {
            "$ref": "#/components/schemas/TargetTypeEnum"
          },
          "target_id": {
            "type": "string",
            "format": "uuid"
          },
          "emoji": {
            "type": "string",
            "minLength": 1,
            "maxLength": 24
          }
        },
        "required": [
          "emoji",
          "target_id",
          "target_type"
        ]
      },
      "AnswerStyleEnum": {
        "enum": [
          "concise",
          "detailed"
        ],
        "type": "string",
        "description": "* `concise` - Concise\n* `detailed` - Detailed"
      },
      "KbClipSourceEnum": {
        "enum": [
          "capture",
          "import",
          "psm"
        ],
        "type": "string",
        "description": "* `capture` - In-browser capture\n* `import` - Imported\n* `psm` - PSM session"
      },
      "PaginatedTimelineList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Timeline"
            }
          }
        }
      },
      "KbLabel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "slug": {
            "type": "string",
            "readOnly": true
          },
          "display": {
            "type": "string",
            "readOnly": true
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "color": {
            "type": "string",
            "maxLength": 16
          },
          "kind": {
            "$ref": "#/components/schemas/KbLabelKindEnum"
          },
          "target_ref": {
            "type": "string",
            "maxLength": 200
          },
          "usage_count": {
            "type": "integer",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "display",
          "id",
          "slug",
          "updated_at",
          "usage_count"
        ]
      },
      "KbAgentRequest": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "icon": {
            "type": "string",
            "maxLength": 40
          },
          "purpose": {
            "type": "string",
            "maxLength": 300
          },
          "instructions": {
            "type": "string"
          },
          "provider": {
            "type": "integer",
            "nullable": true
          },
          "answer_style": {
            "$ref": "#/components/schemas/AnswerStyleEnum"
          },
          "temperature": {
            "type": "number",
            "format": "double"
          },
          "starter_prompts": {},
          "published": {
            "type": "boolean"
          }
        },
        "required": [
          "key",
          "name"
        ]
      },
      "PatchedTimelineRequest": {
        "type": "object",
        "properties": {
          "space": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "color": {
            "type": "string",
            "maxLength": 9
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "default_view": {
            "$ref": "#/components/schemas/DefaultViewEnum"
          },
          "timezone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "enabled_layers": {},
          "source_board": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "archived": {
            "type": "boolean"
          }
        }
      },
      "ContentTypeEnum": {
        "enum": [
          "page",
          "slides",
          "whiteboard",
          "database",
          "embed"
        ],
        "type": "string",
        "description": "* `page` - Page\n* `slides` - Slides\n* `whiteboard` - Whiteboard\n* `database` - Database\n* `embed` - Embed"
      }
    }
  }
}