openapi: 3.1.0
info:
  title: Agent Brain Partner API
  version: 1.0.0
  description: Server-to-server API for integrations operating within one bound Agent Brain organization.
servers:
  - url: /v1/s2s
paths:
  /openapi.yaml:
    get:
      operationId: getPartnerOpenAPI
      summary: Download the Partner OpenAPI document
      security: []
      responses:
        "200":
          description: OpenAPI 3.1 YAML document
          content:
            application/yaml:
              schema:
                type: string
  /organization:
    get:
      operationId: getBoundOrganization
      x-agent-brain-scope: ab:organization:read
      summary: Get the organization bound to the API key
      security:
        - apiKey: []
      responses:
        "200":
          description: Bound organization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrganizationEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    patch:
      operationId: updateBoundOrganization
      x-agent-brain-scope: ab:organization:write
      summary: Update the organization bound to the API key
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrganizationUpdate"
      responses:
        "200":
          description: Updated bound organization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrganizationEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/branding/logo:
    get:
      operationId: getOrganizationBrandingLogo
      x-agent-brain-scope: ab:organization:read
      summary: Download the bound organization's branding logo
      security:
        - apiKey: []
      responses:
        "200":
          description: Branding logo bytes
          headers:
            ETag:
              schema:
                type: string
          content:
            image/png:
              schema:
                type: string
                format: binary
            image/jpeg:
              schema:
                type: string
                format: binary
            image/webp:
              schema:
                type: string
                format: binary
        "304":
          description: Logo has not changed
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    put:
      operationId: uploadOrganizationBrandingLogo
      x-agent-brain-scope: ab:organization:admin
      summary: Upload the bound organization's branding logo
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [logo]
              properties:
                logo:
                  type: string
                  format: binary
          image/png:
            schema:
              type: string
              format: binary
          image/jpeg:
            schema:
              type: string
              format: binary
          image/webp:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: Logo uploaded
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedMediaType"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    delete:
      operationId: deleteOrganizationBrandingLogo
      x-agent-brain-scope: ab:organization:admin
      summary: Delete the bound organization's branding logo
      security:
        - apiKey: []
      responses:
        "204":
          description: Logo deleted
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/members:
    get:
      operationId: listOrganizationMembers
      x-agent-brain-scope: ab:member:read
      summary: List members of the bound organization
      security:
        - apiKey: []
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
          description: Opaque cursor returned by the previous page.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: role
          in: query
          schema:
            type: string
            enum: [owner, admin, member]
        - name: status
          in: query
          schema:
            type: string
            enum: [active, invited]
        - name: search
          in: query
          schema:
            type: string
          description: Trimmed user email/name search; values shorter than three characters are ignored.
      responses:
        "200":
          description: Cursor-paginated bound organization members
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MemberListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/members/{memberId}:
    parameters:
      - name: memberId
        in: path
        required: true
        description: Organization membership identifier returned by the member list.
        schema:
          type: string
          format: uuid
    patch:
      operationId: updateOrganizationMemberRole
      x-agent-brain-scope: ab:member:write
      summary: Update a member role in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MemberRoleUpdateRequest"
      responses:
        "204":
          description: Member role updated
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    delete:
      operationId: removeOrganizationMember
      x-agent-brain-scope: ab:member:write
      summary: Remove a member or cancel a pending invitation in the bound organization
      security:
        - apiKey: []
      responses:
        "204":
          description: Member removed or pending invitation cancelled
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/members/invitations:
    post:
      operationId: inviteOrganizationMember
      x-agent-brain-scope: ab:member:write
      summary: Invite a member to the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MemberInvitationRequest"
      responses:
        "201":
          description: Member invitation created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MemberInvitationEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/members/{memberId}/resend-invitation:
    parameters:
      - name: memberId
        in: path
        required: true
        description: Pending organization membership identifier returned by the member list.
        schema:
          type: string
          format: uuid
    post:
      operationId: resendOrganizationMemberInvitation
      x-agent-brain-scope: ab:member:write
      summary: Resend a pending member invitation for the bound organization
      security:
        - apiKey: []
      responses:
        "200":
          description: Invitation resent
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MemberInvitationResendEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "410":
          $ref: "#/components/responses/Gone"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/settings/audit:
    get:
      operationId: getOrganizationAuditSettings
      x-agent-brain-scope: ab:organization:read
      summary: Get audit settings for the bound organization
      security:
        - apiKey: []
      responses:
        "200":
          description: Audit settings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuditSettingsEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    patch:
      operationId: updateOrganizationAuditSettings
      x-agent-brain-scope: ab:organization:admin
      summary: Partially update audit settings for the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AuditSettingsUpdate"
      responses:
        "200":
          description: Updated audit settings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuditSettingsEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/settings/permission-inheritance:
    get:
      operationId: getOrganizationPermissionInheritanceSettings
      x-agent-brain-scope: ab:organization:read
      summary: Get permission inheritance settings for the bound organization
      security:
        - apiKey: []
      responses:
        "200":
          description: Permission inheritance settings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionInheritanceSettingsEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    put:
      operationId: updateOrganizationPermissionInheritanceSettings
      x-agent-brain-scope: ab:organization:admin
      summary: Replace permission inheritance settings for the bound organization (owner only)
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PermissionInheritanceSettingsUpdate"
      responses:
        "200":
          description: Updated permission inheritance settings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionInheritanceSettingsEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /organization/settings/kg-runtime:
    get:
      operationId: getOrganizationKGRuntimeSettings
      x-agent-brain-scope: ab:organization:read
      summary: Get Knowledge Graph runtime settings for the bound organization
      security:
        - apiKey: []
      responses:
        "200":
          description: Knowledge Graph runtime settings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KGRuntimeSettingsEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    patch:
      operationId: updateOrganizationKGRuntimeSettings
      x-agent-brain-scope: ab:organization:admin
      summary: Update Knowledge Graph runtime settings for the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KGRuntimeSettingsUpdate"
      responses:
        "200":
          description: Updated Knowledge Graph runtime settings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KGRuntimeSettingsEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /me:
    get:
      operationId: getPartnerProfile
      x-agent-brain-scope: ab:profile:read
      summary: Get the Partner API key owner's Agent Brain profile
      security:
        - apiKey: []
      responses:
        "200":
          description: Current Partner principal profile
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /me/profile:
    patch:
      operationId: updatePartnerProfile
      x-agent-brain-scope: ab:profile:write
      summary: Update the Partner API key owner's Agent Brain profile
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProfileUpdate"
      responses:
        "200":
          description: Updated profile
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProfileEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /users/search:
    get:
      operationId: searchOrganizationUsers
      x-agent-brain-scope: ab:user:read
      summary: Search active users in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: q
          in: query
          required: true
          description: Leading and trailing whitespace is ignored; length constraints apply after trimming.
          schema:
            type: string
            minLength: 2
            maxLength: 64
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
      responses:
        "200":
          description: Matching active users from the bound organization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserSearchEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /permission-groups:
    get:
      operationId: listPermissionGroups
      x-agent-brain-scope: ab:permission:read
      summary: List permission groups in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: name
          in: query
          description: Case-insensitive literal substring; SQL wildcard characters have no special meaning.
          schema:
            type: string
            maxLength: 100
        - name: groupType
          in: query
          schema:
            type: string
            enum: [access_group, department]
        - name: parentId
          in: query
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Cursor-paginated permission groups from the bound organization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionGroupListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    post:
      operationId: createPermissionGroup
      x-agent-brain-scope: ab:permission:write
      summary: Create a permission group in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PermissionGroupCreate"
      responses:
        "201":
          description: Permission group created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IDEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /permission-groups/{groupId}:
    get:
      operationId: getPermissionGroup
      x-agent-brain-scope: ab:permission:read
      summary: Get one permission group in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Permission group from the bound organization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionGroupEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    patch:
      operationId: updatePermissionGroup
      x-agent-brain-scope: ab:permission:write
      summary: Update a permission group in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PermissionGroupUpdate"
      responses:
        "204":
          description: Permission group updated
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    delete:
      operationId: deletePermissionGroup
      x-agent-brain-scope: ab:permission:write
      summary: Delete an unused permission group in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Permission group deleted
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /permission-groups/{groupId}/users:
    get:
      operationId: listPermissionGroupUsers
      x-agent-brain-scope: ab:permission:read
      summary: List active members assigned to a permission group in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Cursor-paginated active assignments for the permission group
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionAssignmentListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /permission-groups/{groupId}/table-permissions:
    parameters:
      - name: groupId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      operationId: listGroupTablePermissions
      x-agent-brain-scope: ab:permission:read
      summary: List table permissions assigned to a permission group in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: connectorId
          in: query
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Cursor-paginated table permissions from the bound organization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TablePermissionListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    post:
      operationId: createGroupTablePermission
      x-agent-brain-scope: ab:permission:write
      summary: Create a table permission for a permission group in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TablePermissionCreate"
      responses:
        "201":
          description: Table permission created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IDEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /permission-groups/{groupId}/table-permissions/{tablePermissionId}:
    parameters:
      - name: groupId
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: tablePermissionId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    patch:
      operationId: updateGroupTablePermission
      x-agent-brain-scope: ab:permission:write
      summary: Update a table permission in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TablePermissionUpdate"
      responses:
        "204":
          description: Table permission updated
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    delete:
      operationId: deleteGroupTablePermission
      x-agent-brain-scope: ab:permission:write
      summary: Delete a table permission in the bound organization
      security:
        - apiKey: []
      responses:
        "204":
          description: Table permission deleted
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /users/{userId}/permission-groups:
    get:
      operationId: listUserPermissionGroups
      x-agent-brain-scope: ab:permission:read
      summary: List active permission-group assignments for a member of the bound organization
      security:
        - apiKey: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Cursor-paginated active permission-group assignments for the user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionAssignmentListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    post:
      operationId: assignUserPermissionGroup
      x-agent-brain-scope: ab:permission:write
      summary: Assign an active member to a permission group in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AssignPermissionGroupRequest"
      responses:
        "201":
          description: Permission-group assignment created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IDEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /users/{userId}/permission-groups/{groupId}:
    delete:
      operationId: removeUserPermissionGroup
      x-agent-brain-scope: ab:permission:write
      summary: Remove a permission-group assignment in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: groupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Permission-group assignment removed
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /users/{userId}/primary-department:
    post:
      operationId: reassignUserPrimaryDepartment
      x-agent-brain-scope: ab:permission:write
      summary: Atomically reassign an active member's primary department in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReassignPrimaryDepartmentRequest"
      responses:
        "204":
          description: Primary department reassigned
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /resource-permissions:
    get:
      operationId: listResourcePermissions
      x-agent-brain-scope: ab:permission:read
      summary: List direct grants on one resource in the bound organization
      security:
        - apiKey: []
      parameters:
        - $ref: "#/components/parameters/ResourceType"
        - $ref: "#/components/parameters/ResourceID"
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Cursor-paginated direct grants
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourcePermissionListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /resource-permissions/grants:
    post:
      operationId: grantResourcePermissions
      x-agent-brain-scope: ab:permission:write
      summary: Idempotently grant actions on one or more resources in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ResourcePermissionMutation"
      responses:
        "204":
          description: Grants applied
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /resource-permissions/revocations:
    post:
      operationId: revokeResourcePermissions
      x-agent-brain-scope: ab:permission:write
      summary: Revoke actions on one or more resources in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ResourcePermissionMutation"
      responses:
        "204":
          description: Grants revoked
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /resource-permissions/check:
    get:
      operationId: checkResourcePermission
      x-agent-brain-scope: ab:permission:read
      summary: Check a target same-organization user's effective permission
      security:
        - apiKey: []
      parameters:
        - $ref: "#/components/parameters/ResourceType"
        - $ref: "#/components/parameters/ResourceID"
        - $ref: "#/components/parameters/PermissionAction"
        - $ref: "#/components/parameters/TargetUserID"
      responses:
        "200":
          description: Effective permission decision
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionCheckResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /resource-permissions/check-default:
    get:
      operationId: checkDefaultResourcePermission
      x-agent-brain-scope: ab:permission:read
      summary: Check a target same-organization user's organization-default permission
      security:
        - apiKey: []
      parameters:
        - $ref: "#/components/parameters/ResourceType"
        - $ref: "#/components/parameters/PermissionAction"
        - $ref: "#/components/parameters/TargetUserID"
      responses:
        "200":
          description: Effective organization-default permission decision
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermissionCheckResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /resource-permissions/audit:
    get:
      operationId: listResourcePermissionAudit
      x-agent-brain-scope: ab:permission:read
      summary: List grant and revoke audit rows for one resource
      security:
        - apiKey: []
      parameters:
        - $ref: "#/components/parameters/ResourceType"
        - $ref: "#/components/parameters/ResourceID"
        - name: from
          in: query
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          schema:
            type: string
            format: date-time
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Cursor-paginated resource permission audit
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourcePermissionAuditListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /search:
    post:
      operationId: searchKnowledge
      x-agent-brain-scope: ab:knowledge:read
      summary: Search accessible Knowledge in the bound organization
      description: Runs bounded full-text search only. Resource types, semantic mode, offsets and tenant selectors are not accepted.
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KnowledgeSearchRequest"
      responses:
        "200":
          description: Accessible Knowledge search results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeSearchEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /retrieve-context:
    post:
      operationId: retrieveKnowledgeContext
      x-agent-brain-scope: ab:knowledge:read
      summary: Retrieve model-ready context from accessible Knowledge
      description: Uses the Knowledge branch only. KG graph data and MCP follow-up instructions are not part of this Partner contract.
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KnowledgeContextRequest"
      responses:
        "200":
          description: Accessible Knowledge evidence and citations
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeContextEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges:
    get:
      operationId: listKnowledges
      x-agent-brain-scope: ab:knowledge:read
      summary: List accessible Knowledge in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: search
          in: query
          schema:
            type: string
            maxLength: 200
        - name: status
          in: query
          schema:
            type: string
            maxLength: 20
        - name: categoryId
          in: query
          schema:
            type: string
            format: uuid
        - name: createdBy
          in: query
          schema:
            type: string
            format: uuid
        - name: folderId
          in: query
          schema:
            type: string
            format: uuid
        - name: unfiled
          in: query
          schema:
            type: boolean
        - name: tagId
          in: query
          schema:
            type: string
            format: uuid
        - name: sourceKind
          in: query
          schema:
            type: string
            maxLength: 30
      responses:
        "200":
          description: Cursor-paginated accessible Knowledge
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    post:
      operationId: createKnowledge
      x-agent-brain-scope: ab:knowledge:write
      summary: Create Knowledge in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KnowledgeCreate"
      responses:
        "201":
          description: Knowledge created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IDEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}:
    parameters:
      - name: knowledgeId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      operationId: getKnowledge
      x-agent-brain-scope: ab:knowledge:read
      summary: Get accessible Knowledge by ID
      security:
        - apiKey: []
      responses:
        "200":
          description: Knowledge detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    patch:
      operationId: updateKnowledge
      x-agent-brain-scope: ab:knowledge:write
      summary: Update accessible Knowledge
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KnowledgeUpdate"
      responses:
        "204":
          description: Knowledge updated
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    delete:
      operationId: deleteKnowledge
      x-agent-brain-scope: ab:knowledge:write
      summary: Delete accessible Knowledge
      security:
        - apiKey: []
      responses:
        "204":
          description: Knowledge deleted
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/related:
    get:
      operationId: getRelatedKnowledges
      x-agent-brain-scope: ab:knowledge:read
      summary: List accessible Knowledge related to one live source in the bound organization
      security:
        - apiKey: []
      parameters:
        - name: knowledgeId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: k
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 20
            default: 5
      responses:
        "200":
          description: Related Knowledge filtered by the caller's read ACL
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RelatedKnowledgeEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/versions:
    get:
      operationId: listKnowledgeVersions
      x-agent-brain-scope: ab:knowledge:read
      summary: List version metadata for accessible Knowledge
      security:
        - apiKey: []
      parameters:
        - name: knowledgeId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Cursor-paginated Knowledge version metadata
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeVersionListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/versions/{versionNumber}:
    parameters:
      - name: knowledgeId
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: versionNumber
        in: path
        required: true
        schema:
          type: integer
          minimum: 1
    get:
      operationId: getKnowledgeVersion
      x-agent-brain-scope: ab:knowledge:read
      summary: Get one version of accessible Knowledge
      security:
        - apiKey: []
      responses:
        "200":
          description: Knowledge version including historical content
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeVersionEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/versions/{versionNumber}/rollback:
    post:
      operationId: rollbackKnowledgeVersion
      x-agent-brain-scope: ab:knowledge:write
      summary: Roll back Knowledge content when the bound principal has resource admin ACL
      security:
        - apiKey: []
      parameters:
        - name: knowledgeId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: versionNumber
          in: path
          required: true
          schema:
            type: integer
            minimum: 1
      responses:
        "204":
          description: Knowledge rolled back atomically
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/shares:
    parameters:
      - name: knowledgeId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      operationId: listKnowledgeShares
      x-agent-brain-scope: ab:knowledge:read
      summary: List active share links when the bound principal has resource admin ACL
      description: The response never contains the plaintext token, a token preview, the parent Knowledge ID, or creator identity.
      security:
        - apiKey: []
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Cursor-paginated active Knowledge shares with no token material
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgeShareListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    post:
      operationId: createKnowledgeShare
      x-agent-brain-scope: ab:knowledge:write
      summary: Create a share link when the bound principal has resource admin ACL
      description: The plaintext share token is returned exactly once in this response and is never available from list operations.
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateKnowledgeShareRequest"
      responses:
        "201":
          description: Knowledge share created; capture the one-time plaintext token now
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateKnowledgeShareEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/shares/{shareId}:
    delete:
      operationId: revokeKnowledgeShare
      x-agent-brain-scope: ab:knowledge:write
      summary: Revoke an active share link when the bound principal has resource admin ACL
      security:
        - apiKey: []
      parameters:
        - name: knowledgeId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: shareId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Knowledge share revoked
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/publication:
    parameters:
      - name: knowledgeId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      operationId: publishKnowledge
      x-agent-brain-scope: ab:knowledge:write
      summary: Publish Knowledge when the bound principal has resource admin ACL
      security:
        - apiKey: []
      responses:
        "200":
          description: Knowledge publication state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgePublicationEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    delete:
      operationId: unpublishKnowledge
      x-agent-brain-scope: ab:knowledge:write
      summary: Unpublish Knowledge when the bound principal has resource admin ACL
      security:
        - apiKey: []
      responses:
        "200":
          description: Knowledge publication state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KnowledgePublicationEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /knowledges/{knowledgeId}/kg/row-facts/source:
    parameters:
      - {name: knowledgeId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getKGRowFactSource
      x-agent-brain-scope: ab:kg:read
      summary: Describe the tabular source a row-fact run would read
      description: >-
        Answers with the column headers, the row count and a recommended mapping. The cells behind those headers are
        customer data and are not published here — build a mapping from the column names, then read the proposals the
        run produces. A Knowledge that carries no CSV or TSV content answers 400; one that is unknown, deleted or owned
        by another organization answers 404, identically in every case.
      security: [{apiKey: []}]
      responses:
        "200": {description: Row-fact source summary, content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactSourceEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /knowledges/{knowledgeId}/kg/row-facts/runs:
    parameters:
      - {name: knowledgeId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: createKGRowFactRun
      x-agent-brain-scope: ab:kg:execute
      summary: Generate row-fact proposals from a Knowledge's tabular source
      description: >-
        Reads the source, builds one proposal per row against the supplied mapping and answers 201 with the run and the
        first page of its proposals. Nothing reaches the graph until those proposals are promoted. Generation is not
        deduplicated: two identical submissions produce two runs, so there is no Idempotency-Key requirement and none is
        honoured. Because of that, and because one call can parse a thousand rows, this route is rate limited per API
        key and answers 429 with Retry-After once the budget is spent. A run above the documented row limit is refused
        rather than clamped. When the audit record cannot be written the request is refused with 503 and no run is
        created.
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactGenerateRequest"}}}
      responses:
        "201": {description: Row-fact run with its first page of proposals, content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactRunDetailEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /categories:
    get:
      operationId: listCategories
      x-agent-brain-scope: ab:knowledge:read
      summary: List Categories from the bound organization
      security:
        - apiKey: []
      parameters:
        - name: cursor
          in: query
          schema: {type: string}
        - name: limit
          in: query
          schema: {type: integer, minimum: 1, maximum: 100, default: 20}
        - name: name
          in: query
          schema: {type: string, maxLength: 255}
        - name: status
          in: query
          schema: {type: string, enum: [active, inactive]}
        - name: parentId
          in: query
          schema: {type: string, format: uuid}
      responses:
        "200":
          description: Cursor-paginated live Categories
          content:
            application/json:
              schema: {$ref: "#/components/schemas/CategoryListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createCategory
      x-agent-brain-scope: ab:knowledge:write
      summary: Create a Category in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/CategoryCreate"}
      responses:
        "201":
          description: Category created
          content:
            application/json:
              schema: {$ref: "#/components/schemas/IDEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /categories/tree:
    get:
      operationId: getCategoryTree
      x-agent-brain-scope: ab:knowledge:read
      summary: Get the deterministic Category tree from the bound organization
      security:
        - apiKey: []
      parameters:
        - name: status
          in: query
          schema: {type: string, enum: [active, inactive]}
      responses:
        "200":
          description: Category hierarchy
          content:
            application/json:
              schema: {$ref: "#/components/schemas/CategoryTreeEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /categories/{categoryId}:
    parameters:
      - name: categoryId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: getCategory
      x-agent-brain-scope: ab:knowledge:read
      summary: Get a Category from the bound organization
      security:
        - apiKey: []
      responses:
        "200":
          description: Category detail
          content:
            application/json:
              schema: {$ref: "#/components/schemas/CategoryEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    patch:
      operationId: updateCategory
      x-agent-brain-scope: ab:knowledge:write
      summary: Update a Category in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/CategoryUpdate"}
      responses:
        "204": {description: Category updated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteCategory
      x-agent-brain-scope: ab:knowledge:write
      summary: Delete a leaf Category in the bound organization
      security:
        - apiKey: []
      responses:
        "204": {description: Category deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /tags:
    get:
      operationId: listTags
      x-agent-brain-scope: ab:knowledge:read
      summary: List Tags from the bound organization
      security:
        - apiKey: []
      parameters:
        - name: cursor
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: name
          in: query
          schema:
            type: string
            maxLength: 100
        - name: status
          in: query
          schema:
            type: string
            enum: [active, inactive]
      responses:
        "200":
          description: Cursor-paginated live Tags
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TagListEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    post:
      operationId: createTag
      x-agent-brain-scope: ab:knowledge:write
      summary: Create a Tag in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TagCreate"
      responses:
        "201":
          description: Tag created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IDEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /tags/{tagId}:
    parameters:
      - name: tagId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      operationId: getTag
      x-agent-brain-scope: ab:knowledge:read
      summary: Get a Tag from the bound organization
      security:
        - apiKey: []
      responses:
        "200":
          description: Tag detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TagEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    patch:
      operationId: updateTag
      x-agent-brain-scope: ab:knowledge:write
      summary: Update a Tag in the bound organization
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TagUpdate"
      responses:
        "204":
          description: Tag updated
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    delete:
      operationId: deleteTag
      x-agent-brain-scope: ab:knowledge:write
      summary: Delete a Tag in the bound organization
      security:
        - apiKey: []
      responses:
        "204":
          description: Tag deleted
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /connectors:
    get:
      operationId: listConnectors
      x-agent-brain-scope: ab:connector:read
      summary: List Connectors from the bound organization
      security: [{apiKey: []}]
      parameters:
        - {name: cursor, in: query, schema: {type: string}}
        - name: limit
          in: query
          schema: {type: integer, minimum: 1, maximum: 100, default: 20}
        - name: type
          in: query
          schema: {type: string, enum: [credential, endpoint, secret_key, notification, storage]}
        - {name: subtype, in: query, schema: {type: string, maxLength: 100}}
        - name: status
          in: query
          schema: {type: string, enum: [active, inactive]}
        - name: visibility
          in: query
          schema: {type: string, enum: [private, internal, public]}
        - {name: search, in: query, schema: {type: string, maxLength: 200}}
      responses:
        "200":
          description: Cursor-paginated live Connectors
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createConnector
      x-agent-brain-scope: ab:connector:write
      summary: Create a Connector in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/ConnectorCreate"}
      responses:
        "201":
          description: Connector created
          content:
            application/json:
              schema: {$ref: "#/components/schemas/IDEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: getConnector
      x-agent-brain-scope: ab:connector:read
      summary: Get an authorized Connector from the bound organization
      security: [{apiKey: []}]
      responses:
        "200":
          description: Secret-free Connector detail
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    patch:
      operationId: updateConnector
      x-agent-brain-scope: ab:connector:admin
      summary: Update an authorized Connector in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/ConnectorUpdate"}
      responses:
        "204": {description: Connector updated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteConnector
      x-agent-brain-scope: ab:connector:admin
      summary: Delete an authorized Connector from the bound organization
      security: [{apiKey: []}]
      responses:
        "204": {description: Connector deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/test-config:
    post:
      operationId: testConnectorConfig
      x-agent-brain-scope: ab:connector:write
      summary: Test a write-only candidate Connector config
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/ConnectorTestConfigRequest"}
      responses:
        "200":
          description: Secret-free connection test result
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorTestEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connector-subtypes:
    get:
      operationId: listConnectorSubtypes
      x-agent-brain-scope: ab:connector:read
      summary: List Connector setup subtypes from the bound organization
      security: [{apiKey: []}]
      parameters:
        - {name: cursor, in: query, schema: {type: string}}
        - name: limit
          in: query
          schema: {type: integer, minimum: 1, maximum: 100, default: 20}
        - name: type
          in: query
          schema: {type: string, enum: [credential, endpoint, secret_key, notification, storage]}
      responses:
        "200":
          description: Cursor-paginated Connector subtype catalog
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorSubtypeListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connector-subtypes/{subtypeId}:
    parameters:
      - name: subtypeId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: getConnectorSubtype
      x-agent-brain-scope: ab:connector:read
      summary: Get a Connector setup subtype from the bound organization
      security: [{apiKey: []}]
      responses:
        "200":
          description: Connector subtype setup metadata
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorSubtypeEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/databases:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: listConnectorDatabases
      x-agent-brain-scope: ab:connector:read
      summary: List databases visible through an authorized Connector
      security: [{apiKey: []}]
      responses:
        "200":
          description: Database catalog
          content:
            application/json:
              schema: {$ref: "#/components/schemas/DatabaseListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/schemas:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: listConnectorSchemas
      x-agent-brain-scope: ab:connector:read
      summary: List schemas visible through an authorized Connector
      security: [{apiKey: []}]
      parameters:
        - {name: database, in: query, schema: {type: string, maxLength: 255}}
      responses:
        "200":
          description: Schema catalog
          content:
            application/json:
              schema: {$ref: "#/components/schemas/SchemaListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/tables:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: listConnectorTables
      x-agent-brain-scope: ab:connector:read
      summary: List tables allowed by Connector and table ACLs
      security: [{apiKey: []}]
      parameters:
        - {name: database, in: query, schema: {type: string, maxLength: 255}}
        - {name: schema, in: query, schema: {type: string, maxLength: 255}}
      responses:
        "200":
          description: Authorized table catalog
          content:
            application/json:
              schema: {$ref: "#/components/schemas/TableListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/tables/{tableName}/columns:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
      - name: tableName
        in: path
        required: true
        schema: {type: string, minLength: 1, maxLength: 255}
    get:
      operationId: listConnectorTableColumns
      x-agent-brain-scope: ab:connector:read
      summary: List columns for a table allowed by Connector and table ACLs
      security: [{apiKey: []}]
      parameters:
        - {name: database, in: query, schema: {type: string, maxLength: 255}}
        - {name: schema, in: query, schema: {type: string, maxLength: 255}}
      responses:
        "200":
          description: Authorized column catalog
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ColumnListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/tables/{tableName}/data:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
      - name: tableName
        in: path
        required: true
        schema: {type: string, minLength: 1, maxLength: 255}
    get:
      operationId: previewConnectorTableData
      x-agent-brain-scope: ab:connector:execute
      summary: Preview rows allowed by Connector and table ACLs
      security: [{apiKey: []}]
      parameters:
        - {name: database, in: query, schema: {type: string, maxLength: 255}}
        - {name: schema, in: query, schema: {type: string, maxLength: 255}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 1000, default: 50}}
        - {name: offset, in: query, schema: {type: integer, minimum: 0, default: 0}}
        - {name: sortBy, in: query, schema: {type: string, maxLength: 255}}
        - {name: sortDir, in: query, schema: {type: string, enum: [asc, desc], default: asc}}
      responses:
        "200":
          description: Authorized table preview
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorPreviewEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/query:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
    post:
      operationId: queryConnectorSQL
      x-agent-brain-scope: ab:connector:execute
      summary: Run one side-effect-free SQL query through an authorized Connector
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/ConnectorSQLQueryRequest"}
      responses:
        "200":
          description: Bounded SQL query result
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorQueryEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/execute:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
    post:
      operationId: executeConnectorSQL
      x-agent-brain-scope: ab:connector:execute
      summary: Run one supported write or DDL SQL statement through an authorized Connector
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/ConnectorSQLExecuteRequest"}
      responses:
        "200":
          description: SQL execution result
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorExecuteEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /connectors/{connectorId}/test:
    parameters:
      - name: connectorId
        in: path
        required: true
        schema: {type: string, format: uuid}
    post:
      operationId: testConnector
      x-agent-brain-scope: ab:connector:execute
      summary: Test an authorized existing Connector
      security: [{apiKey: []}]
      responses:
        "200":
          description: Secret-free connection test result
          content:
            application/json:
              schema: {$ref: "#/components/schemas/ConnectorTestEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-templates:
    get:
      operationId: listPromptTemplates
      x-agent-brain-scope: ab:prompt-template:read
      summary: List Prompt Templates in the bound organization
      security: [{apiKey: []}]
      parameters:
        - {name: useCase, in: query, schema: {type: string}}
        - {name: pipelineStep, in: query, schema: {type: string, enum: [step1, step2]}}
        - {name: status, in: query, schema: {type: string, enum: [active, inactive]}}
        - {name: isDefault, in: query, schema: {type: boolean}}
        - {name: search, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 200, default: 20}}
        - {name: offset, in: query, schema: {type: integer, minimum: 0, default: 0}}
      responses:
        "200": {description: Prompt Template list, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createPromptTemplate
      x-agent-brain-scope: ab:prompt-template:write
      summary: Create an organization Prompt Template
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateCreate"}}}}
      responses:
        "201": {description: Prompt Template created, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-templates/resolve:
    get:
      operationId: resolvePromptTemplate
      x-agent-brain-scope: ab:prompt-template:read
      summary: Resolve the effective Prompt Template for a pipeline context
      security: [{apiKey: []}]
      parameters:
        - {name: useCase, in: query, required: true, schema: {type: string}}
        - {name: pipelineStep, in: query, schema: {type: string, enum: [step1, step2]}}
        - {name: overrideId, in: query, schema: {type: string, format: uuid}}
        - {name: assetDefaultId, in: query, schema: {type: string, format: uuid}}
        - {name: folderId, in: query, schema: {type: string, format: uuid}}
      responses:
        "200": {description: Resolved Prompt Template, content: {application/json: {schema: {$ref: "#/components/schemas/ResolvedPromptTemplateEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-templates/{templateId}:
    parameters:
      - {name: templateId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getPromptTemplate
      x-agent-brain-scope: ab:prompt-template:read
      summary: Get a Prompt Template in the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: Prompt Template, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    patch:
      operationId: updatePromptTemplate
      x-agent-brain-scope: ab:prompt-template:write
      summary: Update a Prompt Template
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateUpdate"}}}}
      responses:
        "200": {description: Prompt Template updated, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deletePromptTemplate
      x-agent-brain-scope: ab:prompt-template:write
      summary: Delete an organization Prompt Template
      security: [{apiKey: []}]
      responses:
        "204": {description: Prompt Template deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-templates/{templateId}/default:
    parameters:
      - {name: templateId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: makePromptTemplateDefault
      x-agent-brain-scope: ab:prompt-template:write
      summary: Make a Prompt Template the organization default for its scope
      security: [{apiKey: []}]
      responses:
        "200": {description: Default Prompt Template, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-templates/{templateId}/versions:
    parameters:
      - {name: templateId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listPromptTemplateVersions
      x-agent-brain-scope: ab:prompt-template:read
      summary: List Prompt Template versions
      security: [{apiKey: []}]
      parameters:
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 200, default: 20}}
        - {name: offset, in: query, schema: {type: integer, minimum: 0, default: 0}}
      responses:
        "200": {description: Prompt Template versions, content: {application/json: {schema: {$ref: "#/components/schemas/PromptTemplateVersionListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-use-case-types:
    get:
      operationId: listPromptUseCaseTypes
      x-agent-brain-scope: ab:prompt-template:read
      summary: List Prompt use-case types in the bound organization
      security: [{apiKey: []}]
      parameters:
        - {name: active, in: query, schema: {type: boolean, default: true}}
      responses:
        "200": {description: Prompt use-case types, content: {application/json: {schema: {$ref: "#/components/schemas/PromptUseCaseListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createPromptUseCaseType
      x-agent-brain-scope: ab:prompt-template:write
      summary: Create an organization Prompt use-case type
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/PromptUseCaseCreate"}}}}
      responses:
        "201": {description: Prompt use-case type created, content: {application/json: {schema: {$ref: "#/components/schemas/PromptUseCaseEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-use-case-types/{useCase}:
    parameters:
      - {name: useCase, in: path, required: true, schema: {type: string, pattern: "^[a-z0-9-]+$", maxLength: 40}}
    patch:
      operationId: updatePromptUseCaseType
      x-agent-brain-scope: ab:prompt-template:write
      summary: Update a Prompt use-case type
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/PromptUseCaseUpdate"}}}}
      responses:
        "204": {description: Prompt use-case type updated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deletePromptUseCaseType
      x-agent-brain-scope: ab:prompt-template:write
      summary: Deactivate an organization Prompt use-case type
      security: [{apiKey: []}]
      responses:
        "204": {description: Prompt use-case type deactivated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /prompt-use-case-types/{useCase}/active:
    parameters:
      - {name: useCase, in: path, required: true, schema: {type: string, pattern: "^[a-z0-9-]+$", maxLength: 40}}
    patch:
      operationId: setPromptUseCaseTypeActive
      x-agent-brain-scope: ab:prompt-template:write
      summary: Activate or deactivate a Prompt use-case type
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/PromptUseCaseActiveUpdate"}}}}
      responses:
        "204": {description: Prompt use-case active state updated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /query-logs:
    get:
      operationId: listQueryLogs
      x-agent-brain-scope: ab:query-log:read
      summary: List query audit logs for the bound organization
      security: [{apiKey: []}]
      parameters:
        - name: cursor
          in: query
          schema: {type: string}
        - name: limit
          in: query
          schema: {type: integer, minimum: 1, maximum: 100, default: 20}
        - name: connectorId
          in: query
          schema: {type: string, format: uuid}
        - name: userId
          in: query
          schema: {type: string, format: uuid}
        - name: apiKeyId
          in: query
          schema: {type: string, format: uuid}
        - name: source
          in: query
          schema: {type: string, enum: [cms, bot, n8n, workflow, etl, mcp, partner_s2s]}
        - name: sourceId
          in: query
          schema: {type: string, maxLength: 50}
        - name: status
          in: query
          schema: {type: string, enum: [authorized, success, denied, error]}
        - name: from
          in: query
          schema: {type: string, format: date-time}
        - name: to
          in: query
          schema: {type: string, format: date-time}
      responses:
        "200":
          description: Cursor-paginated, secret-safe query audit logs
          content:
            application/json:
              schema: {$ref: "#/components/schemas/QueryLogListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /query-logs/{queryLogId}:
    parameters:
      - name: queryLogId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: getQueryLog
      x-agent-brain-scope: ab:query-log:read
      summary: Get one query audit log from the bound organization
      security: [{apiKey: []}]
      responses:
        "200":
          description: Secret-safe query audit log
          content:
            application/json:
              schema: {$ref: "#/components/schemas/QueryLogEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows:
    get:
      operationId: listWorkflows
      x-agent-brain-scope: ab:workflow:read
      summary: List accessible Workflows in the bound organization
      security: [{apiKey: []}]
      parameters:
        - {name: cursor, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
        - {name: search, in: query, schema: {type: string, maxLength: 200}}
        - {name: status, in: query, schema: {type: string, maxLength: 20}}
        - {name: kind, in: query, schema: {type: string, enum: [etl, agentic, mixed]}}
        - {name: scheduleType, in: query, schema: {type: string, enum: [manual, cron, once]}}
        - {name: isEnabled, in: query, schema: {type: boolean}}
        - {name: folderId, in: query, schema: {type: string, format: uuid}}
        - {name: unfiled, in: query, schema: {type: boolean}}
      responses:
        "200": {description: Cursor-paginated accessible Workflows, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createWorkflow
      x-agent-brain-scope: ab:workflow:write
      summary: Create a Workflow in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowCreate"}
      responses:
        "201": {description: Workflow created, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/{workflowId}:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getWorkflow
      x-agent-brain-scope: ab:workflow:read
      summary: Get an accessible Workflow definition
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow definition, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    put:
      operationId: updateWorkflow
      x-agent-brain-scope: ab:workflow:write
      summary: Update an authorized Workflow definition
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowUpdate"}
      responses:
        "204": {description: Workflow updated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteWorkflow
      x-agent-brain-scope: ab:workflow:write
      summary: Delete an admin-authorized Workflow
      security: [{apiKey: []}]
      responses:
        "204": {description: Workflow deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/{workflowId}/folder:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
    patch:
      operationId: moveWorkflowFolder
      x-agent-brain-scope: ab:workflow:write
      summary: Move a Workflow to a Folder or clear its Folder in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowFolderMove"}
      responses:
        "200": {description: Workflow Folder assignment updated, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/with-steps:
    post:
      operationId: createWorkflowWithSteps
      x-agent-brain-scope: ab:workflow:write
      summary: Create a Workflow and its steps atomically in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowWithStepsCreate"}
      responses:
        "201": {description: Workflow and steps created, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/{workflowId}/steps:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listWorkflowSteps
      x-agent-brain-scope: ab:workflow:read
      summary: List steps for an accessible Workflow
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow steps, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowStepListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createWorkflowStep
      x-agent-brain-scope: ab:workflow:write
      summary: Add a step to an authorized Workflow
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowStepCreate"}
      responses:
        "201": {description: Workflow step created, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/{workflowId}/steps/replace:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
    put:
      operationId: replaceWorkflowSteps
      x-agent-brain-scope: ab:workflow:write
      summary: Atomically replace every step in an authorized Workflow
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowStepsReplace"}
      responses:
        "204": {description: Workflow steps replaced}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/{workflowId}/steps/{stepId}:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
      - {name: stepId, in: path, required: true, schema: {type: string, format: uuid}}
    put:
      operationId: updateWorkflowStep
      x-agent-brain-scope: ab:workflow:write
      summary: Update a step belonging to an authorized Workflow
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowStepUpdate"}
      responses:
        "204": {description: Workflow step updated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteWorkflowStep
      x-agent-brain-scope: ab:workflow:write
      summary: Delete a step belonging to an authorized Workflow
      security: [{apiKey: []}]
      responses:
        "204": {description: Workflow step deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/{workflowId}/runs:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listWorkflowRuns
      x-agent-brain-scope: ab:workflow:read
      summary: List runs for an accessible Workflow
      security: [{apiKey: []}]
      parameters:
        - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
        - {name: status, in: query, schema: {type: string}}
        - {name: triggeredBy, in: query, schema: {type: string}}
      responses:
        "200": {description: Workflow runs, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowRunListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    post:
      operationId: triggerWorkflowRun
      x-agent-brain-scope: ab:workflow:execute
      summary: Queue a run for an authorized Workflow
      security: [{apiKey: []}]
      parameters:
        - {$ref: "#/components/parameters/IdempotencyKey"}
      responses:
        "202": {description: Workflow run queued, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflows/{workflowId}/stats:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getWorkflowStats
      x-agent-brain-scope: ab:workflow:read
      summary: Get runtime statistics for an accessible Workflow
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow statistics, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowStatsEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflow-runs/{runId}:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getWorkflowRun
      x-agent-brain-scope: ab:workflow:read
      summary: Get one accessible Workflow run
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow run, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowRunEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflow-runs/{runId}/cancel:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: cancelWorkflowRun
      x-agent-brain-scope: ab:workflow:execute
      summary: Cancel an authorized Workflow run
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow run cancellation accepted, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflow-runs/{runId}/resume:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: resumeWorkflowRun
      x-agent-brain-scope: ab:workflow:execute
      summary: Resume an authorized non-approval Workflow run
      security: [{apiKey: []}]
      requestBody:
        required: false
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowRunResume"}
      responses:
        "202": {description: Workflow run resumed, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflow-runs/{runId}/retry:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: retryWorkflowRun
      x-agent-brain-scope: ab:workflow:execute
      summary: Queue a retry of an authorized Workflow run
      security: [{apiKey: []}]
      parameters:
        - {$ref: "#/components/parameters/IdempotencyKey"}
      responses:
        "202": {description: Workflow run retry queued, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflow-runs/{runId}/steps:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listWorkflowRunSteps
      x-agent-brain-scope: ab:workflow:read
      summary: List safe step activity for an accessible Workflow run
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow run steps, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowRunStepListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflow-runs/{runId}/events:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listWorkflowRunEvents
      x-agent-brain-scope: ab:workflow:read
      summary: List event metadata for an accessible Workflow run
      security: [{apiKey: []}]
      parameters:
        - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
      responses:
        "200": {description: Workflow run event metadata, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowRunEventListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflow-runs/{runId}/events/{eventId}:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
      - {name: eventId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getWorkflowRunEvent
      x-agent-brain-scope: ab:workflow:read
      summary: Get one event metadata record without its internal payload
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow run event metadata, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowRunEventEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflows/{workflowId}/webhooks:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listWorkflowWebhooks
      x-agent-brain-scope: ab:workflow:write
      summary: List webhook configurations for an authorized Workflow
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow webhooks without plaintext secrets, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowWebhookListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    post:
      operationId: createWorkflowWebhook
      x-agent-brain-scope: ab:workflow:write
      summary: Create a Workflow webhook and return its plaintext secret once
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowWebhookCreate"}
      responses:
        "201": {description: Workflow webhook with one-time secret, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowWebhookSecretEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflows/{workflowId}/webhooks/{triggerId}:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
      - {name: triggerId, in: path, required: true, schema: {type: string, format: uuid}}
    patch:
      operationId: updateWorkflowWebhook
      x-agent-brain-scope: ab:workflow:write
      summary: Update an authorized Workflow webhook
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowWebhookUpdate"}
      responses:
        "200": {description: Updated Workflow webhook without plaintext secret, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowWebhookEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    delete:
      operationId: deleteWorkflowWebhook
      x-agent-brain-scope: ab:workflow:write
      summary: Delete an authorized Workflow webhook
      security: [{apiKey: []}]
      responses:
        "204": {description: Workflow webhook deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflows/{workflowId}/webhooks/{triggerId}/rotate-secret:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
      - {name: triggerId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: rotateWorkflowWebhookSecret
      x-agent-brain-scope: ab:workflow:write
      summary: Rotate a Workflow webhook secret and return the new plaintext once
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow webhook with one-time rotated secret, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowWebhookSecretEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /kg/entities:
    get:
      operationId: listKGEntities
      x-agent-brain-scope: ab:kg:read
      summary: List KG entities in the bound organization
      security: [{apiKey: []}]
      parameters:
        - {name: cursor, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
        - {name: kind, in: query, schema: {type: string, maxLength: 50}}
        - {name: includeOrphan, in: query, schema: {type: boolean, default: false}}
        - {name: q, in: query, description: Literal case-insensitive name search after trimming, schema: {type: string, maxLength: 200}}
      responses:
        "200": {description: KG entity page, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createKGEntity
      x-agent-brain-scope: ab:kg:write
      summary: Create a manual KG entity in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/CreateKGEntityRequest"}}}
      responses:
        "201": {description: KG entity created, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entities/{entityId}:
    parameters:
      - {name: entityId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getKGEntity
      x-agent-brain-scope: ab:kg:read
      summary: Get a KG entity in the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: KG entity, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    patch:
      operationId: updateKGEntity
      x-agent-brain-scope: ab:kg:write
      summary: Update a manual KG entity in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/UpdateKGEntityRequest"}}}
      responses:
        "200": {description: KG entity updated, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteKGEntity
      x-agent-brain-scope: ab:kg:write
      summary: Delete a KG entity in the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: KG entity deleted, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entities/{entityId}/relations:
    parameters:
      - {name: entityId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listKGEntityRelations
      x-agent-brain-scope: ab:kg:read
      summary: List enterprise relations connected to one entity in the bound organization
      security: [{apiKey: []}]
      parameters:
        - {name: cursor, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
        - {name: relationKind, in: query, schema: {type: string, maxLength: 100}}
      responses:
        "200": {description: KG relation page, content: {application/json: {schema: {$ref: "#/components/schemas/KGRelationListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/relations:
    post:
      operationId: createKGRelation
      x-agent-brain-scope: ab:kg:write
      summary: Create an enterprise KG relation in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/CreateKGRelationRequest"}}}
      responses:
        "201": {description: KG relation created, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/relations/{relationId}:
    parameters:
      - {name: relationId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getKGRelation
      x-agent-brain-scope: ab:kg:read
      summary: Get an enterprise KG relation in the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: KG relation, content: {application/json: {schema: {$ref: "#/components/schemas/KGRelationEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteKGRelation
      x-agent-brain-scope: ab:kg:write
      summary: Delete an enterprise KG relation in the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: KG relation deleted, content: {application/json: {schema: {$ref: "#/components/schemas/IDEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entity-types:
    get:
      operationId: listKGEntityTypes
      x-agent-brain-scope: ab:kg:read
      summary: List entity-type catalog rows for the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: Entity-type catalog, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityTypeListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createKGEntityType
      x-agent-brain-scope: ab:kg:write
      summary: Create a custom entity type in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/CreateKGEntityTypeRequest"}}}
      responses:
        "201": {description: Entity type created, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityTypeEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entity-types/{entityTypeId}:
    parameters:
      - {name: entityTypeId, in: path, required: true, schema: {type: string, format: uuid}}
    patch:
      operationId: updateKGEntityType
      x-agent-brain-scope: ab:kg:write
      summary: Update a custom entity type in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/UpdateKGEntityTypeRequest"}}}
      responses:
        "200": {description: Entity type updated, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityTypeEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteKGEntityType
      x-agent-brain-scope: ab:kg:write
      summary: Soft-delete a custom entity type in the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: Entity type deleted, content: {application/json: {schema: {$ref: "#/components/schemas/DeleteKGEntityTypeEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/relation-types:
    get:
      operationId: listKGRelationTypes
      x-agent-brain-scope: ab:kg:read
      summary: List relation-type catalog rows for the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: Relation-type catalog, content: {application/json: {schema: {$ref: "#/components/schemas/KGRelationTypeListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createKGRelationType
      x-agent-brain-scope: ab:kg:write
      summary: Create a custom relation type in the bound organization
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/CreateKGRelationTypeRequest"}}}}
      responses:
        "201": {description: Relation type created, content: {application/json: {schema: {$ref: "#/components/schemas/KGRelationTypeEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/relation-types/{relationTypeId}:
    parameters:
      - {name: relationTypeId, in: path, required: true, schema: {type: string, format: uuid}}
    patch:
      operationId: updateKGRelationType
      x-agent-brain-scope: ab:kg:write
      summary: Update a custom relation type in the bound organization
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/UpdateKGRelationTypeRequest"}}}}
      responses:
        "200": {description: Relation type updated, content: {application/json: {schema: {$ref: "#/components/schemas/KGRelationTypeEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteKGRelationType
      x-agent-brain-scope: ab:kg:write
      summary: Soft-delete a custom relation type in the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: Relation type deleted, content: {application/json: {schema: {$ref: "#/components/schemas/DeleteKGRelationTypeEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/extractions/replay:
    post:
      operationId: replayKGExtraction
      x-agent-brain-scope: ab:kg:admin
      summary: Replay one Knowledge extraction with durable idempotency
      security: [{apiKey: []}]
      parameters:
        - {name: Idempotency-Key, in: header, required: true, schema: {type: string, format: uuid}}
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/KGReplayRequest"}}}
      responses:
        "202": {description: Extraction replay queued or deduplicated, content: {application/json: {schema: {$ref: "#/components/schemas/KGReplayEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/dedup/candidates:
    get:
      operationId: listKGDedupCandidates
      x-agent-brain-scope: ab:kg:admin
      summary: List single-item KG dedup review candidates
      security: [{apiKey: []}]
      parameters:
        - {name: status, in: query, schema: {type: string, enum: [pending, dismissed, expired, all], default: pending}}
        - {name: method, in: query, schema: {type: string, enum: [embedding, exact_name, fuzzy, all], default: all}}
        - {name: autoRecommended, in: query, schema: {type: string, enum: ["true", "false", all], default: all}}
        - {name: cursor, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 50}}
      responses:
        "200": {description: Bound-organization dedup candidate page, content: {application/json: {schema: {$ref: "#/components/schemas/KGDedupCandidateListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /kg/dedup/candidates/{candidateId}/approve:
    parameters:
      - {name: candidateId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: approveKGDedupCandidate
      x-agent-brain-scope: ab:kg:admin
      summary: Merge one pending KG dedup candidate
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/KGDedupApproveRequest"}}}
      responses:
        "200": {description: Atomic merge result, content: {application/json: {schema: {$ref: "#/components/schemas/KGDedupApproveEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /kg/dedup/candidates/{candidateId}/dismiss:
    parameters:
      - {name: candidateId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: dismissKGDedupCandidate
      x-agent-brain-scope: ab:kg:admin
      summary: Dismiss one pending KG dedup candidate
      security: [{apiKey: []}]
      responses:
        "200": {description: Candidate dismissed, content: {application/json: {schema: {$ref: "#/components/schemas/KGDedupDismissEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /kg/merge-history:
    get:
      operationId: listKGMergeHistory
      x-agent-brain-scope: ab:kg:admin
      summary: List secret-safe KG merge history
      security: [{apiKey: []}]
      parameters:
        - {name: cursor, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 50}}
      responses:
        "200": {description: Bound-organization merge history page without raw snapshots or actor PII, content: {application/json: {schema: {$ref: "#/components/schemas/KGMergeHistoryListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /kg/merge-history/{mergeRecordId}/rollback:
    parameters:
      - {name: mergeRecordId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: rollbackKGMerge
      x-agent-brain-scope: ab:kg:admin
      summary: Roll back one bound-organization KG merge atomically
      security: [{apiKey: []}]
      responses:
        "200": {description: Idempotent rollback result, content: {application/json: {schema: {$ref: "#/components/schemas/KGMergeRollbackEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /checkpoints:
    get:
      operationId: listMyWorkflowCheckpoints
      x-agent-brain-scope: ab:workflow:approve
      summary: List checkpoints assigned to the bound API-key user
      security: [{apiKey: []}]
      parameters:
        - {name: status, in: query, schema: {type: string, enum: [pending, approved, rejected, expired, cancelled, all], default: pending}}
        - {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
      responses:
        "200": {description: Approver checkpoint inbox, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /checkpoints/{checkpointId}:
    parameters:
      - {name: checkpointId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getWorkflowCheckpoint
      x-agent-brain-scope: ab:workflow:approve
      summary: Get a checkpoint visible to its approver or Workflow reader
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow checkpoint detail, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /checkpoints/{checkpointId}/approve:
    parameters:
      - {name: checkpointId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: approveWorkflowCheckpoint
      x-agent-brain-scope: ab:workflow:approve
      summary: Approve a checkpoint as the bound API-key user
      security: [{apiKey: []}]
      requestBody:
        content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointApprove"}}}
      responses:
        "200": {description: Updated checkpoint, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "410": {$ref: "#/components/responses/Gone"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /checkpoints/{checkpointId}/reject:
    parameters:
      - {name: checkpointId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: rejectWorkflowCheckpoint
      x-agent-brain-scope: ab:workflow:approve
      summary: Reject a checkpoint as the bound API-key user
      security: [{apiKey: []}]
      requestBody:
        content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointReject"}}}
      responses:
        "200": {description: Updated checkpoint, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "410": {$ref: "#/components/responses/Gone"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /checkpoints/{checkpointId}/cancel:
    parameters:
      - {name: checkpointId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: cancelWorkflowCheckpoint
      x-agent-brain-scope: ab:workflow:approve
      summary: Cancel a checkpoint with Workflow write permission
      security: [{apiKey: []}]
      requestBody:
        content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointCancel"}}}
      responses:
        "200": {description: Cancelled checkpoint, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "410": {$ref: "#/components/responses/Gone"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflows/{workflowId}/runs/{runId}/checkpoints:
    parameters:
      - {name: workflowId, in: path, required: true, schema: {type: string, format: uuid}}
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: listWorkflowRunCheckpoints
      x-agent-brain-scope: ab:workflow:read
      summary: List checkpoints for one bound Workflow run
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow run checkpoints, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowCheckpointRunListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /workflow-node-types:
    get:
      operationId: listWorkflowNodeTypes
      x-agent-brain-scope: ab:workflow:read
      summary: List the Workflow authoring node catalog
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow node catalog, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowNodeCatalogEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflow-node-types/{type}:
    parameters:
      - {name: type, in: path, required: true, schema: {type: string}}
    get:
      operationId: getWorkflowNodeType
      x-agent-brain-scope: ab:workflow:read
      summary: Get one Workflow authoring node definition
      security: [{apiKey: []}]
      responses:
        "200": {description: Workflow node definition, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowNodeEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflow-reference-options:
    get:
      operationId: listWorkflowReferenceOptions
      x-agent-brain-scope: ab:workflow:read
      summary: List accessible references for Workflow authoring
      security: [{apiKey: []}]
      parameters:
        - {name: kind, in: query, required: true, schema: {type: string}}
        - {name: action, in: query, schema: {type: string, default: read}}
        - {name: nodeType, in: query, schema: {type: string}}
        - {name: fieldKey, in: query, schema: {type: string}}
        - {name: q, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100}}
      responses:
        "200": {description: Accessible reference options, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowReferenceOptionsEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /workflow-reference-resolve:
    post:
      operationId: resolveWorkflowReferences
      x-agent-brain-scope: ab:workflow:read
      summary: Resolve Workflow authoring references without exposing internal metadata
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/WorkflowReferenceResolveRequest"}
      responses:
        "200": {description: Resolved and missing references, content: {application/json: {schema: {$ref: "#/components/schemas/WorkflowReferenceResolveEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /folders:
    get:
      operationId: listFolders
      x-agent-brain-scope: ab:knowledge:read
      summary: List accessible child Folders in the bound organization
      security: [{apiKey: []}]
      parameters:
        - name: parentId
          in: query
          schema: {type: string, format: uuid}
        - name: cursor
          in: query
          schema: {type: string}
        - name: limit
          in: query
          schema: {type: integer, minimum: 1, maximum: 100, default: 20}
      responses:
        "200":
          description: Cursor-paginated accessible Folders
          content:
            application/json:
              schema: {$ref: "#/components/schemas/FolderListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    post:
      operationId: createFolder
      x-agent-brain-scope: ab:knowledge:write
      summary: Create a Folder in the bound organization
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/FolderCreate"}
      responses:
        "201":
          description: Folder created
          content:
            application/json:
              schema: {$ref: "#/components/schemas/FolderEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /folders/{folderId}:
    parameters:
      - name: folderId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: getFolder
      x-agent-brain-scope: ab:knowledge:read
      summary: Get accessible Folder settings and hierarchy detail
      security: [{apiKey: []}]
      responses:
        "200":
          description: Folder detail
          content:
            application/json:
              schema: {$ref: "#/components/schemas/FolderEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    patch:
      operationId: updateFolder
      x-agent-brain-scope: ab:knowledge:write
      summary: Update Folder name or AI pipeline defaults
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/FolderUpdate"}
      responses:
        "200":
          description: Folder updated
          content:
            application/json:
              schema: {$ref: "#/components/schemas/FolderEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteFolder
      x-agent-brain-scope: ab:knowledge:write
      summary: Soft-delete a Folder subtree
      security: [{apiKey: []}]
      responses:
        "200":
          description: Folder subtree soft-deleted
          content:
            application/json:
              schema: {$ref: "#/components/schemas/FolderDeleteEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /folders/{folderId}/move:
    post:
      operationId: moveFolder
      x-agent-brain-scope: ab:knowledge:write
      summary: Move a Folder subtree
      security: [{apiKey: []}]
      parameters:
        - name: folderId
          in: path
          required: true
          schema: {type: string, format: uuid}
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/FolderMove"}
      responses:
        "200":
          description: Folder moved
          content:
            application/json:
              schema: {$ref: "#/components/schemas/FolderEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /trash:
    get:
      operationId: listTrash
      x-agent-brain-scope: ab:knowledge:read
      x-agent-brain-media-scope: ab:media:read
      summary: List globally ordered Trash; media_asset rows additionally require Media read scope
      security: [{apiKey: []}]
      parameters:
        - name: resourceTypes
          in: query
          schema: {type: string, default: folder}
        - name: cursor
          in: query
          schema: {type: string}
        - name: limit
          in: query
          schema: {type: integer, minimum: 1, maximum: 100, default: 20}
      responses:
        "200":
          description: One global keyset page across requested resource types
          content:
            application/json:
              schema: {$ref: "#/components/schemas/TrashListEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /trash/bulk-purge:
    post:
      operationId: bulkPurgeTrash
      x-agent-brain-scope: ab:knowledge:write
      x-agent-brain-media-scope: ab:media:write
      summary: Permanently purge up to 100 Trash resources; media additionally requires Media write scope
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/TrashBulkPurgeRequest"}
      responses:
        "200":
          description: Per-resource purge outcomes with sanitized error IDs
          content:
            application/json:
              schema: {$ref: "#/components/schemas/TrashBulkPurgeEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /trash/{resourceType}/{resourceId}/preview:
    get:
      operationId: previewTrashPurge
      x-agent-brain-scope: ab:knowledge:write
      x-agent-brain-media-scope: ab:media:write
      summary: Preview permanent purge and issue a single-use confirmation token
      security: [{apiKey: []}]
      parameters:
        - $ref: "#/components/parameters/TrashResourceType"
        - $ref: "#/components/parameters/TrashResourceID"
      responses:
        "200":
          description: Purge preview
          content:
            application/json:
              schema: {$ref: "#/components/schemas/TrashPreviewEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /trash/{resourceType}/{resourceId}/restore:
    post:
      operationId: restoreTrashResource
      x-agent-brain-scope: ab:knowledge:write
      x-agent-brain-media-scope: ab:media:write
      summary: Restore a soft-deleted resource
      security: [{apiKey: []}]
      parameters:
        - $ref: "#/components/parameters/TrashResourceType"
        - $ref: "#/components/parameters/TrashResourceID"
      responses:
        "200":
          description: Restore result
          content:
            application/json:
              schema: {$ref: "#/components/schemas/TrashRestoreEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /trash/{resourceType}/{resourceId}:
    delete:
      operationId: purgeTrashResource
      x-agent-brain-scope: ab:knowledge:write
      x-agent-brain-media-scope: ab:media:write
      summary: Permanently purge using a single-use confirmation token
      security: [{apiKey: []}]
      parameters:
        - $ref: "#/components/parameters/TrashResourceType"
        - $ref: "#/components/parameters/TrashResourceID"
        - name: confirmToken
          in: query
          required: true
          schema: {type: string}
      responses:
        "200":
          description: Purge result
          content:
            application/json:
              schema: {$ref: "#/components/schemas/TrashPurgeEnvelope"}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/uploads/presign:
    post:
      operationId: presignMediaUpload
      x-agent-brain-scope: ab:media:write
      summary: Create a direct-to-storage Media upload URL
      description: The client uploads the exact bytes directly to `putUrl`; Agent Brain never proxies the binary body.
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MediaUploadPresignRequest"
      responses:
        "200":
          description: Direct upload URL issued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MediaUploadPresignEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /media/uploads/commit:
    post:
      operationId: commitMediaUpload
      x-agent-brain-scope: ab:media:write
      summary: Verify and commit a directly uploaded Media object
      description: Commit validates storage bytes and creates no processing run. Call a processing endpoint explicitly after success.
      security:
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MediaUploadCommitRequest"
      responses:
        "200":
          description: Media upload committed and ready for explicit processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MediaUploadCommitEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: The upload was already committed; `data` identifies the ready asset.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MediaUploadCommitConflictEnvelope"
        "410":
          $ref: "#/components/responses/Gone"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /media:
    get:
      operationId: listMedia
      x-agent-brain-scope: ab:media:read
      summary: List ACL-visible Media assets in the bound organization
      security:
        - apiKey: []
      parameters:
        - {name: folderId, in: query, schema: {type: string, format: uuid}}
        - {name: unfiled, in: query, schema: {type: boolean, default: false}}
        - {name: kind, in: query, schema: {type: array, items: {type: string, enum: [audio, video, image, raw_doc]}}}
        - {name: status, in: query, schema: {type: array, items: {type: string, enum: [uploaded, processing, ready, failed]}}}
        - {name: q, in: query, schema: {type: string, maxLength: 200}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
        - {name: cursor, in: query, schema: {type: string}}
      responses:
        "200":
          description: Stable keyset page of Media assets
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MediaAssetListEnvelope"
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/{mediaId}:
    parameters:
      - name: mediaId
        in: path
        required: true
        schema: {type: string, format: uuid}
    get:
      operationId: getMedia
      x-agent-brain-scope: ab:media:read
      summary: Get one ACL-visible Media asset
      security: [{apiKey: []}]
      responses:
        "200": {description: Media asset, content: {application/json: {schema: {$ref: "#/components/schemas/MediaAssetEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    patch:
      operationId: updateMedia
      x-agent-brain-scope: ab:media:write
      summary: Update Media filename or folder assignment
      security: [{apiKey: []}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: "#/components/schemas/MediaAssetUpdateRequest"}
      responses:
        "200": {description: Updated Media asset, content: {application/json: {schema: {$ref: "#/components/schemas/MediaAssetEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    delete:
      operationId: deleteMedia
      x-agent-brain-scope: ab:media:write
      summary: Soft-delete a Media asset
      security: [{apiKey: []}]
      responses:
        "200": {description: Media asset deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/{mediaId}/download:
    get:
      operationId: downloadMedia
      x-agent-brain-scope: ab:media:read
      summary: Create a short-lived Media download URL
      security: [{apiKey: []}]
      parameters:
        - {name: mediaId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        "200": {description: Presigned download URL, content: {application/json: {schema: {$ref: "#/components/schemas/MediaDownloadEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/{mediaId}/transcribe:
    post:
      operationId: transcribeMedia
      x-agent-brain-scope: ab:media:process
      summary: Start explicit processing for a committed Media asset
      description: Returns a polling identifier. Upload commit never calls this operation automatically.
      security:
        - apiKey: []
      parameters:
        - name: mediaId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MediaTranscriptionRequest"
      responses:
        "202":
          description: Media processing queued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MediaProcessingRunEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          description: Organization transcription budget is exhausted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /media/jobs/{runId}:
    get:
      operationId: getMediaProcessingRun
      x-agent-brain-scope: ab:media:read
      summary: Poll one Media processing run
      description: Poll until status is `done` or `failed`; Partner V1 does not require CMS SSE.
      security:
        - apiKey: []
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Current Media processing state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MediaProcessingRunEnvelope"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
  /media/jobs/{runId}/refine:
    post:
      operationId: refineMediaProcessingRun
      x-agent-brain-scope: ab:media:process
      summary: Start Pass-2 refinement from a completed Media run
      description: Creates a child processing run and returns its polling identifier.
      security: [{apiKey: []}]
      parameters:
        - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
      requestBody:
        required: false
        content:
          application/json:
            schema: {$ref: "#/components/schemas/MediaRefineRequest"}
      responses:
        "202": {description: Refinement queued, content: {application/json: {schema: {$ref: "#/components/schemas/MediaProcessingRunEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/jobs/{runId}/ingest-kb:
    post:
      operationId: ingestMediaRunIntoKnowledgeBase
      x-agent-brain-scope: ab:media:process
      x-agent-brain-required-scopes: [ab:media:process, ab:knowledge:write]
      summary: Ingest a completed Media transcript into the Knowledge Base
      description: Requires both `ab:media:process` and `ab:knowledge:write`. Duplicate calls are idempotent.
      security: [{apiKey: []}]
      parameters:
        - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
      requestBody:
        required: false
        content:
          application/json:
            schema: {$ref: "#/components/schemas/MediaKnowledgeIngestRequest"}
      responses:
        "200": {description: Transcript ingested or already present, content: {application/json: {schema: {$ref: "#/components/schemas/MediaKnowledgeIngestEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/bulk/digest:
    post:
      operationId: enqueueMediaBulkDigest
      x-agent-brain-scope: ab:media:process
      summary: Queue processing for multiple Media assets
      description: Commit uploads first, then call this endpoint explicitly. `Idempotency-Key` header overrides the optional body key.
      security: [{apiKey: []}]
      parameters:
        - {name: Idempotency-Key, in: header, required: false, schema: {type: string}}
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkDigestRequest"}}}
      responses:
        "202": {description: Bulk job queued or deduplicated, content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkDigestEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/bulk/jobs/active:
    get:
      operationId: listActiveMediaBulkJobs
      x-agent-brain-scope: ab:media:read
      summary: List active bulk Media jobs for the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: Active jobs, content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkJobListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/bulk/jobs/{jobId}:
    get:
      operationId: getMediaBulkJob
      x-agent-brain-scope: ab:media:read
      summary: Poll a bulk Media job
      description: Poll until status is terminal; Partner V1 does not require CMS SSE.
      security: [{apiKey: []}]
      parameters:
        - {name: jobId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        "200": {description: Bulk job state, content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkJobEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/bulk/jobs/{jobId}/items/{itemId}/retry:
    post:
      operationId: retryMediaBulkJobItem
      x-agent-brain-scope: ab:media:process
      summary: Retry one failed bulk Media item
      security: [{apiKey: []}]
      parameters:
        - {name: jobId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: itemId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        "200": {description: Item requeued, content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkJobItemEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/bulk/jobs/{jobId}/items/{itemId}/cancel:
    post:
      operationId: cancelMediaBulkJobItem
      x-agent-brain-scope: ab:media:process
      summary: Cancel one queued bulk Media item
      security: [{apiKey: []}]
      parameters:
        - {name: jobId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: itemId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        "200": {description: Item cancelled, content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkJobItemEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/bulk/jobs/{jobId}/items/{itemId}/priority:
    patch:
      operationId: updateMediaBulkJobItemPriority
      x-agent-brain-scope: ab:media:process
      summary: Change queue priority for one queued bulk Media item
      security: [{apiKey: []}]
      parameters:
        - {name: jobId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: itemId, in: path, required: true, schema: {type: string, format: uuid}}
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkItemPriorityRequest"}}}
      responses:
        "200": {description: Item priority updated, content: {application/json: {schema: {$ref: "#/components/schemas/MediaBulkJobItemEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/settings:
    get:
      operationId: getMediaSettings
      x-agent-brain-scope: ab:media:admin
      summary: Get operational Media settings for the bound organization
      description: The parent `enabled` state is read-only and controlled by the trusted Agent Brain control plane.
      security: [{apiKey: []}]
      responses:
        "200": {description: Secret-free Media settings, content: {application/json: {schema: {$ref: "#/components/schemas/MediaSettingsEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    patch:
      operationId: updateMediaSettings
      x-agent-brain-scope: ab:media:admin
      summary: Update operational Media settings
      description: Cannot change the parent Media `enabled` gate.
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/MediaSettingsPatch"}}}
      responses:
        "200": {description: Updated Media settings, content: {application/json: {schema: {$ref: "#/components/schemas/MediaSettingsEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/storage/readiness:
    get:
      operationId: getMediaStorageReadiness
      x-agent-brain-scope: ab:media:read
      summary: Get secret-free BYO storage readiness
      security: [{apiKey: []}]
      responses:
        "200": {description: Storage readiness, content: {application/json: {schema: {$ref: "#/components/schemas/MediaStorageReadinessEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/storage/probe:
    post:
      operationId: probeMediaStorage
      x-agent-brain-scope: ab:media:admin
      summary: Probe BYO storage credentials without saving them
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/MediaStorageCredentialsRequest"}}}}
      responses:
        "200": {description: Probe result, content: {application/json: {schema: {$ref: "#/components/schemas/MediaStorageResultEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/storage/verify:
    post:
      operationId: verifyMediaStorage
      x-agent-brain-scope: ab:media:admin
      summary: Verify and save encrypted BYO storage credentials
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/MediaStorageCredentialsRequest"}}}}
      responses:
        "200": {description: Verify result, content: {application/json: {schema: {$ref: "#/components/schemas/MediaStorageResultEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/storage/reverify:
    post:
      operationId: reverifyMediaStorage
      x-agent-brain-scope: ab:media:admin
      summary: Reverify saved BYO storage credentials
      security: [{apiKey: []}]
      responses:
        "200": {description: Reverify result, content: {application/json: {schema: {$ref: "#/components/schemas/MediaStorageResultEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /media/storage:
    delete:
      operationId: clearMediaStorage
      x-agent-brain-scope: ab:media:admin
      summary: Clear BYO storage and revert to the platform storage policy
      security: [{apiKey: []}]
      responses:
        "200": {description: Storage cleared, content: {application/json: {schema: {$ref: "#/components/schemas/MediaStorageClearedEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-config/readiness:
    get:
      operationId: getPartnerAIReadiness
      x-agent-brain-scope: ab:ai-config:read
      summary: Get AI readiness for the bound organization
      security: [{apiKey: []}]
      responses:
        "200": {description: AI readiness, content: {application/json: {schema: {$ref: "#/components/schemas/AIReadinessEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-config/providers:
    get:
      operationId: listPartnerAIProviders
      x-agent-brain-scope: ab:ai-config:read
      summary: List AI providers without credentials or private configuration
      security: [{apiKey: []}]
      responses:
        "200": {description: AI providers, content: {application/json: {schema: {$ref: "#/components/schemas/AIProviderListEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    post:
      operationId: createPartnerAIProvider
      x-agent-brain-scope: ab:ai-config:write
      summary: Create an AI provider; credentials and extension configuration are write-only
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/CreateAIProviderRequest"}}}}
      responses:
        "201": {description: Created AI provider, content: {application/json: {schema: {$ref: "#/components/schemas/AIProviderEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-config/providers/{providerId}:
    parameters:
      - {$ref: "#/components/parameters/AIProviderID"}
    patch:
      operationId: updatePartnerAIProvider
      x-agent-brain-scope: ab:ai-config:write
      summary: Update an AI provider; credentials and extension configuration remain write-only
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/UpdateAIProviderRequest"}}}}
      responses:
        "200": {description: Updated AI provider, content: {application/json: {schema: {$ref: "#/components/schemas/AIProviderEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    delete:
      operationId: deletePartnerAIProvider
      x-agent-brain-scope: ab:ai-config:write
      summary: Delete an unused AI provider
      security: [{apiKey: []}]
      responses:
        "204": {description: AI provider deleted}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-config/providers/{providerId}/verify:
    parameters:
      - {$ref: "#/components/parameters/AIProviderID"}
    post:
      operationId: verifyPartnerAIProvider
      x-agent-brain-scope: ab:ai-config:write
      summary: Verify a stored provider credential without returning upstream diagnostics
      security: [{apiKey: []}]
      responses:
        "200": {description: Sanitized verification outcome, content: {application/json: {schema: {$ref: "#/components/schemas/AIVerifyEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-config/providers/{providerId}/models:
    parameters:
      - {$ref: "#/components/parameters/AIProviderID"}
    get:
      operationId: listPartnerAIModels
      x-agent-brain-scope: ab:ai-config:read
      summary: List models for an AI provider
      security: [{apiKey: []}]
      responses:
        "200": {description: AI models, content: {application/json: {schema: {$ref: "#/components/schemas/AIModelListEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    post:
      operationId: createPartnerAIModel
      x-agent-brain-scope: ab:ai-config:write
      summary: Create an organization AI model
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/CreateAIModelRequest"}}}}
      responses:
        "201": {description: Created AI model, content: {application/json: {schema: {$ref: "#/components/schemas/AIModelEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-config/providers/{providerId}/sync-models:
    parameters:
      - {$ref: "#/components/parameters/AIProviderID"}
    post:
      operationId: syncPartnerAIModels
      x-agent-brain-scope: ab:ai-config:write
      summary: Synchronize models from an AI provider
      security: [{apiKey: []}]
      responses:
        "200": {description: Sanitized sync outcome, content: {application/json: {schema: {$ref: "#/components/schemas/AISyncEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-config/providers/{providerId}/probe-model:
    parameters:
      - {$ref: "#/components/parameters/AIProviderID"}
    post:
      operationId: probePartnerAIModel
      x-agent-brain-scope: ab:ai-config:write
      summary: Probe chat or embedding support without returning raw provider errors
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/ProbeAIModelRequest"}}}}
      responses:
        "200": {description: Sanitized probe outcome, content: {application/json: {schema: {$ref: "#/components/schemas/AIProbeEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-config/models/system:
    get:
      operationId: listPartnerSystemAIModels
      x-agent-brain-scope: ab:ai-config:read
      summary: List the public system AI model catalog
      security: [{apiKey: []}]
      responses:
        "200": {description: System AI models, content: {application/json: {schema: {$ref: "#/components/schemas/AIModelListEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-config/models/{modelId}:
    parameters:
      - {$ref: "#/components/parameters/AIModelID"}
    patch:
      operationId: updatePartnerAIModel
      x-agent-brain-scope: ab:ai-config:write
      summary: Update an organization AI model
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/UpdateAIModelRequest"}}}}
      responses:
        "200": {description: Updated AI model, content: {application/json: {schema: {$ref: "#/components/schemas/AIModelEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    delete:
      operationId: deletePartnerAIModel
      x-agent-brain-scope: ab:ai-config:write
      summary: Delete an organization AI model
      security: [{apiKey: []}]
      responses:
        "204": {description: AI model deleted}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-config/defaults/{useCase}:
    parameters:
      - name: useCase
        in: path
        required: true
        schema: {type: string, minLength: 1}
    get:
      operationId: getPartnerAIDefault
      x-agent-brain-scope: ab:ai-config:read
      summary: Get the bound organization's default AI model for a use case
      security: [{apiKey: []}]
      responses:
        "200": {description: AI default, content: {application/json: {schema: {$ref: "#/components/schemas/AIDefaultEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
    put:
      operationId: setPartnerAIDefault
      x-agent-brain-scope: ab:ai-config:write
      summary: Set the bound organization's default AI model for a use case
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/SetAIDefaultRequest"}}}}
      responses:
        "204": {description: AI default updated}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-config/catalog/models:
    get:
      operationId: listPartnerAICatalogModels
      x-agent-brain-scope: ab:ai-config:read
      summary: List public model presets for a provider type
      security: [{apiKey: []}]
      parameters:
        - name: providerType
          in: query
          required: true
          schema: {type: string, minLength: 1}
      responses:
        "200": {description: Public model presets, content: {application/json: {schema: {$ref: "#/components/schemas/AICatalogListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-governance/overview:
    get:
      operationId: getPartnerAIGovernanceOverview
      x-agent-brain-scope: ab:ai-governance:read
      summary: Get the bound organization's AI governance policy, assignments, privacy profiles, and summary
      security: [{apiKey: []}]
      responses:
        "200": {description: AI governance overview, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernanceOverviewEnvelope"}}}}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-governance/events:
    get:
      operationId: listPartnerAIGovernanceEvents
      x-agent-brain-scope: ab:ai-governance:read
      summary: List sanitized AI governance guard events
      security: [{apiKey: []}]
      parameters:
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 200}}
        - {name: decision, in: query, schema: {type: string}}
        - {name: surface, in: query, schema: {type: string}}
        - {name: useCase, in: query, schema: {type: string}}
        - {name: sourceEntityType, in: query, schema: {type: string}}
        - {name: sourceEntityId, in: query, schema: {type: string, format: uuid}}
        - {name: providerType, in: query, schema: {type: string}}
        - {name: folderId, in: query, schema: {type: string, format: uuid}}
        - {name: since, in: query, schema: {type: string, format: date-time}}
        - {name: before, in: query, schema: {type: string, format: date-time}}
      responses:
        "200": {description: Sanitized guard events, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernanceEventListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-governance/effective-preview:
    post:
      operationId: previewPartnerAIGovernancePolicy
      x-agent-brain-scope: ab:ai-governance:read
      summary: Preview the effective AI governance decision without mutating state
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernanceEffectivePreviewRequest"}}}}
      responses:
        "200": {description: Effective policy preview, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernanceEffectivePreviewEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
  /ai-governance/policy:
    patch:
      operationId: updatePartnerAIGovernancePolicy
      x-agent-brain-scope: ab:ai-governance:write
      summary: Update the bound organization's AI governance policy
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernancePolicyUpdate"}}}}
      responses:
        "200": {description: Updated policy, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernancePolicyEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-governance/assignments:
    post:
      operationId: createPartnerAIGovernanceAssignment
      x-agent-brain-scope: ab:ai-governance:write
      summary: Create a tenant-bound AI governance assignment
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernanceAssignmentCreate"}}}}
      responses:
        "201": {description: Created assignment, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernanceAssignmentEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-governance/assignments/{assignmentId}:
    parameters: [{name: assignmentId, in: path, required: true, schema: {type: string, format: uuid}}]
    delete:
      operationId: deletePartnerAIGovernanceAssignment
      x-agent-brain-scope: ab:ai-governance:write
      summary: Disable an AI governance assignment
      security: [{apiKey: []}]
      responses:
        "204": {description: Assignment disabled}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-governance/provider-privacy:
    post:
      operationId: upsertPartnerAIProviderPrivacy
      x-agent-brain-scope: ab:ai-governance:write
      summary: Create or update a tenant-bound provider privacy profile
      security: [{apiKey: []}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernancePrivacyUpsert"}}}}
      responses:
        "200": {description: Provider privacy profile, content: {application/json: {schema: {$ref: "#/components/schemas/AIGovernancePrivacyEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /ai-governance/provider-privacy/{profileId}:
    parameters: [{name: profileId, in: path, required: true, schema: {type: string, format: uuid}}]
    delete:
      operationId: deletePartnerAIProviderPrivacy
      x-agent-brain-scope: ab:ai-governance:write
      summary: Delete a provider privacy profile
      security: [{apiKey: []}]
      responses:
        "204": {description: Provider privacy profile deleted}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /cost/budget:
    get:
      operationId: getCostBudget
      x-agent-brain-scope: ab:cost:read
      summary: Get the bound organization's LLM cost budget
      security: [{apiKey: []}]
      responses:
        "200": {description: Cost budget or editable zero-state, content: {application/json: {schema: {$ref: "#/components/schemas/BudgetEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
    put:
      operationId: replaceCostBudget
      x-agent-brain-scope: ab:cost:write
      summary: Replace the bound organization's LLM cost budget
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/BudgetReplace"}}}
      responses:
        "200": {description: Replaced cost budget, content: {application/json: {schema: {$ref: "#/components/schemas/BudgetEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /cost/budget/spend:
    get:
      operationId: getCostBudgetSpend
      x-agent-brain-scope: ab:cost:read
      summary: Get current-month LLM spend for a cost scope
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/CostScope"}]
      responses:
        "200": {description: Current-month spend, content: {application/json: {schema: {$ref: "#/components/schemas/SpendEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /usage-metrics/summary:
    get:
      operationId: getUsageSummary
      x-agent-brain-scope: ab:usage:read
      summary: Get aggregate LLM usage and prior-period deltas
      security: [{apiKey: []}]
      parameters: &usageMetricParameters [{$ref: "#/components/parameters/UsageFrom"}, {$ref: "#/components/parameters/UsageTo"}, {$ref: "#/components/parameters/UsageProvider"}, {$ref: "#/components/parameters/UsageModel"}, {$ref: "#/components/parameters/UsageUseCase"}]
      responses:
        "200": {description: Usage summary, content: {application/json: {schema: {$ref: "#/components/schemas/UsageSummaryEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /usage-metrics/trend:
    get:
      operationId: getUsageTrend
      x-agent-brain-scope: ab:usage:read
      summary: Get daily LLM cost trend
      security: [{apiKey: []}]
      parameters: *usageMetricParameters
      responses:
        "200": {description: Usage trend, content: {application/json: {schema: {$ref: "#/components/schemas/UsageTrendEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /usage-metrics/by-use-case:
    get:
      operationId: getUsageByUseCase
      x-agent-brain-scope: ab:usage:read
      summary: Get LLM cost grouped by use case
      security: [{apiKey: []}]
      parameters: *usageMetricParameters
      responses:
        "200": {description: Usage by use case, content: {application/json: {schema: {$ref: "#/components/schemas/UsageByUseCaseEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /usage-metrics/top-models:
    get:
      operationId: getUsageTopModels
      x-agent-brain-scope: ab:usage:read
      summary: Get top LLM models ranked by cost
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/UsageFrom"}, {$ref: "#/components/parameters/UsageTo"}, {$ref: "#/components/parameters/UsageProvider"}, {$ref: "#/components/parameters/UsageModel"}, {$ref: "#/components/parameters/UsageUseCase"}, {$ref: "#/components/parameters/UsageLimit"}]
      responses:
        "200": {description: Top models, content: {application/json: {schema: {$ref: "#/components/schemas/UsageTopModelsEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /usage-logs:
    get:
      operationId: listUsageLogs
      x-agent-brain-scope: ab:usage:read
      summary: List bounded LLM usage-log metadata
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/UsageFrom"}, {$ref: "#/components/parameters/UsageTo"}, {$ref: "#/components/parameters/UsageProvider"}, {$ref: "#/components/parameters/UsageModel"}, {$ref: "#/components/parameters/UsageUseCase"}, {$ref: "#/components/parameters/UsageStatus"}, {$ref: "#/components/parameters/UsageSourceEntityType"}, {$ref: "#/components/parameters/UsageSourceEntityID"}, {$ref: "#/components/parameters/UsageCursor"}, {$ref: "#/components/parameters/UsageLimit"}]
      responses:
        "200": {description: Usage-log page, content: {application/json: {schema: {$ref: "#/components/schemas/UsageLogListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /usage-logs/{usageLogId}:
    parameters: [{name: usageLogId, in: path, required: true, schema: {type: string, format: uuid}}]
    get:
      operationId: getUsageLog
      x-agent-brain-scope: ab:usage:read
      summary: Get bounded LLM usage-log metadata
      security: [{apiKey: []}]
      responses:
        "200": {description: Usage log, content: {application/json: {schema: {$ref: "#/components/schemas/UsageLogEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /resource-activity/usage-logs:
    get:
      operationId: listResourceUsageLogs
      x-agent-brain-scope: ab:usage:read
      summary: List usage metadata for an accessible source resource
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/UsageFrom"}, {$ref: "#/components/parameters/UsageTo"}, {$ref: "#/components/parameters/UsageProvider"}, {$ref: "#/components/parameters/UsageModel"}, {$ref: "#/components/parameters/UsageUseCase"}, {$ref: "#/components/parameters/UsageStatus"}, {$ref: "#/components/parameters/RequiredUsageSourceEntityType"}, {$ref: "#/components/parameters/RequiredUsageSourceEntityID"}, {$ref: "#/components/parameters/UsageCursor"}, {$ref: "#/components/parameters/UsageLimit"}]
      responses:
        "200": {description: Resource usage-log page, content: {application/json: {schema: {$ref: "#/components/schemas/ResourceUsageLogListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /audit-log:
    get:
      operationId: listAuditLog
      x-agent-brain-scope: ab:audit:read
      summary: List the bound organization's audit log
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/AuditActorUserID"}, {$ref: "#/components/parameters/AuditResourceType"}, {$ref: "#/components/parameters/AuditResourceID"}, {$ref: "#/components/parameters/AuditActionKey"}, {$ref: "#/components/parameters/AuditSource"}, {$ref: "#/components/parameters/AuditFrom"}, {$ref: "#/components/parameters/AuditTo"}, {$ref: "#/components/parameters/AuditCursor"}, {$ref: "#/components/parameters/AuditLimit"}]
      responses:
        "200": {description: Audit log page, content: {application/json: {schema: {$ref: "#/components/schemas/AuditLogListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /audit-log/summary:
    get:
      operationId: getAuditLogSummary
      x-agent-brain-scope: ab:audit:read
      summary: Summarize audit activity over a trailing window
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/AuditSummaryRange"}]
      responses:
        "200": {description: Audit summary, content: {application/json: {schema: {$ref: "#/components/schemas/AuditSummaryEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /resource-activity/audit:
    get:
      operationId: listResourceAuditLog
      x-agent-brain-scope: ab:audit:read
      summary: List audit activity for one resource
      description: Unknown, deleted, foreign-tenant and inaccessible resource ids all answer 404 so the response cannot be used to probe which ids exist.
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/ResourceType"}, {$ref: "#/components/parameters/ResourceID"}, {$ref: "#/components/parameters/AuditActionKey"}, {$ref: "#/components/parameters/AuditSource"}, {$ref: "#/components/parameters/AuditFrom"}, {$ref: "#/components/parameters/AuditTo"}, {$ref: "#/components/parameters/AuditCursor"}, {$ref: "#/components/parameters/AuditLimit"}]
      responses:
        "200": {description: Resource audit page, content: {application/json: {schema: {$ref: "#/components/schemas/AuditLogListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /dashboard/summary:
    get:
      operationId: getDashboardSummary
      x-agent-brain-scope: ab:dashboard:read
      summary: Read the bound organization's aggregate dashboard summary
      description: Counts are served from nightly snapshot tables, not a live scan. Each entity block carries snapshotDate so a caller can tell how old the figure is.
      security: [{apiKey: []}]
      responses:
        "200": {description: Dashboard summary, content: {application/json: {schema: {$ref: "#/components/schemas/DashboardSummaryEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /dashboard/trend:
    get:
      operationId: getDashboardTrend
      x-agent-brain-scope: ab:dashboard:read
      summary: Read one dashboard trend series over one window
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/DashboardTrendRange"}, {$ref: "#/components/parameters/DashboardTrendMetric"}]
      responses:
        "200": {description: Dashboard trend series, content: {application/json: {schema: {$ref: "#/components/schemas/DashboardTrendEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /dashboard/media-kg-stats:
    get:
      operationId: getDashboardMediaKGStats
      x-agent-brain-scope: ab:dashboard:read
      summary: Read the media and Knowledge Graph aggregate widgets
      description: When the Knowledge Graph feature is disabled for the organization the kgEntities and dlqFailures blocks answer with zero-filled daily series of the same shape; the response is never omitted and never replaced with a disabled marker.
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/DashboardStatsDays"}]
      responses:
        "200": {description: Media and Knowledge Graph stats, content: {application/json: {schema: {$ref: "#/components/schemas/DashboardMediaKGStatsEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/graph/summary:
    get:
      operationId: getKGGraphSummary
      x-agent-brain-scope: ab:kg:read
      summary: Read the community meta-graph of the bound organization
      description: Nodes are communities and edges are inter-community relation counts. Visibility is resolved from the membership the API key is bound to; the request cannot name a tier or scope.
      security: [{apiKey: []}]
      responses:
        "200": {description: KG graph payload, content: {application/json: {schema: {$ref: "#/components/schemas/KGGraphEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/graph/top:
    get:
      operationId: getKGGraphTop
      x-agent-brain-scope: ab:kg:read
      summary: Read the top entities by centrality
      description: A limit outside the documented bounds is rejected rather than clamped. The server may still return fewer nodes than requested, in which case meta.truncated is set.
      security: [{apiKey: []}]
      parameters:
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 50000, default: 10000}}
        - {name: knowledgeId, in: query, description: Scope the slice to one knowledge source instead of the whole organization, schema: {type: string, format: uuid}}
        - {name: includeOrphan, in: query, schema: {type: boolean, default: false}}
      responses:
        "200": {description: KG graph payload, content: {application/json: {schema: {$ref: "#/components/schemas/KGGraphEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/graph/community/{communityId}:
    get:
      operationId: getKGGraphCommunity
      x-agent-brain-scope: ab:kg:read
      summary: Read one community's subgraph
      description: A community with no member visible to the bound organization answers with an empty graph, which is exactly the response an unknown id produces, so the read cannot be used to probe which community ids exist.
      security: [{apiKey: []}]
      parameters:
        - {name: communityId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: includeOrphan, in: query, schema: {type: boolean, default: false}}
      responses:
        "200": {description: KG graph payload, content: {application/json: {schema: {$ref: "#/components/schemas/KGGraphEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/graph/neighbors/{entityId}:
    get:
      operationId: getKGGraphNeighbors
      x-agent-brain-scope: ab:kg:read
      summary: Read the neighborhood around one entity
      description: Unknown, deleted, foreign-tenant and inaccessible entity ids all answer 404 so the response cannot be used to probe which ids exist.
      security: [{apiKey: []}]
      parameters:
        - {name: entityId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: depth, in: query, schema: {type: integer, minimum: 1, maximum: 3, default: 1}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 500, default: 100}}
        - {name: includeOrphan, in: query, schema: {type: boolean, default: false}}
      responses:
        "200": {description: KG graph payload, content: {application/json: {schema: {$ref: "#/components/schemas/KGGraphEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entities/by-knowledge:
    get:
      operationId: listKGEntitiesByKnowledge
      x-agent-brain-scope: ab:kg:read
      summary: List the entities extracted from one knowledge source
      description: A knowledge source the bound organization cannot read answers with an empty page rather than a denial, so the response never distinguishes an empty source from an inaccessible one.
      security: [{apiKey: []}]
      parameters:
        - {name: knowledgeId, in: query, required: true, schema: {type: string, format: uuid}}
        - {name: q, in: query, description: Case-insensitive name search after trimming, schema: {type: string, maxLength: 200}}
        - {name: kind, in: query, schema: {type: string, maxLength: 50}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 50}}
      responses:
        "200": {description: Source entity page, content: {application/json: {schema: {$ref: "#/components/schemas/KGSourceEntityListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entities/batch-read:
    post:
      operationId: batchReadKGEntities
      x-agent-brain-scope: ab:kg:read
      summary: Resolve several KG entities in one call
      description: Only entities the bound organization can read are returned. There is no per-id status, so an id from another tenant, a deleted id and an id that never existed are all simply absent and the call cannot be used to discover which ids exist.
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityBatchReadRequest"}}}
      responses:
        "200": {description: Resolved entities, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityBatchReadEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entities/{entityId}/detail:
    get:
      operationId: getKGEntityDetail
      x-agent-brain-scope: ab:kg:read
      summary: Read one entity with its accessible sources
      description: Sources the bound organization cannot reach are stripped rather than listed with a denied marker. Unknown, deleted, foreign-tenant and inaccessible entity ids all answer 404.
      security: [{apiKey: []}]
      parameters:
        - {name: entityId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        "200": {description: KG entity detail, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityDetailEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entities/{entityId}/provenance:
    get:
      operationId: getKGEntityProvenance
      x-agent-brain-scope: ab:kg:read
      summary: Read the extraction lineage of one entity
      description: Reading the lineage of a sensitive entity is recorded as an auditable access. Unknown, deleted, foreign-tenant and inaccessible entity ids all answer 404.
      security: [{apiKey: []}]
      parameters:
        - {name: entityId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        "200": {description: KG entity provenance, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityProvenanceEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/entities/{entityId}/audit-history:
    get:
      operationId: listKGEntityAuditHistory
      x-agent-brain-scope: ab:kg:read
      summary: Read the per-field change timeline of one entity
      description: limit caps the number of underlying change records read, not the number of expanded events returned, so a page can carry more items than the limit when one change touched several fields. Drive paging from nextCursor, never from item count.
      security: [{apiKey: []}]
      parameters:
        - {name: entityId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: field, in: query, description: Restrict the timeline to one entity field, schema: {type: string, maxLength: 64}}
        - {name: cursor, in: query, description: "Opaque continuation token from the previous page. It pins the page size, entity, field filter, and last consumed createdAt/id keyset; pass it back verbatim, omit limit to reuse the pinned size, and do not change an explicitly repeated limit or filter.", schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
      responses:
        "200": {description: KG entity audit history page, content: {application/json: {schema: {$ref: "#/components/schemas/KGEntityAuditHistoryEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/extractions/runs/active:
    get:
      operationId: listActiveKGExtractionRuns
      x-agent-brain-scope: ab:kg:read
      summary: List the extraction runs still in flight
      description: Poll this to follow extraction progress. An organization with nothing running answers with an empty list rather than a 404. There is no streaming variant.
      security: [{apiKey: []}]
      responses:
        "200": {description: Active extraction runs, content: {application/json: {schema: {$ref: "#/components/schemas/KGExtractionRunListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/extractions/runs/{runId}:
    get:
      operationId: getKGExtractionRun
      x-agent-brain-scope: ab:kg:read
      summary: Read one extraction run
      description: Unknown and foreign-tenant run ids answer identically with 404, so the response cannot be used to probe which runs exist.
      security: [{apiKey: []}]
      parameters:
        - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        "200": {description: Extraction run, content: {application/json: {schema: {$ref: "#/components/schemas/KGExtractionRunEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/extractions/cost-halt-recent:
    get:
      operationId: getKGExtractionCostHalt
      x-agent-brain-scope: ab:kg:read
      summary: Report whether extraction hit the spending cap recently
      description: The resolved window is echoed back. A value outside the documented bounds is rejected rather than clamped.
      security: [{apiKey: []}]
      parameters:
        - {name: hours, in: query, description: Lookback window in hours, schema: {type: integer, minimum: 1, maximum: 168, default: 24}}
      responses:
        "200": {description: Cost-cap state, content: {application/json: {schema: {$ref: "#/components/schemas/KGCostHaltEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/graph/analyzer/status:
    get:
      operationId: getKGAnalyzerStatus
      x-agent-brain-scope: ab:kg:admin
      summary: Report how stale the community and centrality figures are
      description: status is "pending" when extraction landed after the last completed analysis, so a caller can tell whether the graph reads it just made reflect the latest ingest.
      security: [{apiKey: []}]
      responses:
        "200": {description: Analyzer freshness, content: {application/json: {schema: {$ref: "#/components/schemas/KGAnalyzerStatusEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/graph/analyze:
    post:
      operationId: analyzeKGGraph
      x-agent-brain-scope: ab:kg:admin
      summary: Ask for a fresh graph analysis
      description: >-
        Takes no body — the organization and the acting identity both come from the API key. Answers 202 with a
        correlation handle; the analysis runs in the background and is followed by polling the analyzer status, not
        by a stream. Idempotency-Key is required: the accepted response, API-key audit and durable outbox event commit
        together. Replays within the seven-day idempotency retention window return the same response without another
        budget reservation, audit or event; after that window the key may be treated as a new submission. This action shares one organization-wide
        expensive-operation budget with both quality generators across CMS, Partner keys and server replicas. An
        exhausted budget answers 429 with Retry-After. If the budget store or audit record is unavailable the request
        is refused with 503 and nothing is enqueued.
      security: [{apiKey: []}]
      parameters: [{$ref: "#/components/parameters/IdempotencyKey"}]
      responses:
        "202": {description: Analysis accepted, content: {application/json: {schema: {$ref: "#/components/schemas/KGAnalyzeAcceptedEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/row-fact-runs/{runId}:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getKGRowFactRun
      x-agent-brain-scope: ab:kg:read
      summary: Read one row-fact run and a page of its proposals
      description: >-
        proposalSummary counts the whole run whatever page is being read, so a poller watches it without paging.
        The proposals array is one window over the run's fixed proposal set; the continuation token pins the page size
        and the run it was issued for. Unknown and foreign-tenant run ids answer identically with 404.
      security: [{apiKey: []}]
      parameters:
        - {name: cursor, in: query, description: "Opaque continuation token from the previous page. Pass it back verbatim; a limit that contradicts it is rejected.", schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 200, default: 50}}
      responses:
        "200": {description: Row-fact run with a page of proposals, content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactRunDetailEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/row-fact-runs/{runId}/proposals:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    patch:
      operationId: reviewKGRowFactProposals
      x-agent-brain-scope: ab:kg:execute
      summary: Accept or reject row-fact proposals
      description: >-
        All-or-nothing. Every id is resolved against the run first, so a list carrying an id from another run, a
        duplicate, an id claimed by both lists, or a proposal already promoted is refused with 400 and no proposal row
        changes. A proposal that stops being reviewable between the check and the write — a promotion landing in
        between — rolls the whole review back with 409 rather than applying part of it. When the audit record cannot be
        written the request is refused with 503 and nothing changes.
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactDecisionRequest"}}}
      responses:
        "200": {description: Row-fact run after the review, content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactRunDetailEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/row-fact-runs/{runId}/promote:
    parameters:
      - {name: runId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: promoteKGRowFactRun
      x-agent-brain-scope: ab:kg:execute
      summary: Promote accepted row-fact proposals into the graph
      description: >-
        Promotes in one transaction. An absent or empty proposalIds promotes every accepted proposal on the run.
        Entities and relations are upserted on their natural keys and only proposals still in the accepted state are
        selected, so repeating a promotion creates nothing further and is safe without an Idempotency-Key — none is
        required and none is honoured. A repeat over a run with nothing left to promote is recognized as a no-op: it
        answers the run's current status with promotedEntities 0 and changes nothing, so a retry never costs the run
        the promoted state it already reached. A proposal nobody accepted is refused rather than silently skipped.
        Promotion also requires the source to be unchanged: a run whose Knowledge has since been deleted, emptied or
        edited answers 409, because its proposals describe rows the document no longer contains and promoting them
        would overwrite live graph entities with stale values. Regenerate against the current source instead. When the
        audit record cannot be written the request is refused with 503 and nothing is promoted.
      security: [{apiKey: []}]
      requestBody:
        required: false
        content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactPromoteRequest"}}}
      responses:
        "200": {description: Promotion result, content: {application/json: {schema: {$ref: "#/components/schemas/KGRowFactPromotionEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/report:
    get:
      operationId: getKGQualityReport
      x-agent-brain-scope: ab:kg:admin
      summary: Read the organization's graph quality report
      description: >-
        An organization-wide census of the graph: coverage against Knowledge, extraction run states, structural
        figures, noise signals, duplicate-name pressure and governance gaps. Every figure is a count over the bound
        organization; no entity names, document content or actor identities appear here. This is an operator read
        rather than a per-entity one — the counts are not narrowed by the row-level ACL the graph reads apply — so it
        takes the administration scope, not ab:kg:read.
      security: [{apiKey: []}]
      responses:
        "200": {description: Graph quality report, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityReportEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/issues:
    get:
      operationId: listKGQualityIssues
      x-agent-brain-scope: ab:kg:admin
      summary: List the actionable quality buckets behind the report
      description: >-
        Five fixed buckets, each with its own count and a bounded sample of the rows behind it. limit bounds the sample
        list inside every bucket, not the number of buckets. reviewable says whether the bucket can be turned into
        review items; the other three are reported but have no review workflow, and naming one of them in a generation
        request is refused. Each sample carries the reason the detection query emitted, which is one of a fixed set of
        diagnostic phrases — not document content.
      security: [{apiKey: []}]
      parameters:
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 25}, description: "Samples per bucket. A value above the maximum is refused, never clamped."}
      responses:
        "200": {description: Quality issue buckets, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityIssuesEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/actions/replay-missing-kb:
    post:
      operationId: replayKGQualityMissingKnowledge
      x-agent-brain-scope: ab:kg:admin
      summary: Queue extraction for Knowledge carrying no graph entity
      description: >-
        Either the whole backlog or a named set, never both and never neither. A named set is filtered against live
        coverage first, so ids that already have graph entities drop out and a request whose ids all drop out is
        refused with 400 rather than reported as an empty success. Asking for the whole backlog when it exceeds the
        maximum is refused, not truncated to a prefix. The work is queued through the shared admin batch handler, which
        applies the extraction feature policy and the replay rate limiter — an exhausted limiter answers 429 and
        neither it nor the feature gate can be bypassed from this route. That handler applies its own ceiling derived
        from the organization's replay budget, which can be lower than this route's maximum, so a selection accepted
        here can still be refused with 400. An organization with no chat model configured answers 422. A broker outage
        is not a refusal: the batch is created and every item comes back failed, so read queuedCount rather than the
        status code. When the audit record cannot be written the request is refused with 503 and nothing is queued.
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityReplayRequest"}}}
      responses:
        "202": {description: Extraction batch accepted, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityBatchEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/actions/generate-dedup-candidates:
    post:
      operationId: generateKGQualityDedupCandidates
      x-agent-brain-scope: ab:kg:admin
      summary: Materialize dedup candidates from cross-type duplicate names
      description: >-
        Creates pending merge candidates from entities that share a normalized name across two entity types. Nothing is
        merged: each candidate still needs an explicit approval on the dedup surface. Repeating the call is safe — the
        pair query excludes pairs that already carry a candidate, so the second call reports zero created rather than
        duplicating the queue. This action shares one organization-wide expensive-operation budget with graph analysis
        and review-item generation across CMS, Partner keys and server replicas. An exhausted budget answers 429 with
        Retry-After. If the budget store or audit record is unavailable the request is refused with 503 and no candidate
        is created.
      security: [{apiKey: []}]
      requestBody:
        required: false
        content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityDedupRequest"}}}
      responses:
        "200": {description: Candidate generation result, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityCandidateEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/actions/generate-review-items:
    post:
      operationId: generateKGQualityReviewItems
      x-agent-brain-scope: ab:kg:admin
      summary: Turn quality issue samples into review items
      description: >-
        Materializes review rows from the two reviewable buckets. An absent issueKey generates from both; naming a
        bucket that has no review workflow is refused. Generation inserts on conflict-do-nothing keyed on the item's
        issue and resource, so a repeat reports the existing rows as skipped rather than creating a second entry for
        the same entity. This action shares one organization-wide expensive-operation budget with graph analysis and
        dedup generation across CMS, Partner keys and server replicas. An exhausted budget answers 429 with Retry-After.
        If the budget store or audit record is unavailable the request is refused with 503 and no item is created.
      security: [{apiKey: []}]
      requestBody:
        required: false
        content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityGenerateReviewItemsRequest"}}}
      responses:
        "200": {description: Review item generation result, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityCandidateEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/review-items:
    get:
      operationId: listKGQualityReviewItems
      x-agent-brain-scope: ab:kg:admin
      summary: List the graph quality review queue
      description: >-
        One page of the review queue, newest first. Reading it also retires items whose entity has since been deleted or
        orphaned, so a poller is what keeps the queue honest; those rows come back with status resolved and isActionable
        false. The continuation token is opaque — pass it back verbatim. An absent nextCursor is the end of the queue.
      security: [{apiKey: []}]
      parameters:
        - {name: issueKey, in: query, schema: {type: string, enum: [noise.entity_candidates, graph.isolated_entities]}}
        - {name: status, in: query, schema: {type: string, enum: [pending, kept, dismissed, resolved, deleted, all]}, description: "Absent and all both mean every status."}
        - {name: reasonCode, in: query, schema: {type: string, maxLength: 120}, description: "Exact match against the reason the detection query recorded."}
        - {name: cursor, in: query, schema: {type: string}, description: "Opaque createdAt/id keyset token from the previous page. It pins the page size plus issueKey/status/reasonCode filters; omit limit to reuse the pinned size, while an explicitly repeated limit or filter that disagrees is rejected."}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 50}}
      responses:
        "200": {description: Review queue page, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityReviewListEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/review-items/{reviewItemId}/impact:
    parameters:
      - {name: reviewItemId, in: path, required: true, schema: {type: string, format: uuid}}
    get:
      operationId: getKGQualityReviewItemImpact
      x-agent-brain-scope: ab:kg:admin
      summary: Preview what deleting a review item's entity would remove
      description: >-
        Counts the entity's sources and relations, samples a few of the relations, and issues the deleteToken the
        deletion has to carry. The token is not a secret — it pins a deletion to the item and entity the caller was
        shown, which together with confirmName is what makes a stale preview fail closed rather than delete something
        the caller never saw. The item must still be actionable: one already kept, dismissed or deleted answers 409, so
        a preview never describes a decision that cannot be made. Unknown and foreign-tenant item ids answer identically
        with 404.
      security: [{apiKey: []}]
      responses:
        "200": {description: Deletion impact preview, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityImpactEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/review-items/{reviewItemId}/keep:
    parameters:
      - {name: reviewItemId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: keepKGQualityReviewItem
      x-agent-brain-scope: ab:kg:admin
      summary: Keep a flagged entity and close its review item
      description: >-
        Marks the flag a false positive. The entity is untouched; only the review item moves. Takes no body, and one
        sent is refused rather than ignored. The transition is single-shot: repeating it answers 409 naming the state
        the item already reached rather than replaying a success, which is why no Idempotency-Key is required or
        honoured. Unknown and foreign-tenant item ids answer identically with 404 and change nothing in either
        organization. When the audit record cannot be written the request is refused with 503 and the item does not move.
      security: [{apiKey: []}]
      responses:
        "200": {description: Review item kept, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityReviewDecisionEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/review-items/{reviewItemId}/dismiss:
    parameters:
      - {name: reviewItemId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: dismissKGQualityReviewItem
      x-agent-brain-scope: ab:kg:admin
      summary: Dismiss a review item without touching its entity
      description: >-
        Accepts the flag and takes the item off the queue. The entity is untouched — deleting it is a separate,
        explicitly confirmed operation. Same single-shot contract as keep: takes no body, repeating answers 409, and an
        unwritable audit record refuses with 503 before the item moves. Unknown and foreign-tenant item ids answer
        identically with 404.
      security: [{apiKey: []}]
      responses:
        "200": {description: Review item dismissed, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityReviewDecisionEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/review-items/{reviewItemId}/delete-entity:
    parameters:
      - {name: reviewItemId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: deleteKGQualityReviewEntity
      x-agent-brain-scope: ab:kg:admin
      summary: Delete the entity a review item flagged
      description: >-
        Removes the entity and its relations, and closes the review item, in one transaction. Both confirmation fields
        are required and both are checked: deleteToken must equal the one the impact read issued for this item and
        entity, and confirmName must equal the entity's current name — so a caller that read the impact before someone
        renamed or replaced the entity is refused with 400 rather than deleting something it never saw. The item must
        still be actionable, or the answer is 409. Unknown and foreign-tenant item ids answer identically with 404 and
        neither organization changes. When the audit record cannot be written the request is refused with 503 and
        nothing is deleted. This is the most destructive operation on this contract; the deletion is not reversible from
        the API.
      security: [{apiKey: []}]
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityDeleteEntityRequest"}}}
      responses:
        "200": {description: Entity deleted and review item closed, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityReviewDecisionEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
  /kg/quality/review-items/{reviewItemId}/reextract-sources:
    parameters:
      - {name: reviewItemId, in: path, required: true, schema: {type: string, format: uuid}}
    post:
      operationId: reextractKGQualityReviewSources
      x-agent-brain-scope: ab:kg:admin
      summary: Re-extract the Knowledge behind an isolated entity
      description: >-
        Queues a fresh extraction over the entity's live Knowledge sources, which is how an entity with no edges gets a
        second chance at them. Only the graph.isolated_entities bucket supports this: a noise candidate is flagged for
        what its name looks like, not for missing edges, so re-reading its source would change nothing and the request
        is refused with 400. An entity whose sources have all been deleted is refused the same way. The work is queued
        through the shared admin batch handler, so the extraction feature policy and the replay rate limiter apply — 429
        comes from there and cannot be bypassed, as does a 400 when the entity has more live sources than the
        organization's replay budget allows in one batch, and a 422 when no chat model is configured. A broker outage is
        not a refusal: the batch is created and every item comes back failed, so read queuedCount rather than the status
        code. The item must still be actionable, or the answer is 409. Unknown and foreign-tenant item ids answer
        identically with 404. When the audit record cannot be written the request is refused with 503 and nothing is
        queued.
      security: [{apiKey: []}]
      requestBody:
        required: false
        content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityReextractRequest"}}}
      responses:
        "202": {description: Re-extraction batch accepted, content: {application/json: {schema: {$ref: "#/components/schemas/KGQualityBatchEnvelope"}}}}
        "400": {$ref: "#/components/responses/BadRequest"}
        "401": {$ref: "#/components/responses/Unauthorized"}
        "403": {$ref: "#/components/responses/Forbidden"}
        "404": {$ref: "#/components/responses/NotFound"}
        "409": {$ref: "#/components/responses/Conflict"}
        "413": {$ref: "#/components/responses/PayloadTooLarge"}
        "422": {$ref: "#/components/responses/UnprocessableEntity"}
        "429": {$ref: "#/components/responses/TooManyRequests"}
        "500": {$ref: "#/components/responses/InternalServerError"}
        "502": {$ref: "#/components/responses/BadGateway"}
        "503": {$ref: "#/components/responses/ServiceUnavailable"}
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
  parameters:
    AIProviderID:
      name: providerId
      in: path
      required: true
      schema: {type: string, format: uuid}
    AIModelID:
      name: modelId
      in: path
      required: true
      schema: {type: string, format: uuid}
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Caller-generated UUID that makes a retry of this submission safe to send.
      schema:
        type: string
        format: uuid
    ResourceType:
      name: resourceType
      in: query
      required: true
      schema:
        type: string
        enum: [folder, connector, knowledge, media_asset, workflow, kg_entity, kg_relation]
    ResourceID:
      name: resourceId
      in: query
      required: true
      schema:
        type: string
        format: uuid
    PermissionAction:
      name: action
      in: query
      required: true
      schema:
        type: string
        enum: [read, write, admin, run, run_as, approve_checkpoint]
    TargetUserID:
      name: userId
      in: query
      required: true
      schema:
        type: string
        format: uuid
    TrashResourceType:
      name: resourceType
      in: path
      required: true
      schema:
        type: string
        enum: [folder, knowledge, media_asset]
    TrashResourceID:
      name: resourceId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    CostScope: {name: scope, in: query, required: false, schema: {type: string, default: all, enum: [all, chat, embedding, agent, knowledge, transcription]}}
    UsageFrom: {name: from, in: query, required: false, schema: {type: string}, description: YYYY-MM-DD or RFC3339 inclusive lower bound.}
    UsageTo: {name: to, in: query, required: false, schema: {type: string}, description: YYYY-MM-DD or RFC3339 inclusive upper bound.}
    UsageProvider: {name: provider, in: query, required: false, schema: {type: string}}
    UsageModel: {name: model, in: query, required: false, schema: {type: string}}
    UsageUseCase: {name: useCase, in: query, required: false, schema: {type: string}, description: Comma-separated call types; transcribe expands to transcription and transcription_refine.}
    UsageStatus: {name: status, in: query, required: false, schema: {type: string, enum: [reserving, abandoned, success, failed, rate_limited, circuit_open, timeout]}}
    UsageSourceEntityType: {name: sourceEntityType, in: query, required: false, schema: {type: string}}
    UsageSourceEntityID: {name: sourceEntityId, in: query, required: false, schema: {type: string, format: uuid}}
    RequiredUsageSourceEntityType: {name: sourceEntityType, in: query, required: true, schema: {type: string, enum: [knowledge, kg_entity, media_processing_run, workflow_run]}}
    RequiredUsageSourceEntityID: {name: sourceEntityId, in: query, required: true, schema: {type: string, format: uuid}}
    UsageCursor: {name: cursor, in: query, required: false, schema: {type: string}}
    UsageLimit: {name: limit, in: query, required: false, schema: {type: integer, minimum: 1, maximum: 50}}
    AuditActorUserID: {name: actorUserId, in: query, required: false, schema: {type: string, format: uuid}}
    AuditResourceType: {name: resourceType, in: query, required: false, schema: {type: string, pattern: "^[a-z][a-z0-9_]{0,63}$"}, description: Audit resource types are an open set owned by the emitting modules; an unknown but well-formed token simply matches nothing.}
    AuditResourceID: {name: resourceId, in: query, required: false, schema: {type: string, format: uuid}}
    AuditActionKey: {name: actionKey, in: query, required: false, schema: {type: string, pattern: "^[a-z][a-z0-9_.]{0,31}$"}}
    AuditSource: {name: source, in: query, required: false, schema: {type: string, enum: [direct_grant, inherited, owner_bypass, admin_bypass, org_default, denied_no_grant, denied_by_grant, system]}}
    AuditFrom: {name: from, in: query, required: false, schema: {type: string, format: date-time}, description: RFC3339 inclusive lower bound on createdAt.}
    AuditTo: {name: to, in: query, required: false, schema: {type: string, format: date-time}, description: "RFC3339 inclusive upper bound on createdAt. Defaults to the instant the first page was served and is then pinned inside the cursor for the rest of the walk."}
    AuditCursor: {name: cursor, in: query, required: false, schema: {type: string}, description: "Opaque continuation token from nextCursor. It pins both the page size and the upper time bound of the walk, so rows written after the walk started cannot shift the window; sending a limit or to that disagrees with the token is rejected."}
    AuditLimit: {name: limit, in: query, required: false, schema: {type: integer, minimum: 1, maximum: 100}}
    AuditSummaryRange: {name: range, in: query, required: true, schema: {type: string, enum: [24h, 7d]}}
    DashboardTrendRange: {name: range, in: query, required: true, schema: {type: string, enum: [7d, 30d, 90d]}}
    DashboardTrendMetric: {name: metric, in: query, required: true, schema: {type: string, enum: [entities, cost, runs]}}
    DashboardStatsDays: {name: days, in: query, required: false, schema: {type: integer, minimum: 1, maximum: 30, default: 7}, description: "Trailing window in days. A value outside the range is rejected rather than clamped, so a caller never receives a window it did not request."}
  schemas:
    KnowledgeSearchRequest:
      type: object
      additionalProperties: false
      required: [query]
      properties:
        query:
          type: string
          minLength: 1
          description: Must remain non-empty after Agent Brain query normalization.
        limit:
          type: integer
          minimum: 1
          maximum: 50
          default: 10
    KnowledgeSearchItem:
      type: object
      additionalProperties: false
      required: [id, title, snippet, score]
      properties:
        id: {type: string, format: uuid}
        title: {type: string}
        snippet: {type: string}
        score: {type: number}
    KnowledgeSearchResult:
      type: object
      additionalProperties: false
      required: [items, total]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/KnowledgeSearchItem"}
        total: {type: integer, minimum: 0, maximum: 50}
    KnowledgeSearchEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KnowledgeSearchResult"}
    KnowledgeContextRequest:
      type: object
      additionalProperties: false
      required: [question]
      properties:
        question:
          type: string
          minLength: 1
          description: Must remain non-empty after Agent Brain query normalization.
        limit:
          type: integer
          minimum: 1
          maximum: 20
          default: 8
        includeCitations:
          type: boolean
          default: true
    KnowledgeContextEvidence:
      type: object
      additionalProperties: false
      required: [id, kind, title, snippet, score]
      properties:
        id: {type: string}
        kind: {type: string, enum: [knowledge, knowledge_chunk]}
        title: {type: string}
        snippet: {type: string}
        score: {type: number}
        citationIds:
          type: array
          items: {type: string}
    KnowledgeContextCitation:
      type: object
      additionalProperties: false
      required: [id, knowledgeId, knowledgeTitle]
      properties:
        id: {type: string}
        knowledgeId: {type: string, format: uuid}
        knowledgeTitle: {type: string}
        chunkId: {type: string, format: uuid}
        chunkIndex: {type: integer, minimum: 0}
    KnowledgeContextResult:
      type: object
      additionalProperties: false
      required: [evidence, citations]
      properties:
        evidence:
          type: array
          items: {$ref: "#/components/schemas/KnowledgeContextEvidence"}
        citations:
          type: array
          items: {$ref: "#/components/schemas/KnowledgeContextCitation"}
        degraded: {type: boolean}
        degradedReason: {type: string}
    KnowledgeContextEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KnowledgeContextResult"}
    MediaUploadPresignRequest:
      type: object
      additionalProperties: false
      required: [fileName, sizeBytes, contentType, kind]
      properties:
        fileName:
          type: string
          minLength: 1
          maxLength: 500
        sizeBytes:
          type: integer
          format: int64
          minimum: 1
          maximum: 500000000
        contentType:
          type: string
        kind:
          type: string
          enum: [audio, video, image, raw_doc]
        folderId:
          type: string
          format: uuid
    MediaUploadPresign:
      type: object
      required: [assetId, putUrl, expiresAt, fields]
      properties:
        assetId:
          type: string
          format: uuid
        putUrl:
          type: string
          format: uri
        expiresAt:
          type: string
          format: date-time
        fields:
          type: object
          additionalProperties:
            type: string
    MediaUploadPresignEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/MediaUploadPresign"
    MediaUploadCommitRequest:
      type: object
      additionalProperties: false
      required: [assetId]
      properties:
        assetId:
          type: string
          format: uuid
        sha256:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        contentType:
          type: string
    MediaUploadCommit:
      type: object
      required: [assetId, status]
      properties:
        assetId:
          type: string
          format: uuid
        status:
          type: string
          const: ready
    MediaUploadCommitEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/MediaUploadCommit"
    MediaUploadCommitConflictEnvelope:
      type: object
      required: [success, error, data]
      properties:
        success:
          type: boolean
          const: false
        error:
          $ref: "#/components/schemas/ErrorDetail"
        data:
          $ref: "#/components/schemas/MediaUploadCommit"
        message:
          type: string
    MediaAsset:
      type: object
      required: [id, kind, mimeType, fileName, sizeBytes, status, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        folderId: {type: string, format: uuid}
        kind: {type: string, enum: [audio, video, image, raw_doc]}
        mimeType: {type: string}
        fileName: {type: string}
        sizeBytes: {type: integer, format: int64}
        status: {type: string, enum: [uploaded, processing, ready, failed]}
        sha256: {type: string, pattern: "^[0-9a-fA-F]{64}$"}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    MediaAssetEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaAsset"}
    MediaAssetList:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/MediaAsset"}
        nextCursor:
          type: [string, "null"]
    MediaAssetListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaAssetList"}
    MediaAssetUpdateRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        fileName: {type: string, minLength: 1, maxLength: 500}
        folderId:
          type: [string, "null"]
          format: uuid
          description: Set null to move the asset to the organization root.
    MediaDownload:
      type: object
      required: [url, expiresAt]
      properties:
        url: {type: string, format: uri}
        expiresAt: {type: string, format: date-time}
    MediaDownloadEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaDownload"}
    MediaTranscriptionRequest:
      type: object
      additionalProperties: false
      properties:
        modelId:
          type: string
          format: uuid
        refineModelId:
          type: string
          format: uuid
        autoKbIngest:
          type: boolean
        autoExtractKg:
          type: boolean
        targetKnowledgeId:
          type: string
          format: uuid
        step1TemplateId:
          type: string
          format: uuid
        step2TemplateId:
          type: string
          format: uuid
        aiRefineMarkdown:
          type: boolean
    MediaRefineRequest:
      type: object
      additionalProperties: false
      properties:
        modelId: {type: string, format: uuid}
        step2TemplateId: {type: string, format: uuid}
    MediaBulkDigestRequest:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        idempotencyKey: {type: string}
        items:
          type: array
          minItems: 1
          items: {$ref: "#/components/schemas/MediaBulkDigestItem"}
    MediaBulkDigestItem:
      type: object
      additionalProperties: false
      required: [mediaId]
      properties:
        mediaId: {type: string, format: uuid}
        priority: {type: integer}
        options: {$ref: "#/components/schemas/MediaBulkDigestOptions"}
    MediaBulkDigestOptions:
      type: object
      additionalProperties: false
      properties:
        extractKg: {type: boolean}
        refine: {type: boolean}
        addKb: {type: boolean}
        forceReplace: {type: boolean}
        step1TemplateId: {type: string, format: uuid}
        step2TemplateId: {type: string, format: uuid}
        kgProfile: {type: string}
        promptOverrides:
          type: array
          items:
            type: object
            additionalProperties: false
            required: [useCase, pipelineStep, templateId]
            properties:
              useCase: {type: string}
              pipelineStep: {type: string}
              templateId: {type: string, format: uuid}
        modelOverrides:
          type: object
          additionalProperties:
            type: object
            additionalProperties: false
            required: [modelId]
            properties:
              modelId: {type: string, format: uuid}
              source: {type: string}
    MediaBulkDigestResult:
      type: object
      required: [jobId, queued, skipped, deduplicated]
      properties:
        jobId: {type: string, format: uuid}
        queued: {type: integer, minimum: 0}
        skipped: {type: integer, minimum: 0}
        deduplicated: {type: boolean}
    MediaBulkDigestEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaBulkDigestResult"}
    MediaBulkJob:
      type: object
      required: [id, status, totalItems, completedItems, failedItems, createdAt, items]
      properties:
        id: {type: string, format: uuid}
        status: {type: string, enum: [queued, running, done, failed, cancelled]}
        totalItems: {type: integer, minimum: 0}
        completedItems: {type: integer, minimum: 0}
        failedItems: {type: integer, minimum: 0}
        startedAt: {type: string, format: date-time}
        finishedAt: {type: string, format: date-time}
        errorMessage: {type: string, description: Sanitized public failure message.}
        createdAt: {type: string, format: date-time}
        items:
          type: array
          items: {$ref: "#/components/schemas/MediaBulkJobItem"}
    MediaBulkJobItem:
      type: object
      required: [id, mediaId, status, priority, createdAt]
      properties:
        id: {type: string, format: uuid}
        mediaId: {type: string, format: uuid}
        status: {type: string, enum: [queued, running, done, failed, cancelled]}
        runId: {type: string, format: uuid}
        errorMessage: {type: string, description: Sanitized public failure message.}
        startedAt: {type: string, format: date-time}
        finishedAt: {type: string, format: date-time}
        priority: {type: integer}
        fileName: {type: string}
        kind: {type: string}
        createdAt: {type: string, format: date-time}
    MediaBulkItemPriorityRequest:
      type: object
      additionalProperties: false
      required: [priority]
      properties:
        priority: {type: integer, minimum: 1, maximum: 10000}
    MediaBulkJobItemEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaBulkJobItem"}
    MediaBulkJobEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaBulkJob"}
    MediaBulkJobListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/MediaBulkJob"}}
    MediaSettings:
      type: object
      required: [enabled, transcribeEnabled, partsConcurrency, maxUploadBytesMb, extraMimeDenyList, maxRetryAttempts, refineAutoFailEnabled, dlqReplayDailyQuota, bulkDigestMax, updatedAt]
      properties:
        enabled: {type: boolean, readOnly: true, description: Parent Media gate controlled by the trusted control plane.}
        transcribeEnabled: {type: boolean}
        partsConcurrency: {type: integer, minimum: 1, maximum: 10}
        maxUploadBytesMb: {type: integer, minimum: 1, maximum: 5000}
        extraMimeDenyList: {type: array, items: {type: string}}
        maxRetryAttempts: {type: integer, minimum: 1, maximum: 10}
        refineAutoFailEnabled: {type: boolean}
        dlqReplayDailyQuota: {type: integer, minimum: 0, maximum: 1000}
        bulkDigestMax: {type: integer, minimum: 1, maximum: 1000}
        defaultMediaPipeline: {$ref: "#/components/schemas/FolderPipelineConfig"}
        storageProvider: {type: string, enum: [r2, s3, gcs]}
        storageVerifyStatus: {type: string}
        storageVerifiedAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    MediaSettingsPatch:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        transcribeEnabled: {type: boolean}
        partsConcurrency: {type: integer, minimum: 1, maximum: 10}
        maxUploadBytesMb: {type: integer, minimum: 1, maximum: 5000}
        extraMimeDenyList: {type: array, items: {type: string}}
        maxRetryAttempts: {type: integer, minimum: 1, maximum: 10}
        refineAutoFailEnabled: {type: boolean}
        dlqReplayDailyQuota: {type: integer, minimum: 0, maximum: 1000}
        bulkDigestMax: {type: integer, minimum: 1, maximum: 1000}
        defaultMediaPipeline: {$ref: "#/components/schemas/FolderPipelineConfig"}
    MediaSettingsEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaSettings"}
    MediaStorageCredentialsRequest:
      type: object
      additionalProperties: false
      required: [provider]
      properties:
        provider: {type: string, enum: [r2, s3, gcs]}
        r2:
          type: object
          additionalProperties: false
          required: [accountId, accessKey, secretKey, bucketName]
          properties:
            accountId: {type: string}
            accessKey: {type: string, format: password, writeOnly: true}
            secretKey: {type: string, format: password, writeOnly: true}
            bucketName: {type: string}
        s3:
          type: object
          additionalProperties: false
          required: [region, accessKey, secretKey, bucketName]
          properties:
            region: {type: string}
            accessKey: {type: string, format: password, writeOnly: true}
            secretKey: {type: string, format: password, writeOnly: true}
            bucketName: {type: string}
            endpoint: {type: string, format: uri}
        gcs:
          type: object
          additionalProperties: false
          required: [projectId, serviceAccountJson, bucketName]
          properties:
            projectId: {type: string}
            serviceAccountJson: {type: string, format: password, writeOnly: true}
            bucketName: {type: string}
    MediaStorageResult:
      type: object
      required: [status, provider, bucketName]
      properties:
        status: {type: string, enum: [ok, invalid_credentials, access_denied, bucket_not_found, network_unreachable, signature_mismatch, cors_misconfigured, unknown]}
        provider: {type: string, enum: [r2, s3, gcs]}
        bucketName: {type: string}
    MediaStorageResultEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaStorageResult"}
    MediaStorageReadiness:
      type: object
      required: [ready, reason, effectiveMode, provider, verifyStatus, verifiedAt]
      properties:
        ready: {type: boolean}
        reason: {type: [string, "null"], enum: [no_storage_credential, not_verified, verify_failed, null]}
        effectiveMode: {type: string, enum: [strict]}
        provider: {type: [string, "null"], enum: [r2, s3, gcs, null]}
        verifyStatus: {type: [string, "null"]}
        verifiedAt: {type: [string, "null"], format: date-time}
    MediaStorageReadinessEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaStorageReadiness"}
    MediaStorageCleared:
      type: object
      required: [status]
      properties:
        status: {type: string, const: cleared}
    MediaStorageClearedEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaStorageCleared"}
    MediaKnowledgeIngestRequest:
      type: object
      additionalProperties: false
      properties:
        targetKnowledgeId:
          type: string
          format: uuid
          description: Reserved for a future attach-to-existing flow; Partner V1 rejects non-null values.
    MediaKnowledgeIngest:
      type: object
      required: [knowledgeId, status]
      properties:
        knowledgeId: {type: string, format: uuid}
        status: {type: string, enum: [ingested, already_exists]}
    MediaKnowledgeIngestEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/MediaKnowledgeIngest"}
    MediaProcessingRun:
      type: object
      required: [id, mediaId, status]
      properties:
        id:
          type: string
          format: uuid
        mediaId:
          type: string
          format: uuid
        kind:
          type: string
        status:
          type: string
          enum: [pending, processing, done, failed]
        attempt:
          type: integer
          minimum: 0
        startedAt:
          type: string
          format: date-time
        finishedAt:
          type: string
          format: date-time
        costUsd:
          type: number
        errorMessage:
          type: string
          description: Sanitized public failure message; internal worker and storage diagnostics are never exposed.
        targetKnowledgeId:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
    MediaProcessingRunEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/MediaProcessingRun"
    KnowledgeCategory:
      type: object
      required: [id, name, slug]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
    KnowledgeTag:
      type: object
      required: [id, name, slug]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        color:
          type: string
    KnowledgeUserSummary:
      type: object
      required: [id]
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        username:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        avatarUrl:
          type: string
          format: uri
    KnowledgePublication:
      type: object
      required: [status]
      properties:
        status:
          type: string
        publishedAt:
          type: string
          format: date-time
        unpublishedAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    KnowledgePublicationEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/KnowledgePublication"
    RelatedKnowledge:
      type: object
      required: [id, title, slug, similarity]
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        slug:
          type: string
        similarity:
          type: number
          minimum: -1
          maximum: 1
    RelatedKnowledgeList:
      type: object
      required: [related]
      properties:
        related:
          type: array
          items:
            $ref: "#/components/schemas/RelatedKnowledge"
    RelatedKnowledgeEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/RelatedKnowledgeList"
    KnowledgeVersion:
      type: object
      required: [versionNumber]
      properties:
        versionNumber:
          type: integer
          minimum: 1
        changeSummary:
          type: string
        createdAt:
          type: string
          format: date-time
        htmlContent:
          type: string
        markdownContent:
          type: string
    KnowledgeVersionList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/KnowledgeVersion"
        nextCursor:
          type: string
    KnowledgeVersionListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/KnowledgeVersionList"
    KnowledgeVersionEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/KnowledgeVersion"
    KnowledgeShare:
      type: object
      required: [id, accessCount, isActive]
      properties:
        id:
          type: string
          format: uuid
        expiresAt:
          type: string
          format: date-time
        accessCount:
          type: integer
          minimum: 0
        maxAccessCount:
          type: integer
          minimum: 1
        isActive:
          type: boolean
        createdAt:
          type: string
          format: date-time
        lastAccessedAt:
          type: string
          format: date-time
    KnowledgeShareList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/KnowledgeShare"
        nextCursor:
          type: string
    KnowledgeShareListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/KnowledgeShareList"
    CreateKnowledgeShareRequest:
      type: object
      additionalProperties: false
      properties:
        expiresIn:
          type: string
          pattern: '^[1-9][0-9]*[dh]$'
          examples: [7d]
        maxAccessCount:
          type: integer
          minimum: 1
    CreateKnowledgeShareResult:
      type: object
      required: [id, shareToken]
      properties:
        id:
          type: string
          format: uuid
        shareToken:
          type: string
          readOnly: true
          description: One-time plaintext token. Store securely; it cannot be retrieved later.
    CreateKnowledgeShareEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/CreateKnowledgeShareResult"
    Knowledge:
      type: object
      required: [id, title, slug, canMcpUse, sourceKind, createdBy, tags]
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
          maxLength: 500
        slug:
          type: string
          maxLength: 500
        description:
          type: string
        htmlContent:
          type: string
        markdownContent:
          type: string
        aiContext:
          type: string
        canMcpUse:
          type: boolean
        categoryId:
          type: string
          format: uuid
        category:
          $ref: "#/components/schemas/KnowledgeCategory"
        folderId:
          type: string
          format: uuid
        sourceKind:
          type: string
        sourceRefId:
          type: string
          format: uuid
        createdBy:
          type: string
          format: uuid
        createdByUser:
          $ref: "#/components/schemas/KnowledgeUserSummary"
        status:
          type: string
        currentVersion:
          type: integer
        tags:
          type: array
          items:
            $ref: "#/components/schemas/KnowledgeTag"
        publication:
          $ref: "#/components/schemas/KnowledgePublication"
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    KnowledgeList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Knowledge"
        nextCursor:
          type: string
    KnowledgeListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/KnowledgeList"
    KnowledgeEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/Knowledge"
    KnowledgeCreate:
      type: object
      additionalProperties: false
      required: [title]
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 500
        slug:
          type: string
          maxLength: 500
        description:
          type: string
        htmlContent:
          type: string
        markdownContent:
          type: string
        aiContext:
          type: string
        canMcpUse:
          type: boolean
        categoryId:
          type: string
          format: uuid
        folderId:
          type: string
          format: uuid
        tagIds:
          type: array
          items:
            type: string
            format: uuid
    KnowledgeUpdate:
      type: object
      additionalProperties: false
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 500
        slug:
          type: string
          maxLength: 500
        description:
          type: string
        htmlContent:
          type: string
        markdownContent:
          type: string
        aiContext:
          type: string
        canMcpUse:
          type: boolean
        categoryId:
          type: string
          format: uuid
        folderId:
          type: [string, "null"]
          format: uuid
        changeSummary:
          type: string
        tagIds:
          type: array
          items:
            type: string
            format: uuid
    TrashItem:
      type: object
      required: [id, resourceType, name, deletedAt, depth]
      properties:
        id: {type: string, format: uuid}
        resourceType: {type: string, enum: [folder, knowledge, media_asset]}
        name: {type: string}
        parentId: {type: [string, "null"], format: uuid}
        deletedAt: {type: string, format: date-time}
        depth: {type: integer, minimum: 0}
    TrashList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/TrashItem"}
        nextCursor: {type: string}
    TrashListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/TrashList"}
    TrashPreview:
      type: object
      required: [resourceType, resourceId, resourceCount, childCount, confirmToken]
      properties:
        resourceType: {type: string}
        resourceId: {type: string, format: uuid}
        resourceCount: {type: integer, minimum: 1}
        childCount: {type: integer, minimum: 0}
        confirmToken: {type: string}
    TrashRestore:
      type: object
      required: [rootId, restoredCount]
      properties:
        rootId: {type: string, format: uuid}
        restoredCount: {type: integer, minimum: 1}
    TrashPurge:
      type: object
      required: [rootId, resourceType, purgedCount]
      properties:
        rootId: {type: string, format: uuid}
        resourceType: {type: string}
        purgedCount: {type: integer, minimum: 1}
    TrashPreviewEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/TrashPreview"}
    TrashRestoreEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/TrashRestore"}
    TrashPurgeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/TrashPurge"}
    TrashBulkPurgeRequest:
      type: object
      additionalProperties: false
      required: [resourceType, ids]
      properties:
        resourceType: {type: string, enum: [folder, knowledge, media_asset]}
        ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items: {type: string, format: uuid}
    TrashBulkPurgeResult:
      type: object
      required: [succeeded, failed]
      properties:
        succeeded:
          type: array
          items: {type: string, format: uuid}
        failed:
          type: array
          items:
            type: object
            required: [id, errorId]
            properties:
              id: {type: string, format: uuid}
              errorId: {type: string}
    TrashBulkPurgeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/TrashBulkPurgeResult"}
    FolderCreate:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        parentId: {type: string, format: uuid}
        name: {type: string, minLength: 1, maxLength: 200}
    FolderPipelineKind:
      type: object
      additionalProperties: false
      properties:
        transcribe: {type: boolean}
        refine: {type: boolean}
        vision: {type: boolean}
        ocr: {type: boolean}
        addKB: {type: boolean}
        extractKG: {type: boolean}
    FolderPipelineConfig:
      type: object
      additionalProperties: false
      required: [version]
      properties:
        version: {type: integer, enum: [1, 2]}
        audio: {$ref: "#/components/schemas/FolderPipelineKind"}
        video: {$ref: "#/components/schemas/FolderPipelineKind"}
        image: {$ref: "#/components/schemas/FolderPipelineKind"}
        rawDoc: {$ref: "#/components/schemas/FolderPipelineKind"}
        promptDefaults:
          type: object
          additionalProperties:
            type: object
            additionalProperties: {type: string, format: uuid}
        modelDefaults:
          type: object
          additionalProperties: {type: string, format: uuid}
        kg:
          type: object
          additionalProperties: false
          properties:
            profile: {type: string}
            promptTemplateId: {type: string, format: uuid}
            modelId: {type: string, format: uuid}
    FolderUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        name: {type: string, minLength: 1, maxLength: 200}
        defaultStep1TemplateId:
          type: [string, "null"]
          format: uuid
        defaultStep2TemplateId:
          type: [string, "null"]
          format: uuid
        pipelineConfig:
          oneOf:
            - {$ref: "#/components/schemas/FolderPipelineConfig"}
            - {type: "null"}
    FolderMove:
      type: object
      additionalProperties: false
      required: [newParentId]
      properties:
        newParentId:
          type: [string, "null"]
          format: uuid
    Folder:
      type: object
      required: [id, name, depth, pipelineConfig, effectivePipelineConfig, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        parentId: {type: string, format: uuid}
        name: {type: string}
        depth: {type: integer, minimum: 0, maximum: 16}
        defaultStep1TemplateId: {type: string, format: uuid}
        defaultStep2TemplateId: {type: string, format: uuid}
        pipelineConfig:
          oneOf:
            - {$ref: "#/components/schemas/FolderPipelineConfig"}
            - {type: "null"}
        effectivePipelineConfig:
          $ref: "#/components/schemas/FolderPipelineConfig"
        ancestors:
          type: array
          items:
            type: object
            required: [id, name]
            properties:
              id: {type: string, format: uuid}
              name: {type: string}
        descendantsCount: {type: integer, minimum: 0}
        knowledgesCount: {type: integer, minimum: 0}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    FolderList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/Folder"}
        nextCursor: {type: string}
    FolderListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/FolderList"}
    FolderEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/Folder"}
    FolderDeleteResult:
      type: object
      required: [rootId, deletedCount]
      properties:
        rootId: {type: string, format: uuid}
        deletedCount: {type: integer, minimum: 1}
    FolderDeleteEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/FolderDeleteResult"}
    CategoryCreate:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        name: {type: string, minLength: 1, maxLength: 255}
        parentId: {type: string, format: uuid}
        description: {type: string}
        icon: {type: string, maxLength: 100}
        sortOrder: {type: integer}
    CategoryUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        name: {type: string, minLength: 1, maxLength: 255}
        parentId: {type: [string, "null"], format: uuid}
        description: {type: string}
        icon: {type: string, maxLength: 100}
        sortOrder: {type: integer}
        status: {type: string, enum: [active, inactive]}
    Category:
      type: object
      required: [id, name, slug, sortOrder]
      properties:
        id: {type: string, format: uuid}
        parentId: {type: string, format: uuid}
        name: {type: string}
        slug: {type: string}
        description: {type: string}
        icon: {type: string}
        sortOrder: {type: integer}
        status: {type: string, enum: [active, inactive]}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    CategoryTree:
      allOf:
        - $ref: "#/components/schemas/Category"
        - type: object
          properties:
            children:
              type: array
              items: {$ref: "#/components/schemas/CategoryTree"}
    CategoryList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/Category"}
        nextCursor: {type: string}
    CategoryListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/CategoryList"}
    CategoryTreeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data:
          type: array
          items: {$ref: "#/components/schemas/CategoryTree"}
    CategoryEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/Category"}
    TagCreate:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        color:
          type: string
          maxLength: 20
    TagUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        color:
          type: string
          maxLength: 20
        status:
          type: string
          enum: [active, inactive]
    Tag:
      type: object
      required: [id, name, slug]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        color:
          type: string
        status:
          type: string
          enum: [active, inactive]
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    TagList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/Tag"
        nextCursor:
          type: string
    TagListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/TagList"
    TagEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/Tag"
    ConnectorCreate:
      type: object
      additionalProperties: false
      required: [name, type]
      properties:
        name: {type: string, minLength: 1, maxLength: 255}
        description: {type: string}
        type: {type: string, enum: [credential, endpoint, secret_key, notification, storage]}
        subtype: {type: string, maxLength: 100}
        config:
          description: Provider configuration; accepted for writes and never returned.
          writeOnly: true
        metadata: {}
        visibility: {type: string, enum: [private, internal, public], default: private}
    ConnectorUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        name: {type: string, minLength: 1, maxLength: 255}
        description: {type: string}
        subtype: {type: string, maxLength: 100}
        config:
          description: Replacement provider configuration; accepted for writes and never returned.
          writeOnly: true
        metadata: {}
        visibility: {type: string, enum: [private, internal, public]}
        status: {type: string, enum: [active, inactive]}
    Connector:
      type: object
      required: [id, name, slug, type, visibility]
      properties:
        id: {type: string, format: uuid}
        name: {type: string}
        slug: {type: string}
        description: {type: string}
        type: {type: string, enum: [credential, endpoint, secret_key, notification, storage]}
        subtype: {type: string}
        metadata: {}
        visibility: {type: string, enum: [private, internal, public]}
        status: {type: string, enum: [active, inactive]}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    ConnectorList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/Connector"}
        nextCursor: {type: string}
    ConnectorListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ConnectorList"}
    ConnectorEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/Connector"}
    ConnectorSubtype:
      type: object
      required: [id, type, subtype, name, sortOrder]
      properties:
        id: {type: string, format: uuid}
        type: {type: string, enum: [credential, endpoint, secret_key, notification, storage]}
        subtype: {type: string}
        name: {type: string}
        description: {type: string}
        icon: {type: string}
        configSchema: {}
        configUiSchema: {}
        status: {type: string, enum: [active, inactive]}
        sortOrder: {type: integer}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    ConnectorSubtypeList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/ConnectorSubtype"}
        nextCursor: {type: string}
    ConnectorSubtypeListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ConnectorSubtypeList"}
    ConnectorSubtypeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ConnectorSubtype"}
    Database:
      type: object
      required: [name]
      properties:
        name: {type: string}
        selector: {type: string}
        selectorKind: {type: string}
        isDefault: {type: boolean}
        description: {type: string}
    Schema:
      type: object
      required: [name]
      properties:
        name: {type: string}
        tableCount: {type: integer}
        description: {type: string}
    Column:
      type: object
      required: [name, type]
      properties:
        name: {type: string}
        type: {type: string}
        description: {type: string}
        isNullable: {type: boolean}
        isIndexed: {type: boolean}
        indexNames: {type: array, items: {type: string}}
        isUnique: {type: boolean}
        isPrimary: {type: boolean}
    Table:
      type: object
      required: [schema, name, type]
      properties:
        schema: {type: string}
        name: {type: string}
        type: {type: string, enum: [table, view]}
        description: {type: string}
        rowCount: {type: integer, format: int64}
        columns: {type: array, items: {$ref: "#/components/schemas/Column"}}
        isPartitioned: {type: boolean}
        partitionField: {type: string}
        partitionType: {type: string}
    DatabaseListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/Database"}}
    SchemaListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/Schema"}}
    TableListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/Table"}}
    ColumnListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/Column"}}
    ConnectorTestConfigRequest:
      type: object
      additionalProperties: false
      required: [subtype, config]
      properties:
        subtype: {type: string, minLength: 1, maxLength: 100, pattern: '.*\S.*'}
        config: {type: object, minProperties: 1, additionalProperties: true}
    ConnectorTestResult:
      type: object
      required: [success, responseTimeMs, subtype]
      properties:
        success: {type: boolean}
        responseTimeMs: {type: integer, format: int64, minimum: 0}
        error: {type: string, enum: [Connector connection test failed]}
        subtype: {type: string}
    ConnectorTestEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ConnectorTestResult"}
    ConnectorSQLQueryRequest:
      type: object
      additionalProperties: false
      required: [sql]
      properties:
        sql: {type: string, minLength: 1, maxLength: 65536}
        database: {type: string, maxLength: 255}
        limit: {type: integer, minimum: 1, maximum: 5000, default: 1000}
    ConnectorSQLExecuteRequest:
      type: object
      additionalProperties: false
      required: [sql]
      properties:
        sql: {type: string, minLength: 1, maxLength: 65536}
        database: {type: string, maxLength: 255}
    ConnectorPreview:
      type: object
      required: [columns, rows]
      properties:
        columns: {type: array, items: {type: string}}
        rows: {type: array, items: {type: object, additionalProperties: true}}
        totalCount: {type: integer, format: int64}
    ConnectorQueryResult:
      type: object
      required: [columns, rows, rowCount, executionMs]
      properties:
        columns: {type: array, items: {type: string}}
        rows: {type: array, items: {type: object, additionalProperties: true}}
        rowCount: {type: integer, minimum: 0}
        executionMs: {type: integer, minimum: 0}
    ConnectorExecuteResult:
      type: object
      required: [rowsAffected, executionMs]
      properties:
        rowsAffected: {type: integer, format: int64}
        executionMs: {type: integer, minimum: 0}
    ConnectorPreviewEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ConnectorPreview"}
    ConnectorQueryEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ConnectorQueryResult"}
    ConnectorExecuteEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ConnectorExecuteResult"}
    PromptTemplate:
      type: object
      additionalProperties: false
      required: [id, name, slug, useCase, templateText, isSystem, isDefault, status, currentVersionNumber, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        name: {type: string}
        slug: {type: string}
        description: {type: string}
        useCase: {type: string}
        pipelineStep: {type: string, enum: [step1, step2]}
        templateText: {type: string}
        variables: {}
        category: {type: string}
        modelConfig: {}
        isSystem: {type: boolean}
        isDefault: {type: boolean}
        status: {type: string, enum: [active, inactive]}
        currentVersionNumber: {type: integer, minimum: 1}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    PromptTemplateEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/PromptTemplate"}
    PromptTemplateList:
      type: object
      required: [items, total, limit, offset]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/PromptTemplate"}}
        total: {type: integer, format: int64, minimum: 0}
        limit: {type: integer, minimum: 1, maximum: 200}
        offset: {type: integer, minimum: 0}
    PromptTemplateListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/PromptTemplateList"}
    PromptTemplateCreate:
      type: object
      additionalProperties: false
      required: [name, slug, useCase, templateText]
      properties:
        name: {type: string, minLength: 1, maxLength: 120}
        slug: {type: string, pattern: "^[a-z0-9-]+$", maxLength: 120}
        description: {type: [string, "null"], maxLength: 500}
        useCase: {type: string, minLength: 1, maxLength: 40}
        pipelineStep: {type: [string, "null"], enum: [step1, step2, null]}
        templateText: {type: string, minLength: 10, maxLength: 16000}
        variables: {}
        category: {type: [string, "null"]}
        modelConfig: {}
        isDefault: {type: boolean, default: false}
        changeSummary: {type: [string, "null"]}
    PromptTemplateUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        name: {type: string, minLength: 1, maxLength: 120}
        description: {type: string, maxLength: 500}
        templateText: {type: string, minLength: 10, maxLength: 16000}
        modelConfig: {}
        category: {type: string}
        status: {type: string, enum: [active, inactive]}
        isDefault: {type: boolean}
        changeSummary: {type: string}
    PromptTemplateVersion:
      type: object
      additionalProperties: false
      required: [id, templateId, versionNumber, templateText, createdAt]
      properties:
        id: {type: string, format: uuid}
        templateId: {type: string, format: uuid}
        versionNumber: {type: integer, minimum: 1}
        templateText: {type: string}
        modelConfig: {}
        changeSummary: {type: string}
        createdAt: {type: string, format: date-time}
    PromptTemplateVersionList:
      type: object
      required: [items, total, limit, offset]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/PromptTemplateVersion"}}
        total: {type: integer, format: int64, minimum: 0}
        limit: {type: integer, minimum: 1, maximum: 200}
        offset: {type: integer, minimum: 0}
    PromptTemplateVersionListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/PromptTemplateVersionList"}
    ResolvedPromptTemplate:
      type: object
      additionalProperties: false
      required: [versionNumber, templateText, tier]
      properties:
        id: {type: string, format: uuid}
        versionNumber: {type: integer, minimum: 0}
        templateText: {type: string}
        modelConfig: {}
        tier: {type: integer, minimum: 1, maximum: 5}
    ResolvedPromptTemplateEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/ResolvedPromptTemplate"}
    PromptUseCase:
      type: object
      additionalProperties: false
      required: [useCase, displayName, isSystem, isActive]
      properties:
        useCase: {type: string}
        displayName: {type: string}
        description: {type: string}
        relatedCapability: {type: string}
        pipelineSteps: {}
        isSystem: {type: boolean}
        isActive: {type: boolean}
    PromptUseCaseEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/PromptUseCase"}
    PromptUseCaseListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/PromptUseCase"}}
    PromptUseCaseCreate:
      type: object
      additionalProperties: false
      required: [useCase, displayName, isActive]
      properties:
        useCase: {type: string, pattern: "^[a-z0-9-]+$", maxLength: 40}
        displayName: {type: string, minLength: 1, maxLength: 100}
        description: {type: [string, "null"]}
        relatedCapability: {type: [string, "null"]}
        pipelineSteps: {}
        isActive: {type: boolean}
    PromptUseCaseUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        displayName: {type: string, minLength: 1, maxLength: 100}
        description: {type: string}
        relatedCapability: {type: string}
        pipelineSteps: {}
        isActive: {type: boolean}
    PromptUseCaseActiveUpdate:
      type: object
      additionalProperties: false
      required: [active]
      properties:
        active: {type: boolean}
    QueryLog:
      type: object
      additionalProperties: false
      required: [id, sqlQuery, status, createdAt]
      properties:
        id: {type: string, format: uuid}
        apiKeyId: {type: string, format: uuid}
        userId: {type: string, format: uuid}
        connectorId: {type: string, format: uuid}
        sourceId: {type: string}
        source: {type: string}
        sqlQuery: {type: string}
        tablesAccessed: {type: array, items: {type: string}}
        status: {type: string, enum: [authorized, success, denied, error]}
        executionTimeMs: {type: integer, minimum: 0}
        validationTimeMs: {type: integer, minimum: 0}
        poolTimeMs: {type: integer, minimum: 0}
        rowsAffected: {type: integer, format: int64}
        bytesProcessed: {type: integer, format: int64, minimum: 0}
        bytesBilled: {type: integer, format: int64, minimum: 0}
        partitionFilterUsed: {type: boolean}
        cacheHit: {type: boolean}
        error: {type: string, enum: [SQL statement denied, Query execution failed]}
        createdAt: {type: string, format: date-time}
    QueryLogList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items:
          type: array
          items: {$ref: "#/components/schemas/QueryLog"}
        nextCursor: {type: string}
    QueryLogEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/QueryLog"}
    QueryLogListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/QueryLogList"}
    WorkflowCreate:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        name: {type: string, minLength: 1, maxLength: 255}
        description: {type: string}
        scheduleType: {type: string, enum: [manual, cron, once]}
        cronExpression: {type: string, maxLength: 100}
        isEnabled: {type: boolean}
        executionMode: {type: string, enum: [linear, dag]}
        kind: {type: string, enum: [etl, agentic, mixed]}
        folderId: {type: string, format: uuid}
        runAsPrincipalKind: {type: string, enum: [user, service]}
        runAsPrincipalId: {type: string, format: uuid}
    WorkflowUpdate:
      type: object
      additionalProperties: false
      properties:
        name: {type: string, minLength: 1, maxLength: 255}
        description: {type: string}
        scheduleType: {type: string, enum: [manual, cron, once]}
        cronExpression: {type: string, maxLength: 100}
        isEnabled: {type: boolean}
        executionMode: {type: string, enum: [linear, dag]}
        status: {type: string, maxLength: 20}
        folderId: {type: string, format: uuid}
        runAsPrincipalKind: {type: [string, "null"], enum: [user, service, null]}
        runAsPrincipalId: {type: [string, "null"], format: uuid}
    WorkflowFolderMove:
      type: object
      additionalProperties: false
      required: [folderId]
      properties:
        folderId: {type: [string, "null"], format: uuid}
    WorkflowStepCreate:
      type: object
      additionalProperties: false
      required: [stepType, stepName]
      properties:
        stepOrder: {type: integer, minimum: 0}
        stepType: {type: string, minLength: 1}
        stepName: {type: string, minLength: 1, maxLength: 255}
        stepAlias: {type: string}
        connectorId: {type: string, format: uuid}
        promptTemplateId: {type: string, format: uuid}
        promptUseCase: {type: string}
        config: {type: object, additionalProperties: true}
        uiPosition: {type: object, additionalProperties: true}
        inputStepIds: {type: array, items: {type: string, format: uuid}}
    WorkflowStepUpdate:
      type: object
      additionalProperties: false
      properties:
        stepOrder: {type: integer, minimum: 0}
        stepType: {type: string, minLength: 1}
        stepName: {type: string, minLength: 1, maxLength: 255}
        stepAlias: {type: string}
        connectorId: {type: string, format: uuid}
        promptTemplateId: {type: string, format: uuid}
        promptUseCase: {type: string}
        config: {type: object, additionalProperties: true}
        uiPosition: {type: object, additionalProperties: true}
        inputStepIds: {type: array, items: {type: string, format: uuid}}
    WorkflowWithStepsCreate:
      type: object
      additionalProperties: false
      required: [workflow, steps]
      properties:
        workflow: {$ref: "#/components/schemas/WorkflowCreate"}
        steps: {type: array, items: {$ref: "#/components/schemas/WorkflowStepCreate"}}
    WorkflowStepsReplace:
      type: object
      additionalProperties: false
      required: [steps]
      properties:
        steps: {type: array, items: {$ref: "#/components/schemas/WorkflowStepCreate"}}
    WorkflowStep:
      type: object
      additionalProperties: false
      required: [id, stepOrder, stepType, stepName, config, inputStepIds]
      properties:
        id: {type: string, format: uuid}
        stepOrder: {type: integer, minimum: 0}
        stepType: {type: string}
        stepName: {type: string}
        stepAlias: {type: string}
        connectorId: {type: string, format: uuid}
        config: {type: object, additionalProperties: true}
        uiPosition: {type: object, additionalProperties: true}
        inputStepIds: {type: array, items: {type: string, format: uuid}}
        promptTemplateId: {type: string, format: uuid}
        promptUseCase: {type: string}
    Workflow:
      type: object
      additionalProperties: false
      required: [id, kind, name, slug, scheduleType, isEnabled, executionMode, status, steps, requiredUseCases, effectiveActions]
      properties:
        id: {type: string, format: uuid}
        folderId: {type: string, format: uuid}
        kind: {type: string, enum: [etl, agentic, mixed]}
        name: {type: string}
        slug: {type: string}
        description: {type: string}
        scheduleType: {type: string, enum: [manual, cron, once]}
        cronExpression: {type: string}
        isEnabled: {type: boolean}
        executionMode: {type: string, enum: [linear, dag]}
        runAsPrincipalKind: {type: string, enum: [user, service]}
        runAsPrincipalId: {type: string, format: uuid}
        status: {type: string}
        steps: {type: array, items: {$ref: "#/components/schemas/WorkflowStep"}}
        requiredUseCases: {type: array, items: {type: string}}
        effectiveActions: {type: array, items: {type: string, enum: [read, write, run, admin]}}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    WorkflowList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/Workflow"}}
        nextCursor: {type: string}
    WorkflowEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/Workflow"}
    WorkflowListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowList"}
    WorkflowStepListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/WorkflowStep"}}
    WorkflowRun:
      type: object
      additionalProperties: false
      required: [id, workflowId, status, totalItemsProcessed, costUsd, triggeredBy]
      properties:
        id: {type: string, format: uuid}
        workflowId: {type: string, format: uuid}
        status: {type: string, enum: [pending, running, success, failed, cancelled, paused, waiting_checkpoint]}
        startedAt: {type: string, format: date-time}
        finishedAt: {type: string, format: date-time}
        pausedAt: {type: string, format: date-time}
        durationMs: {type: integer, format: int64}
        totalItemsProcessed: {type: integer, format: int64}
        costUsd: {type: number, format: double}
        errorMessage: {type: string, description: Sanitized failure summary; internal errors are never exposed.}
        triggeredBy: {type: string, enum: [scheduler, manual, api, webhook, retry]}
        runPrincipalKind: {type: string}
        runPrincipalSource: {type: string}
        scheduledFor: {type: string, format: date-time}
        retriedFromRunId: {type: string, format: uuid}
        createdAt: {type: string, format: date-time}
    WorkflowRunEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowRun"}
    WorkflowRunList:
      type: object
      additionalProperties: false
      required: [items, page, limit, total]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/WorkflowRun"}}
        page: {type: integer, minimum: 1}
        limit: {type: integer, minimum: 1, maximum: 100}
        total: {type: integer, format: int64, minimum: 0}
    WorkflowRunListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowRunList"}
    WorkflowRunResume:
      type: object
      additionalProperties: false
      properties:
        formData: {type: object, additionalProperties: true}
    WorkflowRunStep:
      type: object
      additionalProperties: false
      required: [id, runId, stepId, status, progressPercent]
      properties:
        id: {type: string, format: uuid}
        runId: {type: string, format: uuid}
        stepId: {type: string, format: uuid}
        status: {type: string}
        progressPercent: {type: integer, minimum: 0, maximum: 100}
        startedAt: {type: string, format: date-time}
        finishedAt: {type: string, format: date-time}
        durationMs: {type: integer, format: int64}
        inputCount: {type: integer, format: int64}
        outputCount: {type: integer, format: int64}
        errorMessage: {type: string, description: Sanitized failure summary; internal errors are never exposed.}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    WorkflowRunStepListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/WorkflowRunStep"}}
    WorkflowRunEvent:
      type: object
      additionalProperties: false
      required: [id, runId, eventType, createdAt]
      properties:
        id: {type: string, format: uuid}
        runId: {type: string, format: uuid}
        stepId: {type: string, format: uuid}
        eventType: {type: string}
        createdAt: {type: string, format: date-time}
    WorkflowRunEventEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowRunEvent"}
    WorkflowRunEventList:
      type: object
      additionalProperties: false
      required: [items, page, limit]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/WorkflowRunEvent"}}
        page: {type: integer, minimum: 1}
        limit: {type: integer, minimum: 1, maximum: 100}
    WorkflowRunEventListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowRunEventList"}
    WorkflowStats:
      type: object
      additionalProperties: false
      required: [runs24h]
      properties:
        runs24h: {type: integer, format: int64, minimum: 0}
        avgCostUsd: {type: [number, "null"], format: double}
        p95DurationMs: {type: [integer, "null"], format: int64}
        avgApprovalMs: {type: [number, "null"], format: double}
    WorkflowStatsEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowStats"}
    WorkflowWebhookCreate:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        name: {type: string, minLength: 1, maxLength: 120}
        enabled: {type: boolean, default: true}
        payloadLimitBytes: {type: integer, minimum: 1024, maximum: 1048576, default: 65536}
    WorkflowWebhookUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        name: {type: string, minLength: 1, maxLength: 120}
        enabled: {type: boolean}
        payloadLimitBytes: {type: integer, minimum: 1024, maximum: 1048576}
    WorkflowWebhook:
      type: object
      additionalProperties: false
      required: [id, name, publicId, ingressPath, enabled, secretPrefix, secretRotatedAt, payloadLimitBytes, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        name: {type: string}
        publicId: {type: string}
        ingressPath: {type: string, description: Secret-authenticated public ingress path; it is not an API-key S2S route.}
        enabled: {type: boolean}
        secretPrefix: {type: string}
        secretRotatedAt: {type: string, format: date-time}
        payloadLimitBytes: {type: integer}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    WorkflowWebhookWithSecret:
      allOf:
        - {$ref: "#/components/schemas/WorkflowWebhook"}
        - type: object
          required: [secret]
          properties:
            secret: {type: string, description: Returned only by create or rotate; store it immediately.}
    WorkflowWebhookEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowWebhook"}
    WorkflowWebhookSecretEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowWebhookWithSecret"}
    WorkflowWebhookListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/WorkflowWebhook"}}
    KGEntity:
      type: object
      additionalProperties: false
      required: [id, typeId, kind, name, properties, graphScope, sourceOrphaned, validFrom, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        typeId: {type: string, format: uuid}
        kind: {type: string}
        name: {type: string}
        description: {type: string}
        properties: {type: object, additionalProperties: true}
        graphScope: {type: string, enum: [enterprise]}
        sourceOrphaned: {type: boolean}
        validFrom: {type: string, format: date-time}
        validUntil: {type: string, format: date-time}
        confidence: {type: number, minimum: 0, maximum: 1}
        degreeBucket: {type: string, enum: [low, medium, high]}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    KGEntityEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGEntity"}
    KGEntityList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/KGEntity"}}
        nextCursor: {type: string}
    KGEntityListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGEntityList"}
    CreateKGEntityRequest:
      type: object
      additionalProperties: false
      required: [kind, name]
      properties:
        kind: {type: string, minLength: 1, maxLength: 50}
        name: {type: string, minLength: 1, maxLength: 500}
        description: {type: string, maxLength: 5000}
    UpdateKGEntityRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        kind: {type: string, minLength: 1, maxLength: 50}
        name: {type: string, minLength: 1, maxLength: 500}
        description: {type: string, maxLength: 5000}
    KGRelation:
      type: object
      additionalProperties: false
      required: [id, headEntityId, tailEntityId, typeId, relationKind, weight, validFrom, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        headEntityId: {type: string, format: uuid}
        tailEntityId: {type: string, format: uuid}
        typeId: {type: string, format: uuid}
        relationKind: {type: string}
        weight: {type: number, minimum: 0}
        confidence: {type: number, minimum: 0, maximum: 1}
        validFrom: {type: string, format: date-time}
        validUntil: {type: string, format: date-time}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    KGRelationEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGRelation"}
    KGRelationList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/KGRelation"}}
        nextCursor: {type: string}
    KGRelationListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGRelationList"}
    CreateKGRelationRequest:
      type: object
      additionalProperties: false
      required: [headEntityId, tailEntityId, relationKind]
      properties:
        headEntityId: {type: string, format: uuid}
        tailEntityId: {type: string, format: uuid}
        relationKind: {type: string, minLength: 1, maxLength: 100}
        weight: {type: number, minimum: 0}
        confidence: {type: number, minimum: 0, maximum: 1}
        metadata: {description: Arbitrary JSON stored with the manual relation; never returned by Partner reads.}
    KGEntityType:
      type: object
      additionalProperties: false
      required: [id, slug, label, isSystem, usageCount, isInterface, sensitivityLevel, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        slug: {type: string}
        label: {type: string}
        description: {type: string}
        isSystem: {type: boolean}
        usageCount: {type: integer, format: int64, minimum: 0}
        parentId: {type: string, format: uuid}
        propertiesSchema: {type: object, additionalProperties: true}
        isInterface: {type: boolean}
        sensitivityLevel: {type: string, enum: [low, medium, high, critical]}
        defaultOwnerGroupId: {type: string, format: uuid}
        deprecatedAt: {type: string, format: date-time}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    KGEntityTypeListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/KGEntityType"}}
    KGEntityTypeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGEntityType"}
    CreateKGEntityTypeRequest:
      type: object
      additionalProperties: false
      required: [slug, label]
      properties:
        slug: {type: string, pattern: '^[a-z][a-z0-9_]{2,49}$'}
        label: {type: string, minLength: 1, maxLength: 100}
        description: {type: string, maxLength: 2000}
        parentId: {type: string, format: uuid}
        propertiesSchema: {type: object, additionalProperties: true}
        isInterface: {type: boolean}
        sensitivityLevel: {type: string, enum: [low, medium, high, critical]}
        defaultOwnerGroupId: {type: string, format: uuid}
    UpdateKGEntityTypeRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        label: {type: string, minLength: 1, maxLength: 100}
        description: {type: string, maxLength: 2000}
        parentId: {type: [string, "null"], format: uuid}
        propertiesSchema: {type: object, additionalProperties: true}
        isInterface: {type: boolean}
        sensitivityLevel: {type: string, enum: [low, medium, high, critical]}
        defaultOwnerGroupId: {type: [string, "null"], format: uuid}
    DeleteKGEntityType:
      type: object
      additionalProperties: false
      required: [id, slug, orphanEntityCount]
      properties:
        id: {type: string, format: uuid}
        slug: {type: string}
        orphanEntityCount: {type: integer, format: int64, minimum: 0}
    DeleteKGEntityTypeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/DeleteKGEntityType"}
    KGRelationType:
      type: object
      additionalProperties: false
      required: [id, slug, label, isSystem, cardinalityMin, isReifiable, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        slug: {type: string}
        label: {type: string}
        description: {type: string}
        isSystem: {type: boolean}
        parentId: {type: string, format: uuid}
        propertiesSchema: {type: object, additionalProperties: true}
        sourceTypeId: {type: string, format: uuid}
        targetTypeId: {type: string, format: uuid}
        cardinalityMin: {type: integer, minimum: 0}
        cardinalityMax: {type: integer, minimum: 0}
        isReifiable: {type: boolean}
        deprecatedAt: {type: string, format: date-time}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    KGRelationTypeListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/KGRelationType"}}
    KGRelationTypeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGRelationType"}
    CreateKGRelationTypeRequest:
      type: object
      additionalProperties: false
      required: [slug, label]
      properties:
        slug: {type: string, pattern: '^[a-z][a-z0-9_]{2,99}$'}
        label: {type: string, minLength: 1, maxLength: 100}
        description: {type: string, maxLength: 2000}
        parentId: {type: string, format: uuid}
        propertiesSchema: {type: object, additionalProperties: true}
        sourceTypeId: {type: string, format: uuid}
        targetTypeId: {type: string, format: uuid}
        cardinalityMin: {type: integer, minimum: 0}
        cardinalityMax: {type: integer, minimum: 0}
        isReifiable: {type: boolean}
    UpdateKGRelationTypeRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        label: {type: string, minLength: 1, maxLength: 100}
        description: {type: string, maxLength: 2000}
        parentId: {type: [string, "null"], format: uuid}
        propertiesSchema: {type: object, additionalProperties: true}
        sourceTypeId: {type: [string, "null"], format: uuid}
        targetTypeId: {type: [string, "null"], format: uuid}
        cardinalityMin: {type: integer, minimum: 0}
        cardinalityMax: {type: [integer, "null"], minimum: 0}
        isReifiable: {type: boolean}
    DeleteKGRelationType:
      type: object
      additionalProperties: false
      required: [id, slug]
      properties:
        id: {type: string, format: uuid}
        slug: {type: string}
    DeleteKGRelationTypeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/DeleteKGRelationType"}
    KGReplayRequest:
      type: object
      additionalProperties: false
      required: [knowledgeId]
      properties:
        knowledgeId: {type: string, format: uuid}
        kgProfile: {type: string, enum: [enterprise_core, technical_docs, structured_schema, candidates]}
        promptTemplateId: {type: string, format: uuid}
        modelId: {type: string, format: uuid}
    KGReplay:
      type: object
      additionalProperties: false
      required: [extractRequestId, runId, status]
      properties:
        extractRequestId: {type: string, format: uuid}
        runId: {type: string, format: uuid}
        status: {type: string, enum: [enqueued]}
        deduplicated: {type: boolean}
    KGReplayEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGReplay"}
    KGDedupCandidate:
      type: object
      additionalProperties: false
      required: [id, entityAId, entityAName, entityBId, entityBName, similarity, method, status, autoRecommended, createdAt]
      properties:
        id: {type: string, format: uuid}
        entityAId: {type: string, format: uuid}
        entityAName: {type: string, minLength: 1}
        entityBId: {type: string, format: uuid}
        entityBName: {type: string, minLength: 1}
        similarity: {type: number, format: float}
        method: {type: string, enum: [embedding, exact_name, fuzzy]}
        status: {type: string, enum: [pending, dismissed, expired]}
        autoRecommended: {type: boolean}
        decisionReason: {type: string}
        blockedReason: {type: string}
        reviewedAt: {type: string, format: date-time}
        expiresAt: {type: string, format: date-time}
        createdAt: {type: string, format: date-time}
    KGDedupCandidateList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/KGDedupCandidate"}}
        nextCursor: {type: string}
    KGDedupCandidateListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGDedupCandidateList"}
    KGDedupApproveRequest:
      type: object
      additionalProperties: false
      required: [survivorId]
      properties:
        survivorId: {type: string, format: uuid}
        reason: {type: string, description: Maximum 1000 UTF-8 bytes.}
    KGDedupApprove:
      type: object
      additionalProperties: false
      required: [survivorId, absorbedId, mergeRecordId]
      properties:
        survivorId: {type: string, format: uuid}
        absorbedId: {type: string, format: uuid}
        mergeRecordId: {type: string, format: uuid}
    KGDedupApproveEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGDedupApprove"}
    KGDedupDismiss:
      type: object
      additionalProperties: false
      required: [dismissed]
      properties:
        dismissed: {type: boolean, const: true}
    KGDedupDismissEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGDedupDismiss"}
    KGMergeHistory:
      type: object
      additionalProperties: false
      required: [id, survivorEntityId, absorbedEntityId, absorbedEntityName, performedAt]
      properties:
        id: {type: string, format: uuid}
        survivorEntityId: {type: string, format: uuid}
        survivorEntityName: {type: string}
        absorbedEntityId: {type: string, format: uuid}
        absorbedEntityName: {type: string, minLength: 1}
        reason: {type: string, description: Maximum 1000 UTF-8 bytes.}
        performedAt: {type: string, format: date-time}
    KGMergeHistoryList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/KGMergeHistory"}}
        nextCursor: {type: string}
    KGMergeHistoryListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGMergeHistoryList"}
    KGMergeRollback:
      type: object
      additionalProperties: false
      required: [mergeRecordId, absorbedId, restoredRelationCount, restoredSourceCount]
      properties:
        mergeRecordId: {type: string, format: uuid}
        absorbedId: {type: string, format: uuid}
        restoredRelationCount: {type: integer, minimum: 0}
        restoredSourceCount: {type: integer, minimum: 0}
    KGMergeRollbackEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/KGMergeRollback"}
    WorkflowCheckpointApprove:
      type: object
      additionalProperties: false
      properties:
        formData: {type: object, additionalProperties: true}
        comment: {type: string, description: Maximum 1000 UTF-8 bytes.}
    WorkflowCheckpointReject:
      type: object
      additionalProperties: false
      properties:
        comment: {type: string, description: Maximum 1000 UTF-8 bytes.}
    WorkflowCheckpointCancel:
      type: object
      additionalProperties: false
      properties:
        reason: {type: string, description: Maximum 500 UTF-8 bytes.}
    WorkflowCheckpointListItem:
      type: object
      additionalProperties: false
      required: [id, runId, stepId, workflowId, status, approvalQuorum, approvedCount, rejectedCount, createdAt]
      properties:
        id: {type: string, format: uuid}
        runId: {type: string, format: uuid}
        stepId: {type: string, format: uuid}
        workflowId: {type: string, format: uuid}
        workflowName: {type: string}
        stepName: {type: string}
        stepAlias: {type: string}
        status: {type: string, enum: [pending, approved, rejected, expired, cancelled]}
        approvalQuorum: {type: integer, minimum: 1}
        approvedCount: {type: integer, minimum: 0}
        rejectedCount: {type: integer, minimum: 0}
        expiresAt: {type: string, format: date-time}
        createdAt: {type: string, format: date-time}
        requestedByUser: {$ref: "#/components/schemas/UserSearchItem"}
    WorkflowCheckpointApprover:
      type: object
      additionalProperties: false
      required: [principalKind, principalId, isRequired]
      properties:
        principalKind: {type: string, enum: [user, role, group]}
        principalId: {type: string, format: uuid}
        isRequired: {type: boolean}
        decision: {type: string, enum: [approved, rejected]}
        decidedAt: {type: string, format: date-time}
        comment: {type: string}
        approverUser: {$ref: "#/components/schemas/UserSearchItem"}
    WorkflowCheckpointDecision:
      type: object
      additionalProperties: false
      required: [outcome]
      properties:
        outcome: {type: string, enum: [approved, rejected]}
        formData: {type: object, additionalProperties: true}
        comment: {type: string}
    WorkflowCheckpoint:
      allOf:
        - {$ref: "#/components/schemas/WorkflowCheckpointListItem"}
        - type: object
          required: [formSchema, isFormLocked, approvers]
          properties:
            formSchema: {type: object, additionalProperties: true}
            formData: {type: object, additionalProperties: true}
            decision: {$ref: "#/components/schemas/WorkflowCheckpointDecision"}
            isFormLocked: {type: boolean}
            myDecision: {type: string, enum: [approved, rejected]}
            approvers: {type: array, items: {$ref: "#/components/schemas/WorkflowCheckpointApprover"}}
            cancelledAt: {type: string, format: date-time}
            cancellationReason: {type: string}
    WorkflowCheckpointPaging:
      type: object
      additionalProperties: false
      required: [page, limit, total]
      properties:
        page: {type: integer, minimum: 1}
        limit: {type: integer, minimum: 1, maximum: 100}
        total: {type: integer, format: int64, minimum: 0}
    WorkflowCheckpointEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowCheckpoint"}
    WorkflowCheckpointListEnvelope:
      type: object
      required: [success, data, paging]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/WorkflowCheckpointListItem"}}
        paging: {$ref: "#/components/schemas/WorkflowCheckpointPaging"}
    WorkflowCheckpointRunListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/WorkflowCheckpointListItem"}}
    WorkflowNodePort:
      type: object
      required: [name, dataType, description]
      properties:
        name: {type: string}
        dataType: {type: string}
        description: {type: string}
    WorkflowNodeRequirement:
      type: object
      required: [kind, selectorPath, actions]
      properties:
        kind: {type: string}
        selectorPath: {type: string}
        actions: {type: array, items: {type: string}}
        tableRefStrategy: {type: string}
        policy: {type: string}
        optional: {type: boolean}
    WorkflowNode:
      type: object
      required: [type, displayName, description, category, icon, schemaVersion, configSchema, configUiSchema, inputOutput, resourceRequirements, riskClass, features, capabilities, executable, runtimeStatus]
      properties:
        type: {type: string}
        displayName: {type: string}
        description: {type: string}
        category: {type: string}
        icon: {type: string}
        schemaVersion: {type: string}
        configSchema: {type: object, additionalProperties: true}
        configUiSchema: {type: object, additionalProperties: true}
        inputOutput:
          type: object
          required: [inputs, outputs]
          properties:
            inputs: {type: array, items: {$ref: "#/components/schemas/WorkflowNodePort"}}
            outputs: {type: array, items: {$ref: "#/components/schemas/WorkflowNodePort"}}
        graphInputPolicy: {type: string}
        resourceRequirements: {type: array, items: {$ref: "#/components/schemas/WorkflowNodeRequirement"}}
        riskClass: {type: string}
        features: {type: array, items: {type: string}}
        capabilities: {type: array, items: {type: string}}
        executable: {type: boolean}
        runtimeStatus: {type: string}
        authoringNotes: {type: array, items: {type: string}}
    WorkflowNodeCatalog:
      type: object
      required: [catalogVersion, nodes]
      properties:
        catalogVersion: {type: string}
        nodes: {type: array, items: {$ref: "#/components/schemas/WorkflowNode"}}
    WorkflowNodeCatalogEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowNodeCatalog"}
    WorkflowNodeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowNode"}
    WorkflowReferenceItem:
      type: object
      additionalProperties: false
      required: [kind, id, state]
      properties:
        kind: {type: string}
        id: {type: string}
        label: {type: string}
        subtitle: {type: string}
        state: {type: string}
        access: {type: string}
    WorkflowReferenceOptions:
      type: object
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/WorkflowReferenceItem"}}
    WorkflowReferenceOptionsEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowReferenceOptions"}
    WorkflowReferenceResolveRef:
      type: object
      additionalProperties: false
      required: [kind, ids]
      properties:
        kind: {type: string}
        action: {type: string, default: read}
        nodeType: {type: string}
        fieldKey: {type: string}
        ids: {type: array, items: {type: string}}
    WorkflowReferenceResolveRequest:
      type: object
      additionalProperties: false
      required: [refs]
      properties:
        refs: {type: array, items: {$ref: "#/components/schemas/WorkflowReferenceResolveRef"}}
    WorkflowReferenceResolveResult:
      type: object
      required: [items, missing]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/WorkflowReferenceItem"}}
        missing: {type: array, items: {$ref: "#/components/schemas/WorkflowReferenceItem"}}
    WorkflowReferenceResolveEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/WorkflowReferenceResolveResult"}
    Profile:
      type: object
      required: [id, locale, status, createdAt, updatedAt]
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        username:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        avatarUrl:
          type: string
          format: uri
        locale:
          type: string
          enum: [vi, en]
        status:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    ProfileUpdate:
      type: object
      additionalProperties: false
      properties:
        firstName:
          type: string
          maxLength: 100
        lastName:
          type: string
          maxLength: 100
        avatarUrl:
          type: string
          format: uri
        locale:
          type: string
          enum: [vi, en]
    ProfileEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/Profile"
    UserSearchItem:
      type: object
      required: [id]
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        username:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        avatarUrl:
          type: string
          format: uri
    UserSearch:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/UserSearchItem"
    UserSearchEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/UserSearch"
    PermissionGroup:
      type: object
      required:
        - id
        - name
        - groupType
        - autoAssignNewMembers
        - inheritsFromParent
        - isDefaultPrimary
        - createdBy
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        groupType:
          type: string
          enum: [access_group, department]
        parentId:
          type: string
          format: uuid
        budgetMonthlyUsd:
          type: number
          format: double
        headUserId:
          type: string
          format: uuid
        autoAssignNewMembers:
          type: boolean
        inheritsFromParent:
          type: boolean
        isDefaultPrimary:
          type: boolean
        status:
          type: string
        createdBy:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PermissionGroupList:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/PermissionGroup"
        nextCursor:
          type: [string, "null"]
    PermissionGroupListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/PermissionGroupList"
    PermissionGroupEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/PermissionGroup"
    PermissionGroupCreate:
      type: object
      additionalProperties: false
      required: [name]
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        description:
          type: string
        groupType:
          type: string
          enum: [access_group, department]
          default: access_group
        parentId:
          type: string
          format: uuid
        budgetMonthlyUsd:
          type: number
          format: double
        headUserId:
          type: string
          format: uuid
    PermissionGroupUpdate:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        description:
          type: string
        status:
          type: string
        autoAssignNewMembers:
          type: boolean
        isDefaultPrimary:
          type: boolean
        inheritsFromParent:
          type: boolean
        parentId:
          type: [string, "null"]
          format: uuid
        budgetMonthlyUsd:
          type: [number, "null"]
          format: double
        headUserId:
          type: [string, "null"]
          format: uuid
    PermissionAssignment:
      type: object
      required: [id, userId, permissionGroupId, isPrimary]
      properties:
        id:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
        permissionGroupId:
          type: string
          format: uuid
        isPrimary:
          type: boolean
        roleInGroup:
          type: string
        startedAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        user:
          $ref: "#/components/schemas/UserSearchItem"
        permissionGroup:
          $ref: "#/components/schemas/PermissionGroup"
    PermissionAssignmentList:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/PermissionAssignment"
        nextCursor:
          type: [string, "null"]
    PermissionAssignmentListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/PermissionAssignmentList"
    AssignPermissionGroupRequest:
      type: object
      additionalProperties: false
      required: [permissionGroupId]
      properties:
        permissionGroupId:
          type: string
          format: uuid
    ReassignPrimaryDepartmentRequest:
      type: object
      additionalProperties: false
      required: [departmentId]
      properties:
        departmentId:
          type: string
          format: uuid
    TablePermissionConnector:
      type: object
      required: [id, name, slug]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        subtype:
          type: string
    TablePermission:
      type: object
      required: [id, connectorId, tablePattern, isDenied, isInheritable, canSelect, canInsert, canUpdate, canDelete, canCreate, canDrop, canAlter]
      properties:
        id:
          type: string
          format: uuid
        connectorId:
          type: string
          format: uuid
        databaseName:
          type: string
          maxLength: 255
        schemaName:
          type: string
          maxLength: 100
        tablePattern:
          type: string
          minLength: 1
          maxLength: 255
        isDenied:
          type: boolean
        isInheritable:
          type: boolean
        canSelect:
          type: boolean
        canInsert:
          type: boolean
        canUpdate:
          type: boolean
        canDelete:
          type: boolean
        canCreate:
          type: boolean
        canDrop:
          type: boolean
        canAlter:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        connector:
          $ref: "#/components/schemas/TablePermissionConnector"
    TablePermissionList:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/TablePermission"
        nextCursor:
          type: [string, "null"]
    TablePermissionListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/TablePermissionList"
    TablePermissionCreate:
      type: object
      additionalProperties: false
      required: [connectorId, tablePattern]
      properties:
        connectorId:
          type: string
          format: uuid
        databaseName:
          type: string
          maxLength: 255
        schemaName:
          type: string
          maxLength: 100
        tablePattern:
          type: string
          minLength: 1
          maxLength: 255
        isDenied:
          type: boolean
          default: false
        isInheritable:
          type: boolean
          default: true
        canSelect:
          type: boolean
          default: true
        canInsert:
          type: boolean
          default: false
        canUpdate:
          type: boolean
          default: false
        canDelete:
          type: boolean
          default: false
        canCreate:
          type: boolean
          default: false
        canDrop:
          type: boolean
          default: false
        canAlter:
          type: boolean
          default: false
    TablePermissionUpdate:
      type: object
      additionalProperties: false
      properties:
        databaseName:
          type: [string, "null"]
          maxLength: 255
        schemaName:
          type: [string, "null"]
          maxLength: 100
        tablePattern:
          type: string
          minLength: 1
          maxLength: 255
        isDenied:
          type: boolean
        isInheritable:
          type: boolean
        canSelect:
          type: boolean
        canInsert:
          type: boolean
        canUpdate:
          type: boolean
        canDelete:
          type: boolean
        canCreate:
          type: boolean
        canDrop:
          type: boolean
        canAlter:
          type: boolean
    ID:
      type: object
      required: [id]
      properties:
        id:
          type: string
          format: uuid
    IDEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/ID"
    Organization:
      type: object
      required: [id, name, slug, defaultLocale, status, enabledFeatures]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        slug:
          type: string
        logoUrl:
          type: [string, "null"]
        effectiveLogoUrl:
          type: [string, "null"]
        description:
          type: [string, "null"]
        type:
          type: [string, "null"]
        employeeSize:
          type: [string, "null"]
        defaultLocale:
          type: string
          enum: [en, vi]
        status:
          type: string
        enabledFeatures:
          type: array
          items:
            type: string
        createdAt:
          type: [string, "null"]
          format: date-time
        updatedAt:
          type: [string, "null"]
          format: date-time
    OrganizationEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/Organization"
    MemberUser:
      type: object
      required: [id]
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        username:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        avatarUrl:
          type: string
          format: uri
    PrimaryDepartment:
      type: object
      required: [id, name]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
    OrganizationMember:
      type: object
      required: [id, userId, role]
      properties:
        id:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
        role:
          type: string
          enum: [owner, admin, member]
        status:
          type: string
          enum: [active, invited]
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        user:
          $ref: "#/components/schemas/MemberUser"
        primaryDepartment:
          $ref: "#/components/schemas/PrimaryDepartment"
    MemberList:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/OrganizationMember"
        nextCursor:
          type: [string, "null"]
    MemberListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/MemberList"
    MemberInvitationRequest:
      type: object
      additionalProperties: false
      required: [email, role]
      properties:
        email:
          type: string
          format: email
        role:
          type: string
          enum: [admin, member]
        departmentId:
          type: string
          format: uuid
    MemberInvitation:
      type: object
      required: [userId, status, email, emailSent]
      properties:
        userId:
          type: string
          format: uuid
        status:
          type: string
        email:
          type: string
          format: email
        emailSent:
          type: boolean
    MemberInvitationEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/MemberInvitation"
    MemberRoleUpdateRequest:
      type: object
      additionalProperties: false
      required: [role]
      properties:
        role:
          type: string
          enum: [admin, member]
    MemberInvitationResend:
      type: object
      required: [expiresAt, emailSent]
      properties:
        expiresAt:
          type: string
          format: date-time
        emailSent:
          type: boolean
    MemberInvitationResendEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/MemberInvitationResend"
    OrganizationUpdate:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        description:
          type: string
        type:
          type: string
          enum: [personal, team, organization, enterprise]
        employeeSize:
          type: string
          enum: [only_me, 1-5, under_20, under_50, under_100, more_than_100]
        defaultLocale:
          type: string
          enum: [en, vi]
    AuditSettings:
      type: object
      required: [enabled, logDenials, logAdminBypassMutating, logAdminBypassPassive, logResourceReads, retentionDays]
      properties:
        enabled:
          type: boolean
        logDenials:
          type: boolean
        logAdminBypassMutating:
          type: boolean
        logAdminBypassPassive:
          type: boolean
        logResourceReads:
          type: boolean
        retentionDays:
          type: integer
          minimum: 30
          maximum: 3650
    AuditSettingsUpdate:
      type: object
      additionalProperties: false
      properties:
        enabled:
          type: boolean
        logDenials:
          type: boolean
        logAdminBypassMutating:
          type: boolean
        logAdminBypassPassive:
          type: boolean
        logResourceReads:
          type: boolean
        retentionDays:
          type: integer
          minimum: 30
          maximum: 3650
    AuditSettingsEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/AuditSettings"
    PermissionInheritanceSettings:
      type: object
      required: [enableDeptInheritance]
      properties:
        enableDeptInheritance:
          type: boolean
    PermissionInheritanceSettingsUpdate:
      type: object
      additionalProperties: false
      required: [enableDeptInheritance]
      properties:
        enableDeptInheritance:
          type: boolean
    PermissionInheritanceSettingsEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/PermissionInheritanceSettings"
    KGRuntimeSettings:
      type: object
      required: [fullViewCap, extractionRatePerHour, adminReplayLimitPerHour]
      properties:
        fullViewCap:
          type: integer
          minimum: 100
          maximum: 100000
        extractionRatePerHour:
          type: integer
          minimum: 1
          maximum: 10000
        adminReplayLimitPerHour:
          type: integer
          minimum: 1
          maximum: 1000
    KGRuntimeSettingsUpdate:
      type: object
      additionalProperties: false
      properties:
        fullViewCap:
          type: integer
          minimum: 100
          maximum: 100000
        extractionRatePerHour:
          type: integer
          minimum: 1
          maximum: 10000
        adminReplayLimitPerHour:
          type: integer
          minimum: 1
          maximum: 1000
    KGRuntimeSettingsEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/KGRuntimeSettings"
    ResourcePermissionMutation:
      type: object
      additionalProperties: false
      required: [resourceType, principalKind, actions]
      anyOf:
        - required: [resourceId]
        - required: [resourceIds]
      properties:
        resourceType:
          type: string
          enum: [folder, connector, knowledge, media_asset, workflow, kg_entity, kg_relation]
        resourceId:
          type: string
          format: uuid
        resourceIds:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: string
            format: uuid
        principalKind:
          type: string
          enum: [user, group, role]
        principalId:
          type: string
          format: uuid
        principalRole:
          type: string
          enum: [owner, admin, member]
        actions:
          type: array
          minItems: 1
          maxItems: 6
          uniqueItems: true
          items:
            type: string
            enum: [read, write, admin, run, run_as, approve_checkpoint]
    ResourcePermissionGroup:
      type: object
      required: [id, name, groupType]
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        groupType:
          type: string
          enum: [access_group, department]
    ResourcePermission:
      type: object
      required: [id, principalKind, action, grantedAt, source, sourceResourceType, sourceResourceId, editable]
      properties:
        id:
          type: string
          format: uuid
        principalKind:
          type: string
          enum: [user, group, role]
        principalId:
          type: string
          format: uuid
        principalRole:
          type: string
        principalUser:
          $ref: "#/components/schemas/MemberUser"
        principalGroup:
          $ref: "#/components/schemas/ResourcePermissionGroup"
        action:
          type: string
          enum: [read, write, admin, run, run_as, approve_checkpoint]
        grantedAt:
          type: string
          format: date-time
        source:
          type: string
          const: direct
        sourceResourceType:
          type: string
        sourceResourceId:
          type: string
          format: uuid
        sourceName:
          type: string
        editable:
          type: boolean
          const: true
    ResourcePermissionList:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/ResourcePermission"
        nextCursor:
          type: [string, "null"]
    ResourcePermissionListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/ResourcePermissionList"
    PermissionCheckResponse:
      type: object
      required: [allowed]
      properties:
        allowed:
          type: boolean
    ResourcePermissionAudit:
      type: object
      required: [id, actorUserId, actionKind, principalKind, action, timestamp]
      properties:
        id:
          type: string
          format: uuid
        actorUserId:
          type: string
          format: uuid
        actionKind:
          type: string
          enum: [grant, revoke]
        principalKind:
          type: string
          enum: [user, group, role]
        principalId:
          type: string
          format: uuid
        principalRole:
          type: string
        action:
          type: string
          enum: [read, write, admin, run, run_as, approve_checkpoint]
        timestamp:
          type: string
          format: date-time
    ResourcePermissionAuditList:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/ResourcePermissionAudit"
        nextCursor:
          type: [string, "null"]
    ResourcePermissionAuditListEnvelope:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: "#/components/schemas/ResourcePermissionAuditList"
    ErrorDetail:
      type: object
      required: [code, id, status, message]
      properties:
        code:
          type: integer
        id:
          type: string
        status:
          type: string
        message:
          type: string
    AIReadinessEntry:
      type: object
      additionalProperties: false
      required: [ready, reason]
      properties:
        ready: {type: boolean}
        reason:
          type: [string, "null"]
          enum: [no_provider, no_provider_credential, no_default, no_capable_model, unsupported_provider_type, null]
    AIReadiness:
      type: object
      additionalProperties: false
      required: [providerCount, modelCount, useCases]
      properties:
        providerCount: {type: integer, minimum: 0}
        modelCount: {type: integer, minimum: 0}
        useCases:
          type: object
          additionalProperties: {$ref: "#/components/schemas/AIReadinessEntry"}
    AIReadinessEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/AIReadiness"}
    AIProvider:
      type: object
      additionalProperties: false
      required: [id, name, displayName, providerType, apiBase, enabled, createdAt, updatedAt, modelCount]
      properties:
        id: {type: string, format: uuid}
        name: {type: string}
        displayName: {type: string}
        providerType: {type: string}
        apiBase: {type: string}
        enabled: {type: boolean}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
        lastVerifiedAt: {type: string, format: date-time}
        lastVerifyStatus: {type: string}
        modelCount: {type: integer, minimum: 0}
        lastSyncedAt: {type: string, format: date-time}
    AIProviderEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/AIProvider"}
    AIProviderListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/AIProvider"}}
    CreateAIProviderRequest:
      type: object
      additionalProperties: false
      required: [name, providerType]
      properties:
        name: {type: string, minLength: 1}
        displayName: {type: string}
        providerType: {type: string, minLength: 1}
        apiBase: {type: string}
        apiKey: {type: string, writeOnly: true}
        enabled: {type: boolean}
        settings: {type: object, writeOnly: true, additionalProperties: true}
        metadata: {type: object, writeOnly: true, additionalProperties: true}
    UpdateAIProviderRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        displayName: {type: string}
        providerType: {type: string, minLength: 1}
        apiBase: {type: string}
        apiKey: {type: string, writeOnly: true}
        enabled: {type: boolean}
        settings: {type: object, writeOnly: true, additionalProperties: true}
        metadata: {type: object, writeOnly: true, additionalProperties: true}
    AICapabilities:
      type: object
      additionalProperties: false
      properties:
        chat: {type: boolean}
        embed: {type: boolean}
        vision: {type: boolean}
        transcription: {type: boolean}
        toolUse: {type: boolean}
        reasoning: {type: boolean}
        imageGen: {type: boolean}
        videoGen: {type: boolean}
        tts: {type: boolean}
    AIPricingImageTier:
      type: object
      additionalProperties: false
      required: [tierKey, perImage]
      properties:
        tierKey: {type: string}
        perImage: {type: number, minimum: 0}
    AIPricing:
      type: object
      additionalProperties: false
      required: [kind]
      properties:
        kind: {type: string}
        currency: {type: string}
        inputPer1M: {type: number, minimum: 0}
        outputPer1M: {type: number, minimum: 0}
        cachedInputPer1M: {type: number, minimum: 0}
        cacheWritePer1M: {type: number, minimum: 0}
        reasoningPer1M: {type: number, minimum: 0}
        perSecond: {type: number, minimum: 0}
        perMillionChars: {type: number, minimum: 0}
        perMinute: {type: number, minimum: 0}
        monthlyFee: {type: number, minimum: 0}
        imageTiers: {type: array, items: {$ref: "#/components/schemas/AIPricingImageTier"}}
    AIModel:
      type: object
      additionalProperties: false
      required: [id, isSystem, modelId, displayName, capabilities, enabled, createdAt, updatedAt, source, adminOverrides]
      properties:
        id: {type: string, format: uuid}
        providerId: {type: string, format: uuid}
        isSystem: {type: boolean}
        modelId: {type: string}
        displayName: {type: string}
        capabilities: {$ref: "#/components/schemas/AICapabilities"}
        contextWindow: {type: integer, minimum: 0}
        maxOutputTokens: {type: integer, minimum: 0}
        embedDimension: {type: integer, minimum: 0}
        pricing: {$ref: "#/components/schemas/AIPricing"}
        enabled: {type: boolean}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
        source: {type: string}
        lastSyncedAt: {type: string, format: date-time}
        probeVerifiedAt: {type: string, format: date-time}
        adminOverrides: {type: object, additionalProperties: {type: boolean}}
    AIModelEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/AIModel"}
    AIModelListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/AIModel"}}
    CreateAIModelRequest:
      type: object
      additionalProperties: false
      required: [modelId, capabilities]
      properties:
        modelId: {type: string, minLength: 1}
        displayName: {type: string}
        capabilities: {$ref: "#/components/schemas/AICapabilities"}
        contextWindow: {type: integer, minimum: 0}
        maxOutputTokens: {type: integer, minimum: 0}
        embedDimension: {type: integer, minimum: 0}
        pricing: {$ref: "#/components/schemas/AIPricing"}
        enabled: {type: boolean}
        metadata: {type: object, writeOnly: true, additionalProperties: true}
    UpdateAIModelRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        displayName: {type: string}
        capabilities: {$ref: "#/components/schemas/AICapabilities"}
        contextWindow: {type: integer, minimum: 0}
        maxOutputTokens: {type: integer, minimum: 0}
        embedDimension: {type: integer, minimum: 0}
        pricing: {$ref: "#/components/schemas/AIPricing"}
        enabled: {type: boolean}
        metadata: {type: object, writeOnly: true, additionalProperties: true}
    AIVerify:
      type: object
      additionalProperties: false
      required: [status, latencyMs]
      properties:
        status: {type: string, enum: [ok, invalid_key, unreachable, unknown, verify_not_supported]}
        lastVerifiedAt: {type: string, format: date-time}
        latencyMs: {type: integer, minimum: 0}
        modelsSyncedCount: {type: integer, minimum: 0}
        sampleModelIds: {type: array, maxItems: 5, items: {type: string}}
    AIVerifyEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/AIVerify"}
    ProbeAIModelRequest:
      type: object
      additionalProperties: false
      required: [modelId, capabilitiesToTest]
      properties:
        modelId: {type: string, minLength: 1}
        capabilitiesToTest: {type: array, minItems: 1, uniqueItems: true, items: {type: string, enum: [chat, embed]}}
    AIProbe:
      type: object
      additionalProperties: false
      required: [verifiedCapabilities, probedAt]
      properties:
        verifiedCapabilities: {type: object, additionalProperties: {type: boolean}}
        embedDimension: {type: integer, minimum: 0}
        errors: {type: object, additionalProperties: {type: string, enum: [upstream_auth_failed, model_not_found, not_supported, cancelled, timeout, unknown]}}
        probedAt: {type: string, format: date-time}
    AIProbeEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/AIProbe"}
    AISync:
      type: object
      additionalProperties: false
      required: [modelsSyncedCount, softDisabledCount, sampleModelIds, lastSyncedAt]
      properties:
        modelsSyncedCount: {type: integer, minimum: 0}
        softDisabledCount: {type: integer, minimum: 0}
        sampleModelIds: {type: array, maxItems: 5, items: {type: string}}
        lastSyncedAt: {type: string, format: date-time}
    AISyncEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/AISync"}
    SetAIDefaultRequest:
      type: object
      additionalProperties: false
      required: [providerId, modelId]
      properties:
        providerId: {type: string, format: uuid}
        modelId: {type: string, format: uuid}
    AIDefault:
      type: object
      additionalProperties: false
      required: [useCase, provider, model]
      properties:
        useCase: {type: string}
        provider: {$ref: "#/components/schemas/AIProvider"}
        model: {$ref: "#/components/schemas/AIModel"}
    AIDefaultEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {$ref: "#/components/schemas/AIDefault"}
    AICatalogModel:
      type: object
      additionalProperties: false
      required: [modelId, displayName, capabilities]
      properties:
        modelId: {type: string}
        displayName: {type: string}
        capabilities: {$ref: "#/components/schemas/AICapabilities"}
        contextWindow: {type: integer, minimum: 0}
        maxOutputTokens: {type: integer, minimum: 0}
        embedDimension: {type: integer, minimum: 0}
        pricing: {$ref: "#/components/schemas/AIPricing"}
        lastVerified: {type: string, format: date-time}
    AICatalogListEnvelope:
      type: object
      required: [success, data]
      properties:
        success: {type: boolean, const: true}
        data: {type: array, items: {$ref: "#/components/schemas/AICatalogModel"}}
    Budget:
      type: object
      additionalProperties: false
      required: [monthlyCapUsd, alertThreshold, enabled]
      properties:
        monthlyCapUsd: {type: number, minimum: 0}
        alertThreshold: {type: number, minimum: 0, maximum: 1}
        enabled: {type: boolean}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    BudgetReplace:
      type: object
      additionalProperties: false
      required: [monthlyCapUsd, alertThreshold, enabled]
      properties:
        monthlyCapUsd: {type: number, minimum: 0}
        alertThreshold: {type: number, minimum: 0, maximum: 1}
        enabled: {type: boolean}
    Spend:
      type: object
      additionalProperties: false
      required: [scope, spendUsd]
      properties:
        scope: {type: string, enum: [all, chat, embedding, agent, knowledge, transcription]}
        spendUsd: {type: number, minimum: 0}
    UsageSummaryDelta:
      type: object
      required: [cost, calls, tokens, latency]
      properties:
        cost: {type: [number, "null"]}
        calls: {type: [number, "null"]}
        tokens: {type: [number, "null"]}
        latency: {type: [number, "null"]}
    UsageSummary:
      type: object
      additionalProperties: false
      required: [totalCostUsd, totalCalls, totalTokens, avgLatencyMsP95, prevPeriodDeltaPct]
      properties:
        totalCostUsd: {type: number, minimum: 0}
        totalCalls: {type: integer, format: int64, minimum: 0}
        totalTokens: {type: integer, format: int64, minimum: 0}
        avgLatencyMsP95: {type: [number, "null"]}
        prevPeriodDeltaPct: {$ref: "#/components/schemas/UsageSummaryDelta"}
    UsageTrendPoint:
      type: object
      additionalProperties: false
      required: [time, costUsd, byCallType]
      properties:
        time: {type: string, format: date-time}
        costUsd: {type: number, minimum: 0}
        byCallType: {type: object, additionalProperties: {type: number}}
    UsageByUseCase:
      type: object
      additionalProperties: false
      required: [useCase, costUsd, pct]
      properties:
        useCase: {type: string}
        costUsd: {type: number, minimum: 0}
        pct: {type: number, minimum: 0, maximum: 100}
    UsageTopModel:
      type: object
      additionalProperties: false
      required: [provider, model, costUsd, calls, rank]
      properties:
        provider: {type: string}
        model: {type: string}
        costUsd: {type: number, minimum: 0}
        calls: {type: integer, format: int64, minimum: 0}
        rank: {type: integer, minimum: 1}
    UsageLog:
      type: object
      additionalProperties: false
      required: [id, callType, source, providerName, modelName, tokensInput, tokensOutput, tokensCachedInput, tokensReasoning, tokensCacheWrite, costUsd, status, createdAt]
      properties:
        id: {type: string, format: uuid}
        callType: {type: string}
        source: {type: string, enum: [local, goclaw]}
        providerName: {type: string}
        modelName: {type: string}
        tokensInput: {type: integer, minimum: 0}
        tokensOutput: {type: integer, minimum: 0}
        tokensCachedInput: {type: integer, minimum: 0}
        tokensReasoning: {type: integer, minimum: 0}
        tokensCacheWrite: {type: integer, minimum: 0}
        costUsd: {type: number, minimum: 0}
        batchSize: {type: integer, minimum: 0}
        dimension: {type: integer, minimum: 0}
        taskType: {type: string}
        latencyMs: {type: integer, minimum: 0}
        status: {type: string, enum: [reserving, abandoned, success, failed, rate_limited, circuit_open, timeout]}
        triggeredByUserId: {type: string, format: uuid}
        sourceEntityType: {type: string}
        sourceEntityId: {type: string, format: uuid}
        createdAt: {type: string, format: date-time}
    ResourceUsageLog:
      type: object
      additionalProperties: false
      required: [id, callType, providerName, modelName, tokensInput, tokensOutput, tokensCachedInput, tokensReasoning, tokensCacheWrite, costUsd, status, createdAt]
      properties:
        id: {type: string, format: uuid}
        callType: {type: string}
        providerName: {type: string}
        modelName: {type: string}
        tokensInput: {type: integer, minimum: 0}
        tokensOutput: {type: integer, minimum: 0}
        tokensCachedInput: {type: integer, minimum: 0}
        tokensReasoning: {type: integer, minimum: 0}
        tokensCacheWrite: {type: integer, minimum: 0}
        costUsd: {type: number, minimum: 0}
        latencyMs: {type: integer, minimum: 0}
        status: {type: string, enum: [reserving, abandoned, success, failed, rate_limited, circuit_open, timeout]}
        createdAt: {type: string, format: date-time}
    BudgetEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/Budget"}}}
    SpendEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/Spend"}}}
    UsageSummaryEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/UsageSummary"}}}
    UsageTrendEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {type: array, items: {$ref: "#/components/schemas/UsageTrendPoint"}}}}
    UsageByUseCaseEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {type: array, items: {$ref: "#/components/schemas/UsageByUseCase"}}}}
    UsageTopModelsEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {type: array, items: {$ref: "#/components/schemas/UsageTopModel"}}}}
    UsageLogEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/UsageLog"}}}
    UsageLogListEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {type: object, required: [items], properties: {items: {type: array, items: {$ref: "#/components/schemas/UsageLog"}}, nextCursor: {type: string}}}}}
    ResourceUsageLogListEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {type: object, required: [items], properties: {items: {type: array, items: {$ref: "#/components/schemas/ResourceUsageLog"}}, nextCursor: {type: string}}}}}
    AuditLog:
      type: object
      additionalProperties: false
      required: [id, actionKey, resourceType, createdAt]
      description: Bounded audit projection. The bound organization is implicit in the API key, actor identity resolves through the member scopes, and audit metadata/payload snapshots are not part of this contract.
      properties:
        id: {type: string, format: uuid}
        actionKey: {type: string}
        resourceType: {type: string}
        resourceId: {type: string, format: uuid}
        actorUserId: {type: string, format: uuid}
        actorApiKeyId: {type: string, format: uuid}
        source: {type: string}
        createdAt: {type: string, format: date-time}
    AuditLogListEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {type: object, required: [items], properties: {items: {type: array, items: {$ref: "#/components/schemas/AuditLog"}}, nextCursor: {type: string}}}}}
    AuditSummary:
      type: object
      additionalProperties: false
      required: [total, errorCount, bySource, byHour, topActors]
      properties:
        total: {type: integer, format: int64}
        errorCount: {type: integer, format: int64}
        bySource: {type: array, items: {type: object, additionalProperties: false, required: [source, count], properties: {source: {type: string}, count: {type: integer, format: int64}}}}
        byHour: {type: array, items: {type: object, additionalProperties: false, required: [hour, count], properties: {hour: {type: string, format: date-time}, count: {type: integer, format: int64}}}}
        topActors: {type: array, items: {type: object, additionalProperties: false, required: [actorUserId, count], properties: {actorUserId: {type: string, format: uuid}, count: {type: integer, format: int64}}}}
    AuditSummaryEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/AuditSummary"}}}
    DashboardEntityCount:
      type: object
      additionalProperties: false
      required: [alive, deleted, snapshotDate]
      description: Alive and deleted counts as of snapshotDate. The date is the freshness marker for this aggregate — the figures come from a nightly snapshot, not a live scan.
      properties:
        alive: {type: integer, format: int64}
        deleted: {type: integer, format: int64}
        snapshotDate: {type: string, format: date-time}
    DashboardSummary:
      type: object
      additionalProperties: false
      required: [entityCounts, auditCount24h, costMtd, costMtdDays, runsLast7d, costLast7dUsd, pendingCheckpoints]
      description: Aggregate-only widget snapshot. An entity type absent from the latest snapshot is omitted rather than reported as zero, so "not snapshotted yet" stays distinguishable from "snapshotted as empty".
      properties:
        entityCounts:
          type: object
          additionalProperties: false
          properties:
            knowledge: {$ref: "#/components/schemas/DashboardEntityCount"}
            connector: {$ref: "#/components/schemas/DashboardEntityCount"}
            workflow: {$ref: "#/components/schemas/DashboardEntityCount"}
            mediaAsset: {$ref: "#/components/schemas/DashboardEntityCount"}
            kgEntity: {$ref: "#/components/schemas/DashboardEntityCount"}
            kgRelation: {$ref: "#/components/schemas/DashboardEntityCount"}
        auditCount24h: {type: integer, format: int64}
        costMtd: {type: number}
        costMtdDays: {type: integer}
        runsLast7d: {type: integer, format: int64}
        costLast7dUsd: {type: number}
        pendingCheckpoints: {type: integer, format: int64}
    DashboardSummaryEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/DashboardSummary"}}}
    DashboardTrend:
      type: object
      additionalProperties: false
      required: [range, metric, points]
      description: The resolved window and metric are echoed so a caller can confirm which series it received.
      properties:
        range: {type: string, enum: [7d, 30d, 90d]}
        metric: {type: string, enum: [entities, cost, runs]}
        points: {type: array, items: {type: object, additionalProperties: false, required: [day, value], properties: {day: {type: string, pattern: "^\\d{4}-\\d{2}-\\d{2}$"}, value: {type: number}}}}
    DashboardTrendEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/DashboardTrend"}}}
    DashboardDailyPoint:
      type: object
      additionalProperties: false
      required: [date, value]
      properties:
        date: {type: string, pattern: "^\\d{4}-\\d{2}-\\d{2}$"}
        value: {type: integer}
    DashboardStatBlock:
      type: object
      additionalProperties: false
      required: [total, daily]
      description: Every day in the requested window is present, missing days filled with zero, so the series length always equals days.
      properties:
        total: {type: integer}
        daily: {type: array, items: {$ref: "#/components/schemas/DashboardDailyPoint"}}
    DashboardMediaKGStats:
      type: object
      additionalProperties: false
      required: [bulkJobs, kgEntities, dlqFailures, pipelineSuccess]
      properties:
        bulkJobs: {$ref: "#/components/schemas/DashboardStatBlock"}
        kgEntities: {$ref: "#/components/schemas/DashboardStatBlock"}
        dlqFailures: {$ref: "#/components/schemas/DashboardStatBlock"}
        pipelineSuccess:
          type: object
          additionalProperties: false
          required: [rate, daily]
          properties:
            rate: {type: number, minimum: 0, maximum: 1, description: "Share of media processing runs that succeeded in the window; 0 when the window has no runs."}
            daily: {type: array, items: {$ref: "#/components/schemas/DashboardDailyPoint"}, description: "Per-day success rate as an integer percentage 0..100."}
    DashboardMediaKGStatsEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/DashboardMediaKGStats"}}}
    KGGraphNode:
      type: object
      additionalProperties: false
      required: [id, label, kind, graphScope, size, color, centrality]
      description: One graph node. On the summary read nodes are communities; on every other read they are entities.
      properties:
        id: {type: string, format: uuid}
        label: {type: string}
        kind: {type: string}
        graphScope: {type: string, enum: [enterprise, schema, provenance]}
        size: {type: number}
        color: {type: string}
        communityId: {type: string, format: uuid}
        centrality: {type: number}
        degreeBucket: {type: string}
        source:
          type: object
          additionalProperties: false
          required: [kind]
          description: Lightweight origin hint. The full source list lives on the entity detail read.
          properties:
            kind: {type: string}
            refId: {type: string, format: uuid}
    KGGraphEdge:
      type: object
      additionalProperties: false
      required: [id, source, target, weight]
      description: On the summary read source and target are community ids; everywhere else they are entity ids.
      properties:
        id: {type: string, format: uuid}
        source: {type: string, format: uuid}
        target: {type: string, format: uuid}
        label: {type: string}
        weight: {type: number}
        color: {type: string}
    KGGraph:
      type: object
      additionalProperties: false
      required: [nodes, edges, meta]
      description: One payload shape backs all four graph reads so a caller never branches on endpoint.
      properties:
        nodes: {type: array, items: {$ref: "#/components/schemas/KGGraphNode"}}
        edges: {type: array, items: {$ref: "#/components/schemas/KGGraphEdge"}}
        meta:
          type: object
          additionalProperties: false
          required: [totalEntities, rendered]
          description: Truncation hint so a caller can tell a complete answer from a capped one without a second round trip.
          properties:
            totalEntities: {type: integer, format: int64}
            rendered: {type: integer}
            truncated: {type: boolean}
            communityLabel: {type: string}
    KGGraphEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGGraph"}}}
    KGSourceEntityView:
      type: object
      additionalProperties: false
      required: [id, name, kind, typeId]
      properties:
        id: {type: string, format: uuid}
        name: {type: string}
        kind: {type: string}
        typeId: {type: string, format: uuid}
        confidence: {type: number}
        knowledgeId: {type: string, format: uuid}
        weight: {type: number}
        firstExtractedAt: {type: string, format: date-time}
    KGSourceEntityList:
      type: object
      additionalProperties: false
      required: [knowledgeId, items, count]
      description: No continuation parameter is exposed; widen the page with limit rather than paging.
      properties:
        knowledgeId: {type: string, format: uuid}
        items: {type: array, items: {$ref: "#/components/schemas/KGSourceEntityView"}}
        count: {type: integer}
    KGSourceEntityListEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGSourceEntityList"}}}
    KGEntityBatchReadRequest:
      type: object
      additionalProperties: false
      required: [ids]
      properties:
        ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          description: Entity ids to resolve. Duplicates are rejected so the returned count always corresponds to the list sent.
          items: {type: string, format: uuid}
    KGEntityBatchRead:
      type: object
      additionalProperties: false
      required: [items, count]
      description: Carries no per-id status by design; an id the caller cannot read is simply absent.
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/KGSourceEntityView"}}
        count: {type: integer}
    KGEntityBatchReadEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGEntityBatchRead"}}}
    KGEntityDetail:
      type: object
      additionalProperties: false
      required: [entity, sources]
      properties:
        entity:
          type: object
          additionalProperties: false
          required: [id, name, kind, graphScope, extractionMethod, sourceOrphaned, sensitivityLevel, validFrom, createdAt, updatedAt]
          properties:
            id: {type: string, format: uuid}
            name: {type: string}
            kind: {type: string}
            graphScope: {type: string, enum: [enterprise, schema, provenance]}
            description: {type: string}
            extractionMethod: {type: string}
            sourceOrphaned: {type: boolean}
            orphanedAt: {type: string, format: date-time}
            communityId: {type: string, format: uuid}
            centralityRank: {type: integer}
            degreeBucket: {type: string}
            confidence: {type: number}
            sourceKind: {type: string}
            sourceRefId: {type: string, format: uuid}
            sensitivityLevel: {type: string}
            validFrom: {type: string, format: date-time}
            validUntil: {type: string, format: date-time}
            createdAt: {type: string, format: date-time}
            updatedAt: {type: string, format: date-time}
        sources:
          type: array
          description: Only the sources the bound organization can reach. Inaccessible ones are stripped, never listed with a denied marker.
          items:
            type: object
            additionalProperties: false
            required: [knowledgeId, knowledgeTitle, weight, firstExtractedAt]
            properties:
              knowledgeId: {type: string, format: uuid}
              knowledgeTitle: {type: string}
              weight: {type: number}
              firstExtractedAt: {type: string, format: date-time}
    KGEntityDetailEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGEntityDetail"}}}
    KGEntityProvenance:
      type: object
      additionalProperties: false
      required: [createdAt, updatedAt, extractionMethod, sourceCount]
      description: Lineage facts only — when the entity appeared, when it last changed, how it was produced and how many sources reference it.
      properties:
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
        extractionMethod: {type: string}
        sourceCount: {type: integer, format: int64}
    KGEntityProvenanceEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGEntityProvenance"}}}
    KGEntityAuditHistory:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items:
          type: array
          description: One entry per changed field per change. The acting identity is deliberately absent — the timeline answers what changed and when.
          items:
            type: object
            additionalProperties: false
            required: [id, action, field, createdAt]
            properties:
              id: {type: string, format: uuid}
              action: {type: string}
              field: {type: string}
              beforeValue: {type: string}
              afterValue: {type: string}
              createdAt: {type: string, format: date-time}
        nextCursor: {type: string}
    KGEntityAuditHistoryEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGEntityAuditHistory"}}}
    KGExtractionRun:
      type: object
      additionalProperties: false
      required: [id, knowledgeId, status, triggerKind, createdAt]
      description: >-
        Progress facts only. The provider and model that served the run and the raw failure text are pipeline
        diagnostics and are not published; errorClass is the stable slug to branch on.
      properties:
        id: {type: string, format: uuid}
        knowledgeId: {type: string, format: uuid}
        knowledgeTitle: {type: string}
        status: {type: string}
        triggerKind: {type: string}
        startedAt: {type: string, format: date-time}
        finishedAt: {type: string, format: date-time}
        entityCount: {type: integer}
        errorClass: {type: string}
        costUsd: {type: number}
        createdAt: {type: string, format: date-time}
    KGExtractionRunEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGExtractionRun"}}}
    KGExtractionRunList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/KGExtractionRun"}}
    KGExtractionRunListEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGExtractionRunList"}}}
    KGCostHalt:
      type: object
      additionalProperties: false
      required: [active, count, hours]
      properties:
        active: {type: boolean}
        count: {type: integer, format: int64}
        lastAt: {type: string, format: date-time}
        hours: {type: integer, description: "The window actually served, echoed so a caller can confirm it."}
    KGCostHaltEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGCostHalt"}}}
    KGAnalyzerStatus:
      type: object
      additionalProperties: false
      required: [status, totalEntities, pendingExtractions]
      properties:
        status: {type: string, enum: [idle, pending]}
        totalEntities: {type: integer, format: int64}
        pendingExtractions: {type: integer, format: int64}
        latestExtractionFinishedAt: {type: string, format: date-time}
        latestAnalyzeFinishedAt: {type: string, format: date-time}
    KGAnalyzerStatusEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGAnalyzerStatus"}}}
    KGAnalyzeAccepted:
      type: object
      additionalProperties: false
      required: [requestId, status]
      properties:
        requestId: {type: string, format: uuid, description: "Correlation handle for the submission; poll the analyzer status to follow the work."}
        status: {type: string}
    KGAnalyzeAcceptedEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGAnalyzeAccepted"}}}
    KGRowFactMapping:
      type: object
      additionalProperties: false
      required: [entityTypeSlug, stableKeyColumn, nameColumn]
      description: >-
        Columns are addressed by header name, so nothing here carries cell content. Supplying relationTypeSlug also
        requires targetEntityTypeSlug and targetStableKeyColumn; the generic relation types are not accepted.
      properties:
        entityTypeSlug: {type: string, minLength: 1, maxLength: 80}
        stableKeyColumn: {type: string, minLength: 1, maxLength: 255}
        nameColumn: {type: string, minLength: 1, maxLength: 255}
        descriptionColumns: {type: array, items: {type: string, maxLength: 255}, maxItems: 30}
        relationTypeSlug: {type: string, maxLength: 80}
        targetEntityTypeSlug: {type: string, maxLength: 80}
        targetStableKeyColumn: {type: string, maxLength: 255}
    KGRowFactGenerateRequest:
      type: object
      additionalProperties: false
      required: [mapping]
      description: >-
        Generation is always rule-based. Normalization by model is not built, so no flag selects it and none is accepted.
      properties:
        mapping: {$ref: "#/components/schemas/KGRowFactMapping"}
        limit: {type: integer, minimum: 1, maximum: 1000, description: "Rows to read. Absent reads the service default. A value above the maximum is refused, never clamped."}
    KGRowFactDecisionRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      description: >-
        At least one id across the two lists, and at most 500 across both together — the cap is on the combined
        request, not on each list. An id may appear once per request: repeating it, or claiming it in both lists, is
        refused rather than silently ordered.
      properties:
        acceptIds: {type: array, items: {type: string, format: uuid}, maxItems: 500, uniqueItems: true}
        rejectIds: {type: array, items: {type: string, format: uuid}, maxItems: 500, uniqueItems: true}
    KGRowFactPromoteRequest:
      type: object
      additionalProperties: false
      properties:
        proposalIds: {type: array, items: {type: string, format: uuid}, maxItems: 500, uniqueItems: true, description: "Absent or empty promotes every accepted proposal on the run."}
    KGRowFactSource:
      type: object
      additionalProperties: false
      required: [knowledgeId, title, format, sourceHash, totalRows, columns, supported, recommended]
      description: >-
        Column headers and counts only. Sample rows are customer data and are not published; read the proposals a run
        produces instead. supported is false when the source cannot be parsed into rows, and reason says why.
      properties:
        knowledgeId: {type: string, format: uuid}
        title: {type: string}
        format: {type: string}
        sourceHash: {type: string, description: "Digest of the parsed source. Changes when the Knowledge content changes."}
        totalRows: {type: integer}
        columns: {type: array, items: {type: string}}
        supported: {type: boolean}
        reason: {type: string}
        recommended: {$ref: "#/components/schemas/KGRowFactMappingRecommendation"}
    KGRowFactMappingRecommendation:
      type: object
      additionalProperties: false
      properties:
        stableKeyColumn: {type: string}
        nameColumn: {type: string}
    KGRowFactSourceEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGRowFactSource"}}}
    KGRowFactRun:
      type: object
      additionalProperties: false
      required: [id, knowledgeId, status, sourceHash, mapping, createdAt, updatedAt]
      description: >-
        The organization and the user who created the run are not published: the organization is implicit in the API key
        and the creator is actor identity. The mapping is echoed because the caller supplied it and a poller that lost
        its state needs it back to interpret the proposals.
      properties:
        id: {type: string, format: uuid}
        knowledgeId: {type: string, format: uuid}
        status: {type: string, enum: [draft, generated, partially_promoted, promoted, cancelled]}
        sourceHash: {type: string}
        mapping: {$ref: "#/components/schemas/KGRowFactMapping"}
        generatedAt: {type: string, format: date-time}
        promotedAt: {type: string, format: date-time}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    KGRowFactProposal:
      type: object
      additionalProperties: false
      required: [id, kind, status, rowNumber, stableKey, name, confidence, warnings]
      description: >-
        What the run would create, not what it read. name, stableKey and description describe the entity and become
        readable graph rows once promoted; the source record behind them — every column, including ones the mapping
        never selected — is not published.
      properties:
        id: {type: string, format: uuid}
        kind: {type: string, enum: [entity, relation]}
        status: {type: string, enum: [candidate, accepted, rejected, promoted, failed]}
        rowNumber: {type: integer, description: "1-based line in the source file, counting the header."}
        entityTypeSlug: {type: string}
        relationTypeSlug: {type: string}
        stableKey: {type: string}
        name: {type: string}
        description: {type: string}
        confidence: {type: number}
        warnings: {type: array, items: {type: string}}
        promotedEntityId: {type: string, format: uuid}
        promotedRelationId: {type: string, format: uuid}
    KGRowFactProposalSummary:
      type: object
      additionalProperties: false
      required: [total, candidate, accepted, rejected, promoted, failed]
      description: "Counts over the whole run, independent of the page being read."
      properties:
        total: {type: integer}
        candidate: {type: integer}
        accepted: {type: integer}
        rejected: {type: integer}
        promoted: {type: integer}
        failed: {type: integer}
    KGRowFactRunDetail:
      type: object
      additionalProperties: false
      required: [run, proposalSummary, proposals]
      properties:
        run: {$ref: "#/components/schemas/KGRowFactRun"}
        proposalSummary: {$ref: "#/components/schemas/KGRowFactProposalSummary"}
        proposals: {type: array, items: {$ref: "#/components/schemas/KGRowFactProposal"}}
        nextCursor: {type: string}
    KGRowFactRunDetailEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGRowFactRunDetail"}}}
    KGRowFactPromotion:
      type: object
      additionalProperties: false
      required: [runId, status, promotedEntities, promotedRelations, skipped]
      description: >-
        Counts and the resulting run status. The ids of what was created are read back through the proposals, which
        carry them per row.
      properties:
        runId: {type: string, format: uuid}
        status: {type: string, enum: [draft, generated, partially_promoted, promoted, cancelled]}
        promotedEntities: {type: integer}
        promotedRelations: {type: integer}
        skipped: {type: integer}
    KGRowFactPromotionEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGRowFactPromotion"}}}
    KGQualityReplayRequest:
      type: object
      additionalProperties: false
      description: >-
        Exactly one of the two forms. Setting both all and knowledgeIds is refused, and so is setting neither: the two
        have different failure modes and a request carrying both leaves no defensible reading of which one was meant.
      properties:
        all: {type: boolean, description: "Queue the whole backlog of Knowledge with no graph entity, up to maxItems."}
        knowledgeIds: {type: array, items: {type: string, format: uuid}, maxItems: 100, uniqueItems: true, description: "Filtered against live coverage; ids that already have graph entities drop out."}
        maxItems: {type: integer, minimum: 1, maximum: 100, description: "Ceiling on how much one call may queue. Absent uses the maximum. A value above it is refused, never clamped."}
    KGQualityDedupRequest:
      type: object
      additionalProperties: false
      properties:
        bucket: {type: string, enum: [cross_type_exact_name], description: "Absent means the only supported bucket."}
        limit: {type: integer, minimum: 1, maximum: 1000, description: "Pairs to inspect. Absent uses the service default of 200."}
    KGQualityGenerateReviewItemsRequest:
      type: object
      additionalProperties: false
      properties:
        issueKey: {type: string, enum: [noise.entity_candidates, graph.isolated_entities], description: "Absent generates from both reviewable buckets."}
        limit: {type: integer, minimum: 1, maximum: 100, description: "Samples per bucket to materialize. Absent uses the maximum."}
    KGQualityDeleteEntityRequest:
      type: object
      additionalProperties: false
      required: [deleteToken, confirmName]
      description: >-
        Both fields come from the impact read for this same item. deleteToken pins the deletion to the item and entity
        that preview described; confirmName must equal the entity's current name, so a preview read before a rename or
        replacement is refused rather than acted on.
      properties:
        deleteToken: {type: string, minLength: 1, maxLength: 128}
        confirmName: {type: string, minLength: 1, maxLength: 512}
    KGQualityReextractRequest:
      type: object
      additionalProperties: false
      properties:
        maxItems: {type: integer, minimum: 1, maximum: 100, description: "Ceiling on Knowledge sources queued. Absent uses the maximum."}
    KGQualityReport:
      type: object
      additionalProperties: false
      required: [coverage, extraction, graph, noise, dedup, governance]
      description: >-
        Counts only, over the bound organization. No entity names, document content or actor identities appear in this
        response.
      properties:
        coverage: {$ref: "#/components/schemas/KGQualityCoverage"}
        extraction: {$ref: "#/components/schemas/KGQualityExtraction"}
        graph: {$ref: "#/components/schemas/KGQualityGraph"}
        noise: {$ref: "#/components/schemas/KGQualityNoise"}
        dedup: {$ref: "#/components/schemas/KGQualityDedup"}
        governance: {$ref: "#/components/schemas/KGQualityGovernance"}
    KGQualityCoverage:
      type: object
      additionalProperties: false
      required: [activeKnowledgeCount, nonEmptyKnowledgeCount, knowledgeWithGraphSourceCount, nonEmptyKnowledgeMissingGraphCount, deletedKnowledgeWithGraphSourceCount]
      properties:
        activeKnowledgeCount: {type: integer}
        nonEmptyKnowledgeCount: {type: integer}
        knowledgeWithGraphSourceCount: {type: integer}
        nonEmptyKnowledgeMissingGraphCount: {type: integer, description: "What replay-missing-kb would queue."}
        deletedKnowledgeWithGraphSourceCount: {type: integer, description: "Graph entities still attributed to Knowledge that has been deleted."}
    KGQualityExtraction:
      type: object
      additionalProperties: false
      required: [runStatusCounts, deadLetterCount]
      properties:
        runStatusCounts: {type: array, items: {$ref: "#/components/schemas/KGQualityStatusCount"}}
        deadLetterCount: {type: integer, description: "Extractions that failed past retry. The dead-letter queue itself is not exposed on this contract."}
    KGQualityStatusCount:
      type: object
      additionalProperties: false
      required: [status, count]
      properties:
        status: {type: string}
        count: {type: integer}
    KGQualityGraph:
      type: object
      additionalProperties: false
      required: [entityCount, relationCount, isolatedEntityCount, averageDegree]
      properties:
        entityCount: {type: integer}
        relationCount: {type: integer}
        isolatedEntityCount: {type: integer, description: "Entities with no enterprise-scope edge in either direction."}
        averageDegree: {type: number}
    KGQualityNoise:
      type: object
      additionalProperties: false
      required: [filePathLikeEntityCount, testArtifactLikeEntityCount, emailLikeEntityCount, shortAmbiguousEntityCount, markdownHeadingLikeEntityCount]
      description: >-
        Name-shape signals for entities extraction probably should not have created. Each is a heuristic, not a verdict.
      properties:
        filePathLikeEntityCount: {type: integer}
        testArtifactLikeEntityCount: {type: integer}
        emailLikeEntityCount: {type: integer}
        shortAmbiguousEntityCount: {type: integer}
        markdownHeadingLikeEntityCount: {type: integer}
    KGQualityDedup:
      type: object
      additionalProperties: false
      required: [crossTypeDuplicateNameGroups, sameTypeDuplicateNameGroups]
      properties:
        crossTypeDuplicateNameGroups: {type: integer}
        sameTypeDuplicateNameGroups: {type: integer}
    KGQualityGovernance:
      type: object
      additionalProperties: false
      required: [entitiesMissingOwnerGroup, relationsMissingOwnerGroup, llmEntitiesMissingConfidence, llmRelationsMissingConfidence, sensitivityCounts]
      properties:
        entitiesMissingOwnerGroup: {type: integer}
        relationsMissingOwnerGroup: {type: integer}
        llmEntitiesMissingConfidence: {type: integer}
        llmRelationsMissingConfidence: {type: integer}
        sensitivityCounts: {type: array, items: {$ref: "#/components/schemas/KGQualitySensitivityCount"}}
    KGQualitySensitivityCount:
      type: object
      additionalProperties: false
      required: [sensitivityLevel, count]
      properties:
        sensitivityLevel: {type: string}
        count: {type: integer}
    KGQualityReportEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGQualityReport"}}}
    KGQualityIssues:
      type: object
      additionalProperties: false
      required: [buckets]
      properties:
        buckets: {type: array, items: {$ref: "#/components/schemas/KGQualityIssueBucket"}}
    KGQualityIssueBucket:
      type: object
      additionalProperties: false
      required: [key, title, severity, count, actionKind, actionRisk, reviewable, samples]
      description: >-
        count is over the whole bucket; samples is a bounded window into it and is never the full set. actionKind and
        actionRisk say what kind of work the bucket implies and how disruptive it is.
      properties:
        key: {type: string}
        title: {type: string}
        severity: {type: string, enum: [low, medium, high]}
        count: {type: integer}
        actionKind: {type: string}
        actionRisk: {type: string, enum: [low, medium, high]}
        reviewable: {type: boolean, description: "Whether generate-review-items accepts this bucket key."}
        samples: {type: array, items: {$ref: "#/components/schemas/KGQualityIssueSample"}}
    KGQualityIssueSample:
      type: object
      additionalProperties: false
      required: [id, resourceType, label]
      properties:
        id: {type: string}
        resourceType: {type: string, enum: [kg_entity, knowledge, dedup_group]}
        label: {type: string}
        subtitle: {type: string}
        reason: {type: string, description: "One of a fixed set of diagnostic phrases the detection query emits. Never document content."}
        entityIds: {type: array, items: {type: string, format: uuid}}
        knowledgeIds: {type: array, items: {type: string, format: uuid}}
    KGQualityIssuesEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGQualityIssues"}}}
    KGQualityReviewItem:
      type: object
      additionalProperties: false
      required: [id, issueKey, resourceType, resourceId, reasonCode, severity, status, isStale, isActionable, createdAt]
      description: >-
        isActionable is the one field the four action routes agree with: false means every action on this item answers
        409. isStale means the entity behind the item is gone or orphaned, which is what the list read retires on the
        next poll. The reviewing user is not published — reviewedAt says when, not who.
      properties:
        id: {type: string, format: uuid}
        issueKey: {type: string, enum: [noise.entity_candidates, graph.isolated_entities]}
        resourceType: {type: string, enum: [kg_entity]}
        resourceId: {type: string, format: uuid}
        resourceName: {type: string}
        resourceKind: {type: string, description: "Entity type slug."}
        reasonCode: {type: string}
        severity: {type: string, enum: [low, medium, high]}
        status: {type: string, enum: [pending, kept, dismissed, resolved, deleted]}
        isStale: {type: boolean}
        isActionable: {type: boolean}
        createdAt: {type: string, format: date-time}
        reviewedAt: {type: string, format: date-time}
    KGQualityReviewList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items: {type: array, items: {$ref: "#/components/schemas/KGQualityReviewItem"}}
        nextCursor: {type: string, description: "Opaque. Absent means the end of the queue."}
    KGQualityReviewListEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGQualityReviewList"}}}
    KGQualityImpact:
      type: object
      additionalProperties: false
      required: [itemId, entityId, entityName, sourceCount, relationCount, relationSample, deleteToken]
      description: >-
        sourceCount and relationCount are the full figures; relationSample is a bounded window over the relations, never
        the full set.
      properties:
        itemId: {type: string, format: uuid}
        entityId: {type: string, format: uuid}
        entityName: {type: string, description: "Pass this back as confirmName to delete the entity."}
        entityKind: {type: string}
        sourceCount: {type: integer, maximum: 100, description: "Live Knowledge documents this entity was extracted from. The underlying lookup is capped at 100, so exactly 100 means at least 100."}
        relationCount: {type: integer}
        relationSample: {type: array, items: {$ref: "#/components/schemas/KGQualityRelationSample"}}
        deleteToken: {type: string, description: "Pass back verbatim on delete-entity. Pins the deletion to this item and entity."}
    KGQualityRelationSample:
      type: object
      additionalProperties: false
      required: [id, otherId, otherName, direction]
      properties:
        id: {type: string, format: uuid}
        otherId: {type: string, format: uuid}
        otherName: {type: string}
        direction: {type: string, enum: [incoming, outgoing]}
        kind: {type: string, description: "Relation type slug."}
    KGQualityImpactEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGQualityImpact"}}}
    KGQualityReviewDecision:
      type: object
      additionalProperties: false
      required: [itemId, status]
      properties:
        itemId: {type: string, format: uuid}
        status: {type: string, enum: [kept, dismissed, deleted]}
    KGQualityReviewDecisionEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGQualityReviewDecision"}}}
    KGQualityCandidateResult:
      type: object
      additionalProperties: false
      required: [createdCount, skippedCount, pendingCount]
      description: >-
        skippedCount counts rows that already existed, which is what a repeated call reports. pendingCount is the whole
        queue after the call, not what this call added.
      properties:
        createdCount: {type: integer}
        skippedCount: {type: integer}
        pendingCount: {type: integer}
    KGQualityCandidateEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGQualityCandidateResult"}}}
    KGQualityBatch:
      type: object
      additionalProperties: false
      required: [batchId, status, totalCount, queuedCount, failedCount, items]
      description: >-
        Per-item outcomes are republished because a partially queued batch is the normal case — some Knowledge may have
        been deleted since the entity was extracted from it — and totals alone would not say which half ran. An item the
        batch refused carries no runId.
      properties:
        batchId: {type: string, format: uuid}
        status: {type: string}
        totalCount: {type: integer}
        queuedCount: {type: integer}
        failedCount: {type: integer}
        items: {type: array, items: {$ref: "#/components/schemas/KGQualityBatchItem"}}
    KGQualityBatchItem:
      type: object
      additionalProperties: false
      required: [knowledgeId, status]
      properties:
        knowledgeId: {type: string, format: uuid}
        runId: {type: string, format: uuid, description: "Poll it on /kg/extractions/runs/{runId}. Absent when the item was refused."}
        status: {type: string}
    KGQualityBatchEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/KGQualityBatch"}}}
    AIGovernancePolicy:
      type: object
      additionalProperties: false
      required: [id, defaultMode, externalCloudMode, embeddingMode, secretsAction, strictClasses, auditLevel, enabled, version, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        defaultMode: {type: string, enum: [allow, monitor, redact, block]}
        externalCloudMode: {type: string, enum: [allow, monitor, redact, block]}
        embeddingMode: {type: string, enum: [allow, monitor, redact, block]}
        secretsAction: {type: string, enum: [redact, block]}
        strictClasses: {type: array, items: {type: string}}
        auditLevel: {type: string, enum: [metadata, preview]}
        enabled: {type: boolean}
        version: {type: integer, format: int64}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    AIGovernanceAssignment:
      type: object
      additionalProperties: false
      required: [id, scopeKind, principalKind, dataClass, action, priority, inherits, enabled, version, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        scopeKind: {type: string, enum: [org, folder]}
        scopeId: {type: string, format: uuid}
        principalKind: {type: string, enum: [everyone, role, department, access_group, user]}
        principalId: {type: string, format: uuid}
        principalRole: {type: string}
        useCase: {type: string}
        dataClass: {type: string}
        action: {type: string, enum: [allow, monitor, redact, block]}
        priority: {type: integer}
        inherits: {type: boolean}
        enabled: {type: boolean}
        reason: {type: string}
        version: {type: integer, format: int64}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    AIGovernancePrivacy:
      type: object
      additionalProperties: false
      required: [id, providerType, trainingOptOut, subprocessors, createdAt, updatedAt]
      properties:
        id: {type: string, format: uuid}
        providerType: {type: string, minLength: 1, maxLength: 30}
        providerId: {type: string, format: uuid}
        dataRegion: {type: string}
        trainingOptOut: {type: boolean}
        retentionDays: {type: integer, minimum: 0}
        subprocessors: {type: array, items: {type: string}}
        notes: {type: string}
        createdAt: {type: string, format: date-time}
        updatedAt: {type: string, format: date-time}
    AIGovernanceEvent:
      type: object
      additionalProperties: false
      required: [id, policyVersion, decision, direction, surface, classes, createdAt]
      properties:
        id: {type: string, format: uuid}
        policyVersion: {type: integer, format: int64}
        decision: {type: string}
        direction: {type: string}
        surface: {type: string}
        useCase: {type: string}
        sourceEntityType: {type: string}
        sourceEntityId: {type: string, format: uuid}
        folderId: {type: string, format: uuid}
        principalKind: {type: string}
        principalId: {type: string, format: uuid}
        principalRole: {type: string}
        providerId: {type: string, format: uuid}
        providerType: {type: string}
        modelId: {type: string, format: uuid}
        classes: {type: array, items: {type: string}}
        redactedPreview: {type: string}
        createdAt: {type: string, format: date-time}
    AIGovernanceSummary:
      type: object
      required: [events24h, monitor24h, redact24h, blocked24h, detected24h]
      properties:
        events24h: {type: integer, format: int64}
        monitor24h: {type: integer, format: int64}
        redact24h: {type: integer, format: int64}
        blocked24h: {type: integer, format: int64}
        detected24h: {type: integer, format: int64}
    AIGovernanceOverview:
      type: object
      additionalProperties: false
      required: [policy, assignments, providerPrivacyProfiles, summary]
      properties:
        policy: {$ref: "#/components/schemas/AIGovernancePolicy"}
        assignments: {type: array, items: {$ref: "#/components/schemas/AIGovernanceAssignment"}}
        providerPrivacyProfiles: {type: array, items: {$ref: "#/components/schemas/AIGovernancePrivacy"}}
        summary: {$ref: "#/components/schemas/AIGovernanceSummary"}
    AIGovernancePolicyUpdate:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        defaultMode: {type: string, enum: [allow, monitor, redact, block]}
        externalCloudMode: {type: string, enum: [allow, monitor, redact, block]}
        embeddingMode: {type: string, enum: [allow, monitor, redact, block]}
        secretsAction: {type: string, enum: [redact, block]}
        strictClasses: {type: array, items: {type: string}}
        auditLevel: {type: string, enum: [metadata, preview]}
        enabled: {type: boolean}
    AIGovernanceAssignmentCreate:
      type: object
      additionalProperties: false
      required: [scopeKind, principalKind, action]
      properties:
        scopeKind: {type: string, enum: [org, folder]}
        scopeId: {type: string, format: uuid}
        principalKind: {type: string, enum: [everyone, role, department, access_group, user]}
        principalId: {type: string, format: uuid}
        principalRole: {type: string}
        useCase: {type: string}
        dataClass: {type: string}
        action: {type: string, enum: [allow, monitor, redact, block]}
        priority: {type: integer}
        inherits: {type: boolean}
        reason: {type: string}
    AIGovernancePrivacyUpsert:
      type: object
      additionalProperties: false
      required: [providerType, trainingOptOut]
      properties:
        id: {type: string, format: uuid}
        providerType: {type: string, minLength: 1, maxLength: 30}
        providerId: {type: string, format: uuid}
        dataRegion: {type: string}
        trainingOptOut: {type: boolean}
        retentionDays: {type: integer, minimum: 0}
        subprocessors: {type: array, items: {type: string}}
        notes: {type: string}
    AIGovernanceEffectivePreviewRequest:
      type: object
      additionalProperties: false
      properties:
        folderId: {type: string, format: uuid}
        userId: {type: string, format: uuid}
        role: {type: string}
        useCase: {type: string}
        dataClass: {type: string}
        surface: {type: string}
        externalCloud: {type: boolean}
    AIGovernanceEffectivePreview:
      type: object
      additionalProperties: false
      required: [action, policyVersion, matchedSource, matchedScope]
      properties:
        action: {type: string}
        policyVersion: {type: integer, format: int64}
        matchedSource: {type: string}
        matchedScope: {type: string}
        matchedId: {type: string, format: uuid}
        reason: {type: string}
    AIGovernanceOverviewEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/AIGovernanceOverview"}}}
    AIGovernancePolicyEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/AIGovernancePolicy"}}}
    AIGovernanceAssignmentEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/AIGovernanceAssignment"}}}
    AIGovernancePrivacyEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/AIGovernancePrivacy"}}}
    AIGovernanceEventListEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {type: array, items: {$ref: "#/components/schemas/AIGovernanceEvent"}}}}
    AIGovernanceEffectivePreviewEnvelope: {type: object, required: [success, data], properties: {success: {type: boolean, const: true}, data: {$ref: "#/components/schemas/AIGovernanceEffectivePreview"}}}
    ErrorEnvelope:
      type: object
      required: [success, error]
      properties:
        success:
          type: boolean
          const: false
        error:
          $ref: "#/components/schemas/ErrorDetail"
        message:
          type: string
  responses:
    BadRequest:
      description: The request is malformed or attempted to override the API key's bound organization
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Unauthorized:
      description: API key is missing, invalid, or revoked
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Forbidden:
      description: Key type, binding, role, organization state, or scope denied the request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    NotFound:
      description: The bound organization or requested resource does not exist
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Conflict:
      description: The current resource or request state does not allow the operation
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Gone:
      description: The invitation no longer exists in Builder Auth
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    TooManyRequests:
      description: The request exceeded an Agent Brain or upstream rate limit
      headers:
        Retry-After:
          description: Seconds until the applicable request budget may accept another operation
          schema: {type: integer, minimum: 1}
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    InternalServerError:
      description: Agent Brain could not complete the request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    BadGateway:
      description: Builder Auth returned an invalid verification response
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    ServiceUnavailable:
      description: Credential verification or organization binding verification is temporarily unavailable
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    PayloadTooLarge:
      description: Request body exceeds the endpoint limit
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    UnprocessableEntity:
      description: The request is valid but its Media state or configuration cannot be processed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    UnsupportedMediaType:
      description: Logo MIME type is not supported
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
