openapi: 3.0.0
info:
  version: '1'
  title: 'enneo.MIND API'
  description: 'This describes the API of enneo Mind, the main ticketing backend'
  contact:
    name: 'enneo GmbH'
    email: richard@enneo.ai
  license:
    name: 'Proprietary software'
    url: 'https://enneo.ai'
security:
  -
    bearerAuth:
      - api
  -
    cookieAuth:
      - api
servers:
  -
    url: 'https://demo.enneo.ai/api/mind'
    description: 'Production server, demo client'
  -
    url: 'https://main.enneo.dev/api/mind'
    description: 'Development main branch'
  -
    url: 'http://localhost:8005/api/mind'
    description: 'Local development server'
paths:
  /ticket/getLatest:
    post:
      tags:
        - Ticket
      summary: 'Get next ticket id to work on'
      operationId: getLatestTicket
      requestBody:
        description: 'Parameters for getting the next ticket'
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                preferredDirection:
                  type: string
                  enum:
                    - earliest
                    - previous
                    - next
                  default: earliest
                  description: 'Direction to navigate in the ticket queue'
                  example: earliest
                ticketId:
                  type: integer
                  nullable: true
                  description: "Current ticket ID (required when preferredDirection is 'previous' or 'next')"
                  example: 123
                filters:
                  type: array
                  description: 'An array of objects containing filters'
                  items:
                    $ref: '#/components/schemas/Filter'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  earliest:
                    type: integer
                    nullable: true
                    example: 2
                  previous:
                    type: integer
                    nullable: true
                    example: 1
                  next:
                    type: integer
                    nullable: true
                    example: 3
  /ticket/search:
    post:
      tags:
        - Ticket
      summary: 'Get a list of tickets'
      description: "Get a list of tickets in compact format for performance.\n\n⚠️ IMPORTANT: This endpoint returns a COMPACT version of tickets. The following fields are EXCLUDED from the response:\n- body, bodyPlain, bodyClean\n- attachments\n- template\n- workitem\n\nAlso note:\n- customer: Only includes {tagIds: [], tags: []} structure, NOT the full customer object\n- `ticketScope=skills` activates routing context: spam tickets (`spamStatus` in `auto_spam`, `auto_programmatic`, `manual_spam`) are excluded automatically. Backlog spam filter (both whitelist `in`): spam → those statuses; not spam → `null`, `auto_clean`, `manual_clean`.\n\nTo get the full ticket data including these fields, use GET /ticket/{id}."
      operationId: getTickets
      requestBody:
        description: 'Search parameters for tickets'
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                limit:
                  type: integer
                  description: 'The number of items to return'
                  default: 100
                  minimum: 1
                  maximum: 500
                  example: 100
                offset:
                  type: integer
                  description: 'The number of items to skip'
                  default: 0
                  minimum: 0
                  example: 0
                orderByField:
                  type: string
                  description: "The field to order by\nt = ticket\ni = intent\ntt = ticket_tag\n"
                  enum:
                    - t.id
                    - t.channel
                    - t.subchannelId
                    - t.channelId
                    - t.direction
                    - t.status
                    - t.priority
                    - t.agentId
                    - t.customerId
                    - t.contractId
                    - t.partnerId
                    - t.isCustomerActive
                    - t.aiSupportLevel
                    - i.aiAgentId
                    - t.createdAt
                    - t.modifiedAt
                    - t.firstResponseDueBy
                    - t.dueBy
                    - t.lastMessageAt
                    - t.lastCustomerMessageAt
                    - tt.tagId
                    - t.sentiment
                    - t.language
                    - t.languageCode
                    - t.from
                  default: t.id
                  example: t.id
                orderByDirection:
                  type: string
                  description: 'The direction to order by'
                  enum:
                    - asc
                    - desc
                  default: desc
                  example: asc
                ticketScope:
                  type: string
                  description: "Restrict the list to tickets matching the agent's configured skill tags.\n- all (default): no skill-based restriction — standard backlog search\n- skills: routing-scoped view; activates assignee-bypass so a restricted agent\n  still sees tickets assigned to them even when they carry routing-excluded tags.\n  Also activates NRR-tag stripping (non-routing-relevant tags are ignored for\n  skill-match purposes). Matches the behaviour of Report dashboard `ticketScope=skills`.\n"
                  enum:
                    - all
                    - skills
                  default: all
                  example: all
                filters:
                  type: array
                  description: 'An array of objects containing filters'
                  items:
                    $ref: '#/components/schemas/Filter'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  tickets:
                    type: array
                    items:
                      $ref: '#/components/schemas/Ticket'
                  total:
                    type: integer
                    description: 'Total number of tickets that match the filter'
                    example: 21
                  offset:
                    type: integer
                    description: 'Offset used'
                    example: 0
                  limit:
                    type: integer
                    description: 'Limit used'
                    example: 100
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
  /ticket:
    post:
      tags:
        - Ticket
      summary: 'Create a ticket'
      description: "Create a new ticket.\n\nRequired fields:\n- channel (only required field!)\n\nAuto-generated if not provided:\n- direction (default: 'in')\n- status (default: 'open')\n- priority (default: 'low')\n- interface (default: 'internal')\n- to, ccEmails, bccEmails (default: [])\n- createdAt (default: current timestamp)\n- aiSupportLevel (default: 'unprocessed')\n- id (auto-generated by database)\n\nAgent assignment:\n- If `agentId` is set in the request, it is used as-is.\n- Otherwise the ticket is auto-assigned to the calling profile only when the `lastAgentRouting` setting is enabled AND the caller is a human profile (`user`, `enneo`, or `enneoPartner`). Service workers, code executors, and system users never trigger auto-assignment.\n- When `lastAgentRouting` is disabled, the ticket stays unassigned regardless of direction."
      operationId: createTicket
      parameters:
        -
          name: process
          in: query
          schema:
            type: string
            enum:
              - realtime
              - batch
              - 'false'
            default: batch
          description: "realtime = immediately run the AI logic synchronously;\nbatch = process the AI asyncronously;\nfalse = not run pre-run AI agents. AI will be run on first user view;\n"
      requestBody:
        description: 'Ticket object to create'
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                from:
                  type: string
                  format: email
                  nullable: true
                  description: 'For new tickets, the email/id of the requester, typically the customer name'
                fromName:
                  type: string
                  nullable: true
                  description: "For new Tickets: The original requester, typically the customer name For outgoing emails: The name of the sender, typically the name of the mailbox (null value). If provided, the name is used, e.g. if an agent wants to send a personalized mail in his own name.\n"
                  example: null
                subchannelId:
                  type: integer
                  description: 'The ID of the subchannel (=mailbox) from which the ticket should be sent. The list of subchannels is available through the /settings/subchannel endpoint'
                  default: null
                  nullable: true
                  example: 2
                agentId:
                  type: integer
                  description: "Optional. Profile ID of the agent the ticket should be assigned to. If omitted, the ticket is auto-assigned to the calling profile only when the `lastAgentRouting` setting is enabled AND the caller is a human profile (`user`, `enneo`, `enneoPartner`). When the setting is disabled, the ticket stays unassigned.\n"
                  nullable: true
                  default: null
                  example: null
                to:
                  type: array
                  description: "For new tickets: Optional, to which mailbox this was sent to. For outgoing emails: The email of the customer that the email should be sent to\n"
                  items:
                    type: string
                    format: email
                    example: customer@web.de
                cc:
                  type: array
                  description: "For new tickets: Optional, to which mailbox this was sent to in cc. For outgoing emails: The cc emails that the email should be sent to\n"
                  items:
                    type: string
                    format: email
                    example: customer@web.de
                bcc:
                  type: array
                  description: "Only for outgoing emails: The bcc emails that the email should be sent to\n"
                  items:
                    type: string
                    format: email
                    example: customer@web.de
                priority:
                  $ref: '#/components/schemas/Priority'
                channel:
                  $ref: '#/components/schemas/Channel'
                status:
                  $ref: '#/components/schemas/Status'
                subject:
                  type: string
                  example: 'Ticket subject'
                body:
                  type: string
                  description: 'Can be either HTML (preferred if available) or plaintext'
                  example: '<p>Ticket body<p>'
                createdAt:
                  type: string
                  format: DateTime
                  example: '2021-01-01 00:00:00'
                firstResponseDueBy:
                  type: string
                  format: DateTime
                  example: '2021-01-02 00:00:00'
                dueBy:
                  type: string
                  format: DateTime
                  example: '2021-01-07 00:00:00'
                attachments:
                  type: array
                  items:
                    type: object
                    oneOf:
                      -
                        properties:
                          name:
                            type: string
                            example: myattachment1.png
                          url:
                            type: string
                            format: uri
                            example: 'https://my-server.com/my-image.png'
                        required:
                          - name
                          - url
                      -
                        properties:
                          name:
                            type: string
                            example: myattachment2.png
                          base64:
                            type: string
                            format: byte
                            example: base-64-encoded-string-of-attachment-file
                        required:
                          - base64
                tags:
                  type: array
                  items:
                    type: integer
                    example: 54
                contractId:
                  type: string
                  description: 'If available, a contract id that was already identified for this ticket. If not set, enneo will attempt to auto-detect the contract'
                  nullable: true
                  default: null
                  example: null
                externalTicketId:
                  type: string
                  description: 'If available, the unique identifier of a third-party ticketing system can be stored in enneo'
                  nullable: true
                  default: null
                  example: null
                rawData:
                  nullable: true
                  type: object
                  description: 'Any raw source data from the originating system in the proprietary format. Any data passed here is stored for later use via API or user defined functions.'
                  additionalProperties: true
                  example: null
                templateId:
                  type: integer
                  description: 'If available, a template id to be rendered into ticket body using data provided in templateData parameter.'
                  nullable: true
                  default: null
                  example: null
                templateData:
                  type: object
                  description: 'Data to be used while rendering template into ticket body.'
                  additionalProperties: true
                  nullable: true
                  default: null
                  example: null
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ticket'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
    patch:
      tags:
        - Ticket
      summary: 'Update a few tickets at once'
      description: "Update a few tickets at once.\n\n**Tags handling:**\n- Use `tagIds` to replace all tags\n- Use `addTagIds`/`removeTagIds` to modify existing tags\n- Cannot combine `tagIds` with `addTagIds`/`removeTagIds`\n\n**Special flags:**\n- `refreshMode`: Schedule a selective AI re-run (`full`, `summaryAndText`, `customer`, `tags`, `agents`). Skips saving and webhooks.\n- `refresh` (deprecated — use `refreshMode=full`): Skips saving and webhooks, used for refreshing data without changes.\n- `refreshTags` (deprecated — use `refreshMode=tags`): Performs only AI tag detection (faster), skips saving and webhooks.\n"
      operationId: updateTickets
      parameters:
        -
          name: includeInWorklog
          in: query
          required: false
          schema:
            type: boolean
            default: true
          description: 'Whether to include the ticket in time tracking, and thus worklog. Useful when a spam ticket should be closed, but it should not show up in the average handling time statistics'
      requestBody:
        required: true
        description: 'A JSON object containing changes'
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/Ticket'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}':
    get:
      tags:
        - Ticket
      summary: 'Get a ticket (full data)'
      description: "Get a single ticket by ticketId with FULL data.\n\nUnlike POST /ticket/search (which returns compact data), this endpoint includes:\n- body, bodyPlain, bodyClean (full content)\n- attachments (complete list)\n- intents (full details)\n- template (email template)\n- workitem (for system tickets only)\n- customer (full object with all properties)"
      operationId: getTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket to retrieve'
          schema:
            type: integer
          example: 376189
        -
          name: refresh
          in: query
          required: false
          description: 'Trigger a hard refresh (incl. re-sync of contract from ERP system) before?'
          schema:
            type: boolean
          example: false
        -
          name: erpCacheOnly
          in: query
          required: false
          description: 'If true, only use locally cached ERP data for contract/customer enrichment (no external ERP calls). Useful for bulk fetches where fresh ERP data is not needed.'
          schema:
            type: boolean
          example: false
        -
          name: includeSessions
          in: query
          required: false
          description: "Embed the ticket's NEO sessions as a `sessions` array (same shape as `GET /sessions?ticketId`), so the FE needs no second call. Defaults to true; pass `false` to skip (e.g. bulk fetches)."
          schema:
            type: boolean
            default: true
          example: true
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Ticket'
                  -
                    type: object
                    properties:
                      prevTicketId:
                        type: integer
                        example: 1
                      nextTicketId:
                        type: integer
                        example: 3
                      template:
                        type: string
                        example: "<p>Hallo Richard,</p>\n\n<p>%MESSAGE%</p>\n\nViele Grüße,\n<br>Dein enneo-Team"
                      workitem:
                        type: object
                        nullable: true
                        example: null
                        description: 'If a workitem (a.k.a. system dropout or action) is attached to this ticket, the source data is shown here'
                      sessions:
                        type: array
                        description: 'NEO sessions on this ticket (present by default; omitted with `?includeSessions=false`). Same shape as `GET /sessions?ticketId`.'
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                            ticketId:
                              type: integer
                              nullable: true
                            lastActivityAt:
                              type: string
                              format: date-time
                            createdAt:
                              type: string
                              format: date-time
                            participants:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                  name:
                                    type: string
                                    nullable: true
                            messageCount:
                              type: integer
                            costUsd:
                              type: number
                              nullable: true
                              description: 'Requires reportAiPerformance permission'
                            model:
                              type: string
                              nullable: true
                              description: 'Requires reportAiPerformance permission'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Ticket
      summary: 'Update a ticket'
      description: 'Update a ticket by ticketId'
      operationId: updateTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
          example: 376189
        -
          name: includeInWorklog
          in: query
          required: false
          schema:
            type: boolean
            default: true
          description: 'Whether to include the ticket in time tracking, and thus worklog. Useful when a spam ticket should be closed, but it should not show up in the average handling time statistics'
      requestBody:
        required: true
        description: 'A JSON object containing changes'
        content:
          application/json:
            schema:
              type: object
              properties:
                contractId:
                  type: string
                  description: 'The id of the contract to set'
                  example: '376189'
                  nullable: true
                status:
                  $ref: '#/components/schemas/Status'
                workedOnBy:
                  type: integer
                  description: 'If set to null, flag that the agent has stopped working on this ticket'
                  nullable: true
                  example: null
                priority:
                  $ref: '#/components/schemas/Priority'
                dueBy:
                  type: string
                  format: DateTime
                  description: 'The due date to set'
                  example: 1609502400
                  nullable: true
                firstResponseDueBy:
                  type: string
                  format: DateTime
                  description: 'The first response due date to set'
                  example: 1609502400
                  nullable: true
                tagIds:
                  type: array
                  description: 'An array of tag ids to set'
                  items:
                    type: integer
                    example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      ticket:
                        $ref: '#/components/schemas/Ticket'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Ticket
      summary: 'GDPR-clean ticket deletion'
      description: "Permanently deletes a ticket and **all** associated data: conversations,\nintents, tag links, queue entries, quality assessments, surveys, reminders,\nreporting rows, worklog, time-tracking, S3 attachments, and `files` rows.\n\nRequires the `deleteTicket` permission (admin / role 1 only).\nThis action is irreversible."
      operationId: deleteTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket to delete'
          schema:
            type: integer
          example: 376189
      responses:
        '200':
          description: 'Ticket deleted successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Unauthorized — requires deleteTicket permission'
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/attachment/{attachmentId}':
    delete:
      tags:
        - Ticket
      summary: 'GDPR-clean attachment deletion'
      description: "Permanently removes a single attachment from a ticket or its conversations:\ndeletes the S3 object, the `files` row, and strips the entry from the\nticket/conversation `attachments` JSON.\n\nThe attachment is identified by its numeric `id` field inside the JSON array.\nSearch covers both ticket-level and conversation-level attachments.\n\nRequires the `updateTechnicalTicketFields` permission (admin / role 1 only).\nThis action is irreversible."
      operationId: deleteTicketAttachment
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket that owns the attachment'
          schema:
            type: integer
          example: 376189
        -
          name: attachmentId
          in: path
          required: true
          description: 'The id of the attachment entry to delete (alphanumeric string, e.g. an 8-char hex hash)'
          schema:
            type: string
          example: 81ebabb9
      responses:
        '200':
          description: 'Attachment deleted successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Unauthorized — requires updateTechnicalTicketFields permission'
        '404':
          description: 'Ticket or attachment not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/variables':
    get:
      tags:
        - Ticket
      summary: 'Get ticket variables'
      description: 'Get ticket variables by ticketId'
      operationId: getTicketVariables
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
          example: 376189
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/history':
    get:
      tags:
        - Ticket
      summary: 'Get a tickets history'
      description: 'Get a history of created tickets and other relevant actions from a customer/contract'
      operationId: getTicketHistory
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
          example: 376189
        -
          $ref: '#/components/parameters/limitParam'
        -
          $ref: '#/components/parameters/offsetParam'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  total:
                    type: number
                    example: 11
                  totalOpenedTickets:
                    type: number
                    example: 2
                  history:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          nullable: true
                        type:
                          type: string
                          description: 'Type of interaction'
                          enum:
                            - ticket
                            - gridMessage
                            - delivery
                            - payment
                        channel:
                          type: string
                          description: 'The channel of the ticket. NULL if type is not ticket'
                          allOf:
                            -
                              $ref: '#/components/schemas/Channel'
                          nullable: true
                        subChannel:
                          type: string
                          description: 'Further detailing of the channel. Needed for to define icon.'
                          enum:
                            - facebook
                            - instagram
                            - whatsapp
                            - telegram
                            - companyWebsite
                            - gridOperator
                            - previousSupplier
                            - nextSupplier
                        status:
                          type: string
                          description: 'The status of the ticket. NULL if type is not ticket'
                          allOf:
                            -
                              $ref: '#/components/schemas/Status'
                          nullable: true
                        subject:
                          description: 'If subject matches a localization entry, then show it. E.g. deliveryStart=Belieferungsbeginn or missedCall=Rückrufversuch. Otherwise the FE shows the string directly.'
                          type: string
                          nullable: true
                        summary:
                          type: string
                          description: 'A short summary of the interaction. NULL if type is not ticket'
                          nullable: true
                        url:
                          type: string
                          description: 'Agent can get more information behind this url. NULL if there should be no hyperlink OR the frontend knows how to create a hyperlink by itself, as is the case with type=ticket'
                          format: uri
                          nullable: true
                        createdAt:
                          type: string
                          format: DateTime
                    example:
                      -
                        id: 1022
                        type: ticket
                        channel: email
                        subChannel: null
                        status: active
                        subject: 'Tom Mustermann'
                        url: null
                        createdAt: 1673039966
                      -
                        id: 222
                        type: ticket
                        channel: phone
                        subChannel: null
                        status: pending
                        subject: missedCall
                        url: null
                        createdAt: 1672953566
                      -
                        id: 135
                        type: ticket
                        channel: chat
                        subChannel: facebook
                        status: open
                        subject: null
                        url: 'https://facebook.com/message/289291'
                        createdAt: 1662153566
                      -
                        id: null
                        type: delivery
                        channel: null
                        subChannel: null
                        status: null
                        subject: deliveryStart
                        url: 'https://powercloud.de/gridMessages/41425548'
                        createdAt: 1660953600
                      -
                        id: null
                        type: gridMessage
                        channel: null
                        subChannel: gridOperator
                        status: null
                        subject: deliveryStartConfirmed
                        url: 'https://powercloud.de/gridMessages/41425548'
                        createdAt: 1660345921
                      -
                        id: null
                        type: gridMessage
                        channel: null
                        subChannel: previousSupplier
                        status: null
                        subject: handoverDateConfirmed
                        url: 'https://powercloud.de/gridMessages/41425548'
                        createdAt: 1657667521
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/forward':
    post:
      tags:
        - Ticket
      summary: 'Forward a ticket'
      description: 'Forward a ticket to free typed email address'
      operationId: forwardTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
          example: 376189
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body:
                  type: string
                  description: 'The body of the note'
                  example: 'Hello, I am forwarding this ticket to you. Please take care of it.'
                toEmail:
                  type: string
                  description: 'The email address to forward the ticket to'
                  example: grid@operator.de
                subject:
                  type: string
                  description: 'Custom subject for the forwarded email. If not provided, the original ticket subject will be used.'
                  example: 'Forwarded: Customer inquiry about contract'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/autoexecute':
    post:
      tags:
        - Ticket
      summary: 'Auto-execute a ticket'
      description: 'Automatically process a ticket that was previously marked as auto-executable. Optionally, specify an AI agent to force-execute even if the agent is not configured for auto-execution.'
      operationId: autoExecuteTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
          example: 376189
        -
          name: executeAgentId
          in: query
          required: false
          description: 'If set, only execute intents from this AI agent. Bypasses the auto-executable configuration check, allowing execution not just of rule-based, but also smart-reasoning based AI agents.'
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/ping':
    get:
      tags:
        - Ticket
      summary: 'Ping a ticket'
      description: "Ping a ticket to keep time tracking for agent and to refresh the list of\ncurrently-active agents on this ticket. The FE polls this every 30s and\nuses `workedOnBy` to overwrite its cached list, so the \"another agent\nis working on this ticket\" warning becomes two-sided (both the first and\nthe second agent see it)."
      operationId: pingTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
            example: 376189
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                    description: 'Operation was successful'
                  workedOnBy:
                    type: array
                    description: "Agents currently working on this ticket (live — only entries with\n`ttl > NOW()` in `ticket_agent`). Includes the pinging user. Same\nshape as `Ticket.workedOnBy` in `GET /ticket/{id}` — FE overwrites\nits cached list with this array."
                    items:
                      $ref: '#/components/schemas/AuthProfile'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/activity':
    get:
      tags:
        - Ticket
      summary: 'Get ticket activity'
      description: 'Get ticket activity by ticketId'
      operationId: getTicketActivity
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
            example: 376189
        -
          name: showTechnicalInformation
          in: query
          required: false
          description: 'If true, then the technical information will be shown'
          schema:
            type: boolean
            example: true
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    example: 1
                  status:
                    type: string
                    example: open
                  user:
                    $ref: '#/components/schemas/AuthProfile'
                  createdAt:
                    type: string
                    format: DateTime
                    example: '2021-01-01 00:00:00'
                  activity:
                    type: string
                    example: 'hat 2 Themen zugewiesen'
                  details:
                    type: array
                    items:
                      type: object
                      properties:
                        label:
                          type: string
                          example: 'Thema zugewiesen'
                        value:
                          type: string
                          example: Allgemein
                        tooltip:
                          type: string
                          example: Allgemein
                          nullable: true
                        url:
                          type: string
                          example: 'https://enneo.ai'
                          nullable: true
                        type:
                          type: string
                          example: string
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /ticket/stats:
    get:
      tags:
        - Ticket
      summary: 'Get ticket stats'
      description: 'Get ticket stats'
      operationId: getTicketStats
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  total:
                    type: number
                    example: 11
                  intents:
                    type: array
                    items:
                      type: object
                      properties:
                        total:
                          type: number
                          example: 3
                        id:
                          type: integer
                          example: 1
                        name:
                          type: string
                          example: 'Zählerstand verarbeiten'
                  groups:
                    type: array
                    items:
                      type: object
                      properties:
                        total:
                          type: number
                          example: 3
                        id:
                          type: integer
                          example: 1
                        name:
                          type: string
                          example: Billing
                  tags:
                    type: array
                    items:
                      type: object
                      properties:
                        total:
                          type: number
                          example: 3
                        id:
                          type: integer
                          example: 1
                        fullName:
                          type: string
                          example: powercloud
  /ticket/textUpdate:
    post:
      tags:
        - Ticket
      summary: 'Apply an AI-driven text transformation'
      description: "Runs an AI text-modification operation on the supplied text and returns the transformed result.\nUsed by ops-fe's compose box (rephrase, shorten, translate, …).\n"
      operationId: ticketTextUpdate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                text:
                  type: string
                  description: 'Source text to transform.'
                operation:
                  type: string
                  description: 'Transformation operation (e.g. `rephrase`, `shorten`, `translate`).'
                parameters:
                  type: object
                  description: 'Operation-specific parameters (e.g. target language for `translate`).'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  text:
                    type: string
                    description: 'Transformed text.'
        '400':
          description: 'Invalid input'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
  '/ticket/{ticketId}/conversation':
    get:
      tags:
        - Conversation
      summary: 'Get conversations for a ticket'
      description: 'Retrieve all conversations associated with a specific ticket'
      operationId: getConversations
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the ticket to get conversations for'
        -
          name: includeRawData
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: 'Whether to include raw data in the response'
        -
          name: type
          in: query
          required: false
          schema:
            oneOf:
              -
                type: string
              -
                type: array
                items:
                  type: string
          style: form
          explode: true
          description: 'Filter by conversation type (e.g. neo, text, html, note). Accepts a single value or an array. When absent, all types are returned.'
          examples:
            single:
              value: text
            multi:
              value:
                - text
                - html
        -
          name: direction
          in: query
          required: false
          schema:
            oneOf:
              -
                type: string
                enum:
                  - in
                  - out
                  - internal
              -
                type: array
                items:
                  type: string
                  enum:
                    - in
                    - out
                    - internal
          style: form
          explode: true
          description: 'Filter by conversation direction. Accepts a single value or an array. When absent, all directions are returned.'
          examples:
            single:
              value: out
            multi:
              value:
                - in
                - out
        -
          name: private
          in: query
          required: false
          schema:
            type: boolean
          description: 'Filter by private flag. When absent, both private and public conversations are returned.'
        -
          name: senderId
          in: query
          required: false
          schema:
            oneOf:
              -
                type: integer
              -
                type: array
                items:
                  type: integer
          style: form
          explode: true
          description: 'Filter by `sender.id` (the human operator/customer). Accepts a single value or an array.'
          examples:
            single:
              value: 1
            multi:
              value:
                - 1
                - 2
        -
          name: toId
          in: query
          required: false
          schema:
            oneOf:
              -
                type: integer
              -
                type: array
                items:
                  type: integer
          style: form
          explode: true
          description: 'Filter by the `id` of any entry in the `to` array (participant id; email/string entries carry no id and never match). Accepts a single value or an array.'
          examples:
            single:
              value: 1
            multi:
              value:
                - 1
                - 2
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  conversations:
                    type: array
                    items:
                      $ref: '#/components/schemas/Conversation'
                  success:
                    type: boolean
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/conversation/reply':
    post:
      tags:
        - Conversation
      summary: 'Reply to a ticket thread'
      description: 'Create a new conversation by replying to a ticket thread. This actually sends out an email (for email tickets), triggers sending of a letter (for letter tickets) or a message (for chat tickets). Can also create drafts that require supervisor approval before sending.'
      operationId: replyToTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the ticket to reply to'
        -
          name: includeInWorklog
          in: query
          required: false
          schema:
            type: boolean
            default: true
          description: 'Whether to include the reply in time tracking, and thus worklog. Useful when a spam ticket should be closed, but it should not show up in the average handling time statistics, e.g. for spam emails'
        -
          name: includeQuotedHistory
          in: query
          required: false
          schema:
            type: boolean
            default: true
          description: 'Whether to append the quoted history block to outgoing emails — the chat transcript ("Chat-Verlauf") for chat tickets, or the quoted previous message for email tickets. Set to false for automated template emails (e.g. welcome / mandate letters sent from a chat ticket) so the customer does not receive the whole conversation log. Defaults to true (unchanged behavior). May also be supplied in the request body.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: object
                  properties:
                    message:
                      type: string
                      description: 'The reply message content'
                  description: 'The content of the reply'
                type:
                  type: string
                  enum:
                    - text
                    - html
                  default: text
                  description: 'The type of content'
                direction:
                  type: string
                  enum:
                    - in
                    - out
                    - internal
                  default: out
                  description: 'The direction of the conversation'
                private:
                  type: boolean
                  description: 'Whether this is a private note'
                isDraft:
                  type: boolean
                  default: false
                  description: 'Whether this is a draft message that requires supervisor approval before sending'
                subject:
                  type: string
                  description: 'Optional subject for the reply'
                to:
                  type: array
                  items:
                    type: string
                  description: 'Recipients email addresses'
                cc:
                  type: array
                  items:
                    type: string
                  description: 'CC email addresses'
                bcc:
                  type: array
                  items:
                    type: string
                  description: 'BCC email addresses'
                intentIds:
                  type: array
                  items:
                    type: integer
                  description: 'Array of intent IDs'
                attachments:
                  type: array
                  items:
                    type: object
                  description: 'File attachments'
                channelId:
                  type: string
                  description: 'Channel ID for chat tickets'
                subchannelId:
                  type: integer
                  description: 'Subchannel ID'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  id:
                    type: integer
                    description: 'The ID of the created conversation'
                  conversation:
                    $ref: '#/components/schemas/Conversation'
                  surveyCode:
                    type: string
                    description: 'Survey code if user survey is needed'
                  webhookResponse:
                    type: string
                    description: 'Response from webhook execution'
        '400':
          description: 'Bad request - Invalid input'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/conversation/store':
    post:
      tags:
        - Conversation
      summary: 'Store a new conversation'
      description: 'Store a new conversation without sending any messages'
      operationId: storeConversation
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the ticket to store conversation for'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                process:
                  type: string
                  enum:
                    - realtime
                    - batch
                    - 'false'
                  default: batch
                  description: 'When to run AI logic for the conversation. Realtime=Run AI logic synchronously, even though this will take some time, Batch=Run AI logic after the conversation is created asynchronously, False=Do not run AI logic at all.'
                direction:
                  type: string
                  enum:
                    - in
                    - out
                    - internal
                  description: 'The direction of the conversation. Out=Outgoing to customer, In=Incoming from customer, Internal=Internal conversation, e.g. from an internal email address from another department.'
                from:
                  type: string
                  description: 'From email address'
                  example: john@smith.com
                fromName:
                  type: string
                  description: 'From name'
                  example: 'John Smith'
                to:
                  type: array
                  items:
                    type: string
                  description: 'To email addresses'
                  example:
                    - customer@web.de
                ccEmails:
                  type: array
                  items:
                    type: string
                  description: 'CC email addresses (can include names with email format like "Name <email@domain.com>")'
                  example:
                    - 'Michael Jackson <michael-test123@jack.s.on.com>'
                bccEmails:
                  type: array
                  items:
                    type: string
                  description: 'BCC email addresses'
                subject:
                  type: string
                  description: 'Email subject line'
                  example: 'Ticket subject'
                body:
                  type: string
                  description: 'HTML body content'
                  example: 'Ticket body PART 2'
                bodyPlain:
                  type: string
                  description: 'Optional plain text body content. If not set, the body content will be used, stripped of HTML tags.'
                bodyClean:
                  type: string
                  description: 'Optional clean body content. This is the body content without any HTML tags. If not set, the body content will be used, stripped of HTML tags.'
                private:
                  type: boolean
                  default: false
                  description: 'Whether this is a private conversation (internal note by an agent)'
                  example: false
                subchannelId:
                  type: integer
                  description: "Subchannel ID. Must correspond to a subchannel in the ticket's channel. A subchannel is e.g. a specific mailbox (for email tickets) or a specific chat channel (for chat tickets)"
                  example: 4
                status:
                  type: string
                  enum:
                    - open
                    - pending
                    - closed
                  description: 'Ticket status'
                  example: open
                createdAt:
                  type: string
                  description: 'Creation timestamp (YYYY-MM-DD HH:MM:SS format) when the conversation was created. If not set, the current timestamp will be used.'
                  example: '2024-03-01 00:00:00'
                firstResponseDueBy:
                  type: string
                  description: 'Optional first response due date (YYYY-MM-DD HH:MM:SS format). If set, the ticket will be updated with this first response due date.'
                  example: '2021-01-02 00:00:00'
                dueBy:
                  type: string
                  description: 'Optional due date (YYYY-MM-DD HH:MM:SS format). If set, the ticket will be updated with this due date.'
                  example: '2021-01-07 00:00:00'
                attachments:
                  type: array
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                        description: 'Attachment file name'
                        example: myattachment1.png
                      url:
                        type: string
                        description: 'URL to download the attachment from'
                        example: 'https://framerusercontent.com/images/n1fjKCgnztVE5RhOJBPPaCIRw.png'
                      base64:
                        type: string
                        description: 'Base64 encoded file content (alternative to url)'
                        example: base-64-encoded-string-of-attachment-file
                  description: 'File attachments (either url or base64 should be provided per attachment)'
                externalConversationId:
                  type: string
                  description: 'External conversation ID for tracking'
                  example: abc-123
                rawData:
                  type: object
                  description: 'Custom user-defined data object for additional metadata'
                  example:
                    myObjectId: 123
                type:
                  type: string
                  default: html
                  description: 'The type of conversation'
                sender:
                  type: object
                  description: 'Sender information'
                content:
                  type: object
                  description: 'Conversation content'
                intentIds:
                  type: array
                  items:
                    type: integer
                  description: 'Array of intent IDs'
                crmNotificationId:
                  type: string
                  description: 'Optional external CRM notification ID'
                contractId:
                  type: string
                  description: 'Contract ID'
                customerId:
                  type: string
                  description: 'Customer ID'
                agentId:
                  type: integer
                  description: 'Agent ID that authored the conversation'
                priority:
                  type: string
                  enum:
                    - low
                    - medium
                    - high
                    - urgent
                  description: 'Optional ticket priority. If set, the ticket will be updated with this priority.'
      responses:
        '201':
          description: 'Conversation created successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  conversationId:
                    type: integer
                  ticketId:
                    type: integer
                  conversation:
                    $ref: '#/components/schemas/Conversation'
                  eventId:
                    type: integer
        '400':
          description: 'Bad request - Invalid input'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/ticket/{ticketId}/conversation/{conversationId}':
    get:
      tags:
        - Conversation
      summary: 'Get a specific conversation by ID'
      description: 'Retrieve a specific conversation by its ID within a ticket'
      operationId: getConversationById
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the ticket'
        -
          name: conversationId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the conversation to retrieve'
        -
          name: includeRawData
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: 'Whether to include raw data in the response'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  conversation:
                    $ref: '#/components/schemas/Conversation'
                  success:
                    type: boolean
        '400':
          description: 'Bad request - Conversation does not belong to this ticket'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket or conversation not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Conversation
      summary: 'Update a conversation'
      description: 'Update an existing conversation (internal notes and drafts can be updated; drafts can also be published by changing isDraft to false)'
      operationId: updateConversation
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the ticket'
        -
          name: conversationId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the conversation to update'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: object
                  properties:
                    message:
                      type: string
                      description: 'Updated message content'
                  description: 'Updated content'
                type:
                  type: string
                  enum:
                    - text
                    - html
                  description: 'Content type'
                isDraft:
                  type: boolean
                  description: 'Whether this is a draft (set to false to publish a draft)'
                to:
                  type: array
                  items:
                    type: string
                  description: 'Recipients email addresses (for drafts)'
                cc:
                  type: array
                  items:
                    type: string
                  description: 'CC email addresses (for drafts)'
                bcc:
                  type: array
                  items:
                    type: string
                  description: 'BCC email addresses (for drafts)'
                intentIds:
                  type: array
                  items:
                    type: integer
                  description: 'Array of intent IDs (for drafts)'
                attachments:
                  type: array
                  items:
                    type: object
                  description: 'File attachments (for drafts)'
                subchannelId:
                  type: integer
                  description: 'Subchannel ID (for drafts)'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '400':
          description: "Bad request - Can only update internal notes or user doesn't own the note"
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket or conversation not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Conversation
      summary: 'Delete a conversation'
      description: 'Delete an existing conversation (only private conversations can be deleted)'
      operationId: deleteConversation
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the ticket'
        -
          name: conversationId
          in: path
          required: true
          schema:
            type: integer
          description: 'The ID of the conversation to delete'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '400':
          description: "Bad request - Cannot delete public conversation or user doesn't have permission"
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket or conversation not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /sessions:
    get:
      tags:
        - 'NEO Sessions'
      summary: 'List NEO sessions'
      description: "Returns sessions the caller participates in. With `?ticketId=` and `accessTechnicalEvents` permission: returns all sessions on that ticket. With `?ticketId=` as a plain human agent (ticket must be accessible): returns the caller's own sessions on that ticket **plus** the ticket's unclaimed (owner-less) cortex warm-start sessions — the discover-and-continue path, so the human agent picks up cortex's pre-work and continues the same session. `costUsd`/`model` are only included when the caller has `reportAiPerformance`.\n"
      operationId: listNeoSessions
      parameters:
        -
          name: ticketId
          in: query
          required: false
          schema:
            type: integer
          description: 'Filter sessions by ticket ID.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  sessions:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        ticketId:
                          type: integer
                          nullable: true
                        lastActivityAt:
                          type: string
                          format: date-time
                        createdAt:
                          type: string
                          format: date-time
                        participants:
                          type: array
                          items:
                            type: object
                            properties:
                              id:
                                type: integer
                              name:
                                type: string
                                nullable: true
                        messageCount:
                          type: integer
                        costUsd:
                          type: number
                          nullable: true
                          description: 'Aggregate session cost; requires reportAiPerformance permission'
                        model:
                          type: string
                          nullable: true
                          description: 'Latest model used; requires reportAiPerformance permission'
                  success:
                    type: boolean
        '401':
          description: Unauthorized
    post:
      tags:
        - 'NEO Sessions'
      summary: 'Create a NEO session'
      description: "Create a session with a **server-minted** UUID — the BE-owned id-generation path. Always creates a new session (a ticket may have many; the UI decides how many). This is an additional path, not a replacement: a client may still lazily create a session by POSTing its first message with a self-minted UUID (see `POST /sessions/{id}/messages`).\nThe human agent caller is recorded as a participant; an internal (cortex) caller creates an unowned session. `costUsd`/`model` in the response are only included when the caller has `reportAiPerformance`.\n"
      operationId: createNeoSession
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketId:
                  type: integer
                  nullable: true
                  description: 'Associated ticket ID'
      responses:
        '201':
          description: 'Session created'
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      ticketId:
                        type: integer
                        nullable: true
                      lastActivityAt:
                        type: string
                        format: date-time
                      createdAt:
                        type: string
                        format: date-time
                      participants:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                              nullable: true
                      messageCount:
                        type: integer
                      costUsd:
                        type: number
                        nullable: true
                        description: 'Requires reportAiPerformance permission'
                      model:
                        type: string
                        nullable: true
                        description: 'Requires reportAiPerformance permission'
                  success:
                    type: boolean
        '401':
          description: Unauthorized
  '/sessions/{id}':
    get:
      tags:
        - 'NEO Sessions'
      summary: 'Get one NEO session'
      description: "Returns a single session with its `ticketId` — the session→ticket lookup used on a cold-start when only the session id is known. Caller must be a participant of the session, or have `accessTechnicalEvents` permission.\n"
      operationId: getNeoSession
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: 'Session ID (client-minted UUIDv4)'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      ticketId:
                        type: integer
                        nullable: true
                      lastActivityAt:
                        type: string
                        format: date-time
                      createdAt:
                        type: string
                        format: date-time
                      participants:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                              nullable: true
                      messageCount:
                        type: integer
                      costUsd:
                        type: number
                        nullable: true
                        description: 'Aggregate session cost; requires reportAiPerformance permission'
                      model:
                        type: string
                        nullable: true
                        description: 'Latest model used; requires reportAiPerformance permission'
                  success:
                    type: boolean
        '403':
          description: 'Not a participant of this session'
        '404':
          description: 'Session not found'
    patch:
      tags:
        - 'NEO Sessions'
      summary: 'Update session participants'
      description: "Add or remove participants from a session. The caller must already be a participant of the session. A future `title` field is accepted but ignored.\n"
      operationId: patchNeoSession
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: 'Session ID (client-minted UUIDv4)'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                add:
                  type: array
                  items:
                    type: integer
                  description: 'User ids to add as participants'
                remove:
                  type: array
                  items:
                    type: integer
                  description: 'User ids to remove'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  participants:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        name:
                          type: string
                          nullable: true
        '403':
          description: 'Not a participant of this session'
        '404':
          description: 'Session not found'
  '/sessions/{id}/messages':
    get:
      tags:
        - 'NEO Sessions'
      summary: 'Get session transcript'
      description: "Returns all messages for a session, ordered by createdAt ascending. Caller must be a participant of the session, or have `accessTechnicalEvents` permission. `costUsd`, `usageJson`, and `model` are only included when the caller has `reportAiPerformance`.\n"
      operationId: listNeoSessionMessages
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: 'Session ID (client-minted UUIDv4)'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  messages:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        sessionId:
                          type: string
                          format: uuid
                        type:
                          type: string
                          enum:
                            - text
                            - tool_use
                            - tool_result
                            - result
                            - thinking
                        direction:
                          type: string
                          enum:
                            - in
                            - out
                        sender:
                          type: object
                          nullable: true
                          description: '{id, name, email} of the sender; null = NEO'
                        to:
                          type: array
                          nullable: true
                          items:
                            type: object
                            properties:
                              id:
                                type: integer
                        content:
                          type: string
                        attachments:
                          type: array
                          nullable: true
                        createdAt:
                          type: string
                          format: date-time
                        costUsd:
                          type: number
                          nullable: true
                          description: 'Requires reportAiPerformance permission'
                        usageJson:
                          type: object
                          nullable: true
                          description: 'Requires reportAiPerformance permission'
                        model:
                          type: string
                          nullable: true
                          description: 'Requires reportAiPerformance permission'
                  success:
                    type: boolean
        '403':
          description: 'Not a participant of this session'
        '404':
          description: 'Session not found'
    post:
      tags:
        - 'NEO Sessions'
      summary: 'Append a message to a session'
      description: "Append a message to a session. On the first message the session is lazily created and the initiator is recorded as a participant.\nA human agent who is not yet a participant may append to an **unclaimed** (owner-less) cortex warm-start session on a ticket they can access — this **claims** it (they become its first participant), the discover-and-continue path. Posting to a session already claimed by another human returns `403`.\n**Auth rules:** - Cookie/JWT (human agent): sender = {id, name, email}, direction = 'in' - Internal token (cortex/NEO): sender = null, direction = 'out';\n  `costUsd`, `usageJson`, `model` accepted only from the internal caller.\nThe `to` field is stamped by Mind from the current participant set — never read from the request body.\n"
      operationId: createNeoSessionMessage
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: 'Session ID (client-minted UUIDv4)'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
                - type
                - content
              properties:
                id:
                  type: string
                  format: uuid
                  description: 'Client-minted message UUIDv4'
                type:
                  type: string
                  enum:
                    - text
                    - tool_use
                    - tool_result
                    - result
                    - thinking
                content:
                  type: string
                  description: 'Raw payload (opaque string or JSON-encoded object)'
                ticketId:
                  type: integer
                  nullable: true
                  description: 'Associated ticket ID (used on first message to set session.ticketId)'
                attachments:
                  type: array
                  nullable: true
                  items:
                    type: object
                createdAt:
                  type: string
                  format: date-time
                  description: 'Caller-supplied event time (defaults to now if absent)'
      responses:
        '201':
          description: 'Message created'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  id:
                    type: string
                    format: uuid
                  sessionId:
                    type: string
                    format: uuid
                  direction:
                    type: string
                    enum:
                      - in
                      - out
                  to:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
        '400':
          description: 'Missing or invalid fields'
        '401':
          description: Unauthorized
  /tool:
    get:
      tags:
        - 'NEO Tools'
      summary: 'List NEO tools'
      description: "Returns live (non-deleted) tools. With `?aiAgentId=N`, returns shared tools (`aiAgentId` null) plus that agent's private tools.\n"
      operationId: listTools
      parameters:
        -
          name: aiAgentId
          in: query
          required: false
          schema:
            type: integer
          description: "Restrict to shared tools plus this agent's private tools."
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  tools:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        slug:
                          type: string
                        aiAgentId:
                          type: integer
                          nullable: true
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        definition:
                          type: object
                          nullable: true
                        createdAt:
                          type: string
                          format: date-time
                        modifiedAt:
                          type: string
                          format: date-time
                  success:
                    type: boolean
        '401':
          description: Unauthorized
    post:
      tags:
        - 'NEO Tools'
      summary: 'Create a NEO tool'
      description: "Creates a tool. `slug` is the cortex reference name; it defaults to a slug of `name` and is immutable after creation. The definition is validated via an ephemeral agent before saving; a create snapshot is appended to the tool history.\n"
      operationId: createTool
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                slug:
                  type: string
                  description: 'Stable cortex reference name. Defaults to a slug of `name`.'
                aiAgentId:
                  type: integer
                  nullable: true
                  description: 'Owning agent (private) or null (shared).'
                description:
                  type: string
                  nullable: true
                definition:
                  type: object
                  description: 'Full ToolSpec (view/ui/placement/layout/props/input/permission/executor[]).'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  tool:
                    type: object
                    properties:
                      id:
                        type: integer
                      slug:
                        type: string
                      aiAgentId:
                        type: integer
                        nullable: true
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      definition:
                        type: object
                        nullable: true
                      createdAt:
                        type: string
                        format: date-time
                      modifiedAt:
                        type: string
                        format: date-time
                  success:
                    type: boolean
        '400':
          description: 'Missing name or invalid slug'
        '409':
          description: 'A tool with this slug already exists'
        '422':
          description: 'Invalid definition (validation violations returned)'
  '/tool/{id}':
    get:
      tags:
        - 'NEO Tools'
      summary: 'Get a NEO tool'
      operationId: getTool
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Tool ID.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  tool:
                    type: object
                    properties:
                      id:
                        type: integer
                      slug:
                        type: string
                      aiAgentId:
                        type: integer
                        nullable: true
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      definition:
                        type: object
                        nullable: true
                      createdAt:
                        type: string
                        format: date-time
                      modifiedAt:
                        type: string
                        format: date-time
                  success:
                    type: boolean
        '404':
          description: 'Tool not found'
    patch:
      tags:
        - 'NEO Tools'
      summary: 'Update a NEO tool'
      description: "Updates `name`, `description`, `definition` and/or `aiAgentId`. `slug` is immutable and ignored. Re-validated via an ephemeral agent; an update snapshot is appended to history.\n"
      operationId: updateTool
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Tool ID.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                  nullable: true
                definition:
                  type: object
                  nullable: true
                aiAgentId:
                  type: integer
                  nullable: true
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  tool:
                    type: object
                    properties:
                      id:
                        type: integer
                      slug:
                        type: string
                      aiAgentId:
                        type: integer
                        nullable: true
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      definition:
                        type: object
                        nullable: true
                      createdAt:
                        type: string
                        format: date-time
                      modifiedAt:
                        type: string
                        format: date-time
                  success:
                    type: boolean
        '404':
          description: 'Tool not found'
        '422':
          description: 'Invalid definition (validation violations returned)'
    delete:
      tags:
        - 'NEO Tools'
      summary: 'Soft-delete a NEO tool'
      description: 'Soft-deletes the tool and appends a delete snapshot to its history.'
      operationId: deleteTool
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Tool ID.'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '404':
          description: 'Tool not found'
  '/tool/{id}/history':
    get:
      tags:
        - 'NEO Tools'
      summary: 'NEO tool change history'
      description: 'Append-only version history (newest first), never pruned.'
      operationId: getToolHistory
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Tool ID.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  history:
                    type: array
                    items:
                      type: object
                      properties:
                        version:
                          type: integer
                        action:
                          type: string
                          enum:
                            - create
                            - update
                            - delete
                        snapshot:
                          type: object
                          nullable: true
                        changedBy:
                          type: integer
                          nullable: true
                          description: 'User id, or null for a system (cortex) caller.'
                        changedByName:
                          type: string
                          nullable: true
                        createdAt:
                          type: string
                          format: date-time
                  success:
                    type: boolean
        '404':
          description: 'Tool not found'
  /skill:
    get:
      tags:
        - 'NEO Skills'
      summary: 'List NEO skills'
      description: "Returns live (non-deleted) skills. With `?aiAgentId=N`, returns shared skills (`aiAgentId` null) plus that agent's private skills.\n"
      operationId: listSkills
      parameters:
        -
          name: aiAgentId
          in: query
          required: false
          schema:
            type: integer
          description: "Restrict to shared skills plus this agent's private skills."
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  skills:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        slug:
                          type: string
                        aiAgentId:
                          type: integer
                          nullable: true
                        name:
                          type: string
                        description:
                          type: string
                          nullable: true
                        content:
                          type: string
                          nullable: true
                        createdAt:
                          type: string
                          format: date-time
                        modifiedAt:
                          type: string
                          format: date-time
                  success:
                    type: boolean
        '401':
          description: Unauthorized
    post:
      tags:
        - 'NEO Skills'
      summary: 'Create a NEO skill'
      description: "Creates a skill. `slug` is the cortex reference name; it defaults to a slug of `name` and is immutable after creation. `content` is the `skill.md` markdown body. Validated via an ephemeral agent before saving; a create snapshot is appended to the skill history.\n"
      operationId: createSkill
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                slug:
                  type: string
                  description: 'Stable cortex reference name. Defaults to a slug of `name`.'
                aiAgentId:
                  type: integer
                  nullable: true
                  description: 'Owning agent (private) or null (shared).'
                description:
                  type: string
                  nullable: true
                content:
                  type: string
                  nullable: true
                  description: 'The skill.md markdown body.'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  skill:
                    type: object
                    properties:
                      id:
                        type: integer
                      slug:
                        type: string
                      aiAgentId:
                        type: integer
                        nullable: true
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      content:
                        type: string
                        nullable: true
                      createdAt:
                        type: string
                        format: date-time
                      modifiedAt:
                        type: string
                        format: date-time
                  success:
                    type: boolean
        '400':
          description: 'Missing name or invalid slug'
        '409':
          description: 'A skill with this slug already exists'
        '422':
          description: 'Invalid skill (validation violations returned)'
  '/skill/{id}':
    get:
      tags:
        - 'NEO Skills'
      summary: 'Get a NEO skill'
      operationId: getSkill
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Skill ID.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  skill:
                    type: object
                    properties:
                      id:
                        type: integer
                      slug:
                        type: string
                      aiAgentId:
                        type: integer
                        nullable: true
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      content:
                        type: string
                        nullable: true
                      createdAt:
                        type: string
                        format: date-time
                      modifiedAt:
                        type: string
                        format: date-time
                  success:
                    type: boolean
        '404':
          description: 'Skill not found'
    patch:
      tags:
        - 'NEO Skills'
      summary: 'Update a NEO skill'
      description: "Updates `name`, `description`, `content` and/or `aiAgentId`. `slug` is immutable and ignored. Re-validated via an ephemeral agent; an update snapshot is appended to history.\n"
      operationId: updateSkill
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Skill ID.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                  nullable: true
                content:
                  type: string
                  nullable: true
                aiAgentId:
                  type: integer
                  nullable: true
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  skill:
                    type: object
                    properties:
                      id:
                        type: integer
                      slug:
                        type: string
                      aiAgentId:
                        type: integer
                        nullable: true
                      name:
                        type: string
                      description:
                        type: string
                        nullable: true
                      content:
                        type: string
                        nullable: true
                      createdAt:
                        type: string
                        format: date-time
                      modifiedAt:
                        type: string
                        format: date-time
                  success:
                    type: boolean
        '404':
          description: 'Skill not found'
        '422':
          description: 'Invalid skill (validation violations returned)'
    delete:
      tags:
        - 'NEO Skills'
      summary: 'Soft-delete a NEO skill'
      description: 'Soft-deletes the skill and appends a delete snapshot to its history.'
      operationId: deleteSkill
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Skill ID.'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '404':
          description: 'Skill not found'
  '/skill/{id}/history':
    get:
      tags:
        - 'NEO Skills'
      summary: 'NEO skill change history'
      description: 'Append-only version history (newest first), never pruned.'
      operationId: getSkillHistory
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Skill ID.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  history:
                    type: array
                    items:
                      type: object
                      properties:
                        version:
                          type: integer
                        action:
                          type: string
                          enum:
                            - create
                            - update
                            - delete
                        snapshot:
                          type: object
                          nullable: true
                        changedBy:
                          type: integer
                          nullable: true
                          description: 'User id, or null for a system (cortex) caller.'
                        changedByName:
                          type: string
                          nullable: true
                        createdAt:
                          type: string
                          format: date-time
                  success:
                    type: boolean
        '404':
          description: 'Skill not found'
  '/intent/{intentId}':
    get:
      tags:
        - Intent
      summary: 'Get an intent by id'
      description: 'An intent defines in a structured and machine-processable way what the customer wanted, e.g. *save my meter reading of 265 kWh* or *send me a bill*'
      operationId: getIntentById
      parameters:
        -
          name: intentId
          in: path
          required: true
          description: 'The id of the intent that should be retrieved'
          schema:
            type: integer
          example: 1211221
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Intent'
        '403':
          description: Unauthorized
        '404':
          description: 'Customer not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      tags:
        - Intent
      summary: 'Update an intent with new, modified input data'
      operationId: updateIntent
      parameters:
        -
          name: intentId
          in: path
          required: true
          description: 'The id of the intent that should be executed'
          schema:
            type: integer
          example: 1211221
      requestBody:
        description: 'Parameters to override/validate the existing data in the intent'
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                  example: process_meter_reading
                  description: "This field is necessary for creating new intent when we don't have intentId"
                ticketId:
                  type: number
                  example: 1010
                  description: "This field is necessary for creating new intent when we don't have intentId"
                data:
                  type: object
                  description: 'Intent-specific data object that should be used when updating the intent. If a property is not included in the request, it is not updated.'
      responses:
        '200':
          description: 'Intent was updated'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Intent'
        '403':
          description: Unauthorized
        '404':
          description: 'Intent not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Intent
      summary: 'Delete an intent'
      description: 'Delete an intent. This is only possible if the intent is not yet executed'
      operationId: deleteIntent
      parameters:
        -
          name: intentId
          in: path
          required: true
          description: 'The id of the intent that should be deleted'
          schema:
            type: integer
          example: 1211221
      responses:
        '200':
          description: 'Intent was deleted'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Intent not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/intent/byTicketId/{ticketId}':
    get:
      tags:
        - Intent
      summary: 'Get intent(s) detected/assigned for a ticket'
      operationId: getIntentByTicketId
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket for which the intents should be retrieved'
          schema:
            type: integer
          example: 376189
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  ticketId:
                    type: integer
                    example: 376189
                  intentsFound:
                    description: 'Number of intents found'
                    type: integer
                    example: 1
                  aiOutcome:
                    description: "Outcome of the AI's detected processing 1) noDetection: AI did not find anything useful 2) customerDetected: AI detected the customer, but nothing else 3) agentAssist: AI detected an intent, but is not confident enough to excecute it without additional support 4) full: AI fully resolved the issue. No agent interaction needed any more\n"
                    type: string
                    enum:
                      - noDetection
                      - customerDetected
                      - agentAssist
                      - full
                    example: agentAssist
                  intents:
                    type: array
                    items:
                      $ref: '#/components/schemas/Intent'
        '403':
          description: Unauthorized
        '404':
          description: 'Customer not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/intent/{intentId}/execute':
    post:
      tags:
        - Intent
      summary: 'Execute an option for a specific intent'
      operationId: executeIntent
      parameters:
        -
          name: intentId
          in: path
          required: true
          description: 'The id of the intent that should be executed'
          schema:
            type: integer
          example: 1211221
      requestBody:
        description: 'Optional additional parameters to override/validate the existing data in the intent or trigger a response to the customer'
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                intentId:
                  type: number
                code:
                  type: string
                  example: process_meter_reading
                  description: "This field is necessary for creating new intent when we don't have intentId"
                ticketId:
                  type: number
                  example: 1010
                  description: "This field is necessary for creating new intent when we don't have intentId"
                informCustomer:
                  type: boolean
                  description: 'If true, an email is being sent to the customer informing him about the outcome, and the corresponding ticket is closed (if the operation was successful)'
                  default: false
                  example: false
                dryRun:
                  type: boolean
                  description: 'If true, the execution is simulated without any write API calls triggered to the backend systems'
                  default: false
                  example: true
                data:
                  type: object
                  description: 'Intent-specific data object that should be used to process the intent'
                  allOf:
                    -
                      type: object
                      properties:
                        _action:
                          type: string
                          description: 'The way how this intent should be resolved. Must have been listed as option when retrieving the intent'
                          example: enter_into_system
                    -
                      type: object
                      description: 'Any Intent-specific data'
                contractId:
                  type: string
                  description: 'Optional contract id. If provided, the backend verifies that the provided contractId matches the contract id associated to the intent and throws an error if it does not match'
                  example: '376189'
      responses:
        '200':
          description: 'Option was processed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntentOutcome'
              examples:
                success:
                  value:
                    responseCode: entered_into_system
                    success: true
                    messageLocalized: 'Meter reading successfully entered into system'
                    internalData:
                      requestEndpoint: saveReadingByContractId
                      requestParams: readingValue=21;date=2022-12-31
                    executedAt: 1672410060
                    userId: 1
                    sent: true
                    ticketClosed: true
                    recipient: john@smith.com
                    message: 'We successfully processed your meter reading of 21 kWh dated Dec 31, 2022'
                    template: '<p>Dear John,</p><p>%MESSAGE%</p><i>Mike from your service team</i>'
                    txId: c916167c94
                failure:
                  value:
                    responseCode: reading_rejected
                    success: false
                    messageLocalized: 'ERP system rejected reading: Contract has 2 registers, but only one was provided'
                    internalData:
                      requestEndpoint: saveReadingByContractId
                      requestParams: readingValue=21;date=2022-12-31
                    executedAt: 1672410060
                    userId: 1
                    ticketClosed: false
                    sent: false
                    recipient: john@smith.com
                    message: null
                    template: null
                    txId: c916167c94
        '403':
          description: Unauthorized
        '404':
          description: 'Intent not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /intent/list:
    get:
      tags:
        - Intent
      summary: 'List of all available intents and categories'
      operationId: getIntentList
      parameters:
        -
          name: ticketId
          in: query
          required: false
          description: 'If provided, include specific recommendations based on the provided ticket id'
          schema:
            type: integer
          example: 376189
        -
          name: q
          in: query
          required: true
          description: 'Search query'
          schema:
            type: string
          example: Zählerstand
        -
          name: includeTopIntents
          in: query
          required: false
          description: 'Includes also top intents as a root items. Top intents are suggested intents based on what the AI thinks fits to the ticket'
          schema:
            type: boolean
            default: false
          example: true
        -
          name: includeUnassignedIntents
          in: query
          required: false
          description: "Includes any intents that are not assigned to a skill tag. Useful when a supervisor edits intents, and we don't have an agent working on the ticket"
          schema:
            type: boolean
            default: false
          example: false
        -
          name: onlyTagsWithIntents
          in: query
          required: false
          description: 'If yes, then tags with no intents assigned will be excluded from the result tree'
          schema:
            type: boolean
            default: false
          example: false
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  intentCandidates:
                    allOf:
                      -
                        $ref: '#/components/schemas/TagWithIntentCandidates'
                      -
                        type: object
                        properties:
                          children:
                            type: array
                            items:
                              $ref: '#/components/schemas/TagWithIntentCandidates'
        '403':
          description: Unauthorized
        '404':
          description: 'Customer not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/intent/preview/{aiAgentId}':
    get:
      tags:
        - Intent
      summary: 'Get a preview of an intent'
      operationId: getIntentPreview
      parameters:
        -
          name: aiAgentId
          in: path
          required: true
          description: 'The ID of the AI agent to preview'
          schema:
            type: integer
            format: int32
            minimum: 0
          example: 1
        -
          name: ticketId
          in: query
          required: true
          description: 'The ticketId for which to tailor the response to. A future release will also support an intent preview without linking it to a ticketId'
          schema:
            type: string
          example: 20
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Intent'
        '403':
          description: Unauthorized
        '404':
          description: 'Customer not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiAgents:
    get:
      tags:
        - AiAgent
      summary: 'List of all available AI agents as an array'
      operationId: getAiAgents
      parameters:
        -
          $ref: '#/components/parameters/limitParam'
        -
          $ref: '#/components/parameters/offsetParam'
        -
          name: categoryFilter
          in: query
          required: false
          description: 'Filter criteria based on category. If ommited, only ai agents will be returned ("intent")'
          example: intent
          schema:
            type: string
            enum:
              - all
              - intent
              - template
        -
          name: typeFilter
          in: query
          required: false
          description: 'Filter criteria based on type of the executor. If ommited, all types are returned.'
          example: apiCall
          schema:
            type: string
            enum:
              - sourceCode
              - apiCall
              - visualEditor
        -
          name: format
          in: query
          required: false
          description: 'Output format. Either short (only name/id), medium (key facts) or full (all details). Defaults to full.'
          example: full
          schema:
            type: string
            enum:
              - short
              - medium
              - full
        -
          name: q
          in: query
          required: false
          description: 'Search query for fulltext search in name and description fields. Results are ordered by relevance when search is used'
          example: 'customer contract'
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AiAgent'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiAgents/tree:
    get:
      tags:
        - AiAgent
      summary: 'List of all available AI agents as an tree based on associated tag'
      operationId: getAiAgentsTree
      parameters:
        -
          $ref: '#/components/parameters/limitParam'
        -
          $ref: '#/components/parameters/offsetParam'
        -
          name: categoryFilter
          in: query
          required: false
          description: 'Filter criteria based on category. If ommited, only ai agents will be returned ("intent")'
          example: intent
          schema:
            type: string
            enum:
              - all
              - intent
              - template
        -
          name: typeFilter
          in: query
          required: false
          description: 'Filter criteria based on type of the executor. If ommited, all types are returned.'
          example: apiCall
          schema:
            type: string
            enum:
              - sourceCode
              - apiCall
              - visualEditor
        -
          name: format
          in: query
          required: false
          description: 'Output format. Either short (only name/id), medium (key facts) or full (all details). Defaults to full.'
          example: full
          schema:
            type: string
            enum:
              - short
              - medium
              - full
        -
          name: q
          in: query
          required: false
          description: 'Search query for fulltext search in name and description fields. Results are ordered by relevance when search is used'
          example: 'customer contract'
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  aiAgentsTree:
                    allOf:
                      -
                        $ref: '#/components/schemas/TagWithAiAgents'
                      -
                        type: object
                        properties:
                          children:
                            type: array
                            items:
                              $ref: '#/components/schemas/TagWithAiAgents'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiAgents/availableDefaults:
    get:
      tags:
        - AiAgent
      summary: 'Get list of available enneo default AI Agents'
      description: 'Retrieve all available enneo default AI agents that can be imported. Shows which agents are already imported and which are available for import.'
      operationId: getAvailableDefaultAiAgents
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  availableAiAgents:
                    type: array
                    items:
                      type: object
                      description: 'AI agent configuration data'
                      properties:
                        name:
                          type: string
                          description: 'Display name of the AI agent'
                          example: Basis-Agent
                        description:
                          type: string
                          description: 'Description of what the AI agent does'
                          example: 'Erledigt grundlegende Aufgaben und ist mit Branchendaten vortrainiert.'
                        channels:
                          type: array
                          items:
                            type: string
                          description: 'Supported communication channels'
                          example:
                            - email
                            - phone
                            - chat
                            - letter
                        intelligence:
                          type: string
                          description: 'Type of intelligence used'
                          example: smart
                        settings:
                          type: object
                          description: 'AI agent configuration settings'
                        id:
                          type: integer
                          description: 'Internal ID'
                          example: 1
                        slug:
                          type: string
                          description: 'URL-friendly identifier'
                          example: basic_task_agent
                        createdAt:
                          type: string
                          format: date-time
                          description: 'Creation timestamp'
                          example: '2023-08-08 18:53:23'
                        modifiedAt:
                          type: string
                          format: date-time
                          description: 'Last modification timestamp'
                          example: '2025-06-06 09:19:24'
                  success:
                    type: boolean
                    example: true
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiAgents/samples:
    get:
      tags:
        - AiAgent
      summary: 'List all sample AI agents'
      description: 'Returns all sample AI agents from the enneo admin portal, without any environment or category filtering. Each agent includes additional `includedInSeeds` and `category` metadata fields.'
      operationId: listSampleAiAgents
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AiAgent'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiAgents/samples/{id}':
    get:
      tags:
        - AiAgent
      summary: 'Get a sample AI agent by ID'
      description: 'Returns a single sample AI agent from the enneo admin portal by its ID. The response includes additional `includedInSeeds` and `category` metadata fields.'
      operationId: getSampleAiAgent
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The ID of the sample AI agent'
          schema:
            type: string
          example: '53'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AiAgent'
        '403':
          description: Unauthorized
        '404':
          description: 'Sample AI agent not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiAgents/loadDefaults:
    post:
      tags:
        - AiAgent
      summary: 'Load enneo default AI Agents'
      description: "Load all the enneo default AI agents, e.g. for meter reading, bank data, etc., and saves them in the client DB. If the client is configured to not use enneo's default AI agents, then nothing is written."
      operationId: loadDefaultAiAgents
      responses:
        '200':
          description: 'Loaded successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '412':
          description: 'Client is configured to not use AI agents in his settings'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiAgent:
    post:
      tags:
        - AiAgent
      summary: 'Create a new aiAgent'
      description: 'This API creates a new AI Agent and returns the newly created id'
      operationId: createAiAgent
      requestBody:
        description: 'The new AI Agent that should be created. If not defined, then a blank "new ai agent" will be created'
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AiAgent'
      responses:
        '200':
          description: 'New intent created'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '422':
          description: 'AI agent definition contains invalid parameter references'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AiAgentValidationError'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiAgent/{id}':
    get:
      tags:
        - AiAgent
      summary: 'Get an AI Agent by id'
      operationId: getAiAgent
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the AI Agent that should be retrieved'
          schema:
            type: string
          example: ai_agent_bank_data_code
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AiAgent'
        '403':
          description: Unauthorized
        '404':
          description: 'Intent description not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - AiAgent
      summary: 'Update an existing AI Agent'
      operationId: updateAiAgent
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The unique identificator of the AI Agent'
          schema:
            type: string
          example: process_meter_reading
      requestBody:
        description: 'The new updated AI agent'
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AiAgent'
      responses:
        '200':
          description: 'Intent description updated'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Intent description not found'
        '422':
          description: 'AI agent definition contains invalid parameter references'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AiAgentValidationError'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - AiAgent
      summary: 'Delete an existing AI Agent'
      operationId: deleteAiAgent
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The unique identificator of the AI Agent'
          schema:
            type: string
          example: process_meter_reading
      responses:
        '200':
          description: 'Intent description deleted'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Intent description not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiAgent/{id}/similarTickets':
    get:
      tags:
        - AiAgent
      summary: 'Get similar tickets for an AI Agent'
      operationId: getSimilarTickets
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the AI Agent that should be retrieved'
          schema:
            type: integer
            format: int32
          example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Ticket'
        '403':
          description: Unauthorized
        '404':
          description: 'Intent description not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiAgent/{id}/preview':
    post:
      tags:
        - AiAgent
      summary: 'Preview an AI Agent'
      operationId: getAiAgentPreview
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the AI Agent that should be retrieved'
          schema:
            type: integer
            format: int32
          example: 1
        -
          name: ticketId
          in: query
          required: true
          description: 'The ticketId for which to tailor the response to. A future release will also support an intent preview without linking it to a ticketId'
          schema:
            type: integer
          example: 20
      requestBody:
        description: 'The new updated AI agent'
        required: true
        content:
          application/json:
            schema:
              allOf:
                -
                  $ref: '#/components/schemas/AiAgent'
                -
                  type: object
                  properties:
                    previewType:
                      type: string
                      description: 'The type of preview to use'
                    message:
                      type: string
                      description: 'The message to send to the AI Agent'
                    subject:
                      type: string
                      description: 'The subject of the message'
                    sender:
                      type: string
                      description: 'The sender of the message'
                    history:
                      type: array
                      description: 'The history of the message'
                      items:
                        type: object
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: 'The outcome of the preview'
                    example: true
                  error:
                    type: string
                    description: 'The error message if the preview failed'
                    example: null
                    nullable: true
                  dataOutcome:
                    type: object
                    description: 'The outcome of the data preview'
                  dataOutcomeInfo:
                    type: string
                  customerOutcome:
                    type: object
                    description: 'The outcome of the customer preview'
                  customerOutcomeType:
                    type: string
                  customerOutcomeInfo:
                    type: string
                  curlRequests:
                    type: array
                    items:
                      type: object
                    description: 'The curl requests that would be sent to the backend systems'
                    example: []
                    nullable: true
  '/aiAgent/{id}/prompt-eval':
    post:
      tags:
        - 'AI Agents'
      summary: 'Evaluate an AI agent prompt'
      description: "Run the LLM prompt that belongs to the given AI agent against a sample input and return the\nraw model output. Used by the prompt editor in ops-fe to iterate on prompt wording without\ndeploying the agent.\n"
      operationId: aiAgentPromptEval
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'ID of the AI agent.'
          schema:
            type: integer
          example: 42
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                prompt:
                  type: string
                  description: "Prompt template to evaluate (overrides the agent's stored prompt for this call)."
                ticketId:
                  type: integer
                  description: 'Optional ticket id to inject ticket context into the prompt.'
                variables:
                  type: object
                  description: 'Additional template variables.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  output:
                    type: string
                    description: 'Raw LLM output.'
                  tokens:
                    type: object
                    description: 'Token usage breakdown.'
        '400':
          description: 'Invalid input'
        '403':
          description: Unauthorized
  /aiAgent/outcome:
    post:
      tags:
        - AiAgent
      summary: 'Get the outcome of an AI Agent'
      operationId: getAiAgentOutcome
      requestBody:
        description: 'The new updated AI agent'
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ticket:
                  type: object
                  properties:
                    id:
                      type: integer
                      description: 'The ID of the ticket'
                      example: 8
                    contractId:
                      type: string
                      description: 'The contract ID associated with the ticket'
                      example: '715559'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: 'The outcome of the preview'
                    example: true
                  eventId:
                    type: integer
                    description: 'The event ID of the outcome'
                    example: 1234
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /tools:
    get:
      tags:
        - 'AI Tools'
      summary: 'Get available tools'
      description: 'Get a list of available tools that can be used by AI agents. This includes both built-in tools and user-defined functions marked as tools.'
      operationId: getTools
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      tools:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                              description: 'The name of the tool'
                              example: GetKnowledgeBaseEntries
                            type:
                              type: string
                              description: 'The type of the tool (builtin or custom)'
                              enum:
                                - builtin
                                - custom
                              example: builtin
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/tools/{identifier}':
    get:
      tags:
        - 'AI Tools'
      summary: 'Get a specific custom tool'
      description: 'Get details of a specific custom tool by its ID or slug. These are user-defined functions marked as tools.'
      operationId: getTool
      parameters:
        -
          name: identifier
          in: path
          required: true
          description: 'The identifier of the tool to get, either the numeric ID or the slug'
          schema:
            oneOf:
              -
                type: integer
              -
                type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      tool:
                        type: object
                        properties:
                          name:
                            type: string
                            description: 'The name of the tool'
                            example: MyCustomTool
                          id:
                            type: integer
                            description: 'The ID of the tool'
                            example: 1
                          description:
                            type: string
                            description: 'Description of what the tool does'
                            example: 'My custom tool that does something'
                          parameters:
                            type: object
                            description: 'Parameters configuration for the tool'
                          executor:
                            type: object
                            description: 'Executor configuration for the tool'
        '403':
          description: Unauthorized
        '404':
          description: 'Tool not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/tools/{identifier}/run':
    post:
      tags:
        - 'AI Tools'
      summary: 'Execute a tool'
      description: 'Execute a specific custom tool by its ID or slug. The request body should contain the parameters the tool expects. Required parameters are validated before execution.'
      operationId: executeTool
      parameters:
        -
          name: identifier
          in: path
          required: true
          description: 'The identifier of the tool to execute, either the numeric ID or the slug'
          schema:
            oneOf:
              -
                type: integer
              -
                type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: "Key-value object of parameters the tool expects. The required parameters and their types depend on the tool's parameter definitions."
              example:
                contractId: 7115214
      responses:
        '200':
          description: 'Tool executed successfully'
          content:
            application/json:
              schema:
                description: 'The output of the tool execution. The structure depends on the tool.'
        '400':
          description: 'Validation error (missing required parameters or type mismatch) or tool execution failed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Unauthorized - missing runExecutors permission or tool is private'
        '404':
          description: 'Tool not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiQualityCheck/testRun:
    post:
      tags:
        - 'AI Quality Check'
      summary: 'Schedule a new AI quality check'
      operationId: scheduleAiQualityCheck
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                aiAgentId:
                  type: integer
                  format: int32
                  description: 'The id of the AI agent to filter test cases by. If not set, all test cases will be tested using their reference agents. Note that each test case creates only ONE test ticket per test run, regardless of how many agents are in its aiAgentIds array.'
                  example: 123
                description:
                  type: string
                  description: 'Optional description for the test run'
                  example: 'Standard quality check for bank data'
                config:
                  type: object
                  properties:
                    limit:
                      type: integer
                      description: 'Limit the number of test cases to run. If not set or 0, there is no limit.'
                      example: 100
                    reRunModels:
                      type: boolean
                      description: 'Whether to re-run the AI models. If not set, models will be re-run.'
                      example: true
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AiTestRun'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    get:
      tags:
        - 'AI Quality Check'
      summary: 'Get all test runs with pagination'
      operationId: getAiQualityTestRuns
      parameters:
        -
          $ref: '#/components/parameters/limitParam'
        -
          $ref: '#/components/parameters/offsetParam'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AiTestRun'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiQualityCheck/testRun/{testRunId}':
    get:
      tags:
        - 'AI Quality Check'
      summary: 'Get a specific test run by ID'
      operationId: getAiQualityTestRun
      parameters:
        -
          name: testRunId
          in: path
          required: true
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  scores:
                    type: object
                    example:
                      overall: 0.82
                      contractId: 0.72
                      sentiment: 0.89
                  testRun:
                    $ref: '#/components/schemas/AiTestRun'
                  tickets:
                    type: array
                    items:
                      $ref: '#/components/schemas/AiTestTicket'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - 'AI Quality Check'
      summary: 'Delete a specific test run by ID'
      operationId: deleteAiQualityTestRun
      parameters:
        -
          name: testRunId
          in: path
          required: true
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '404':
          description: 'Test run not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiQualityCheck/testRun/{testRunId}/cancel':
    patch:
      tags:
        - 'AI Quality Check'
      summary: 'Cancel a specific test run by ID'
      description: "Cancels a test run that is currently in 'scheduled' or 'processing' state. All pending test tickets will be cancelled and the test run will be set to 'cancelled' state."
      operationId: cancelAiQualityTestRun
      parameters:
        -
          name: testRunId
          in: path
          required: true
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Test run cancelled successfully'
        '400':
          description: 'Test run cannot be cancelled in its current state'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  error:
                    type: string
                    example: 'Cannot cancel test run in state completed'
        '404':
          description: 'Test run not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiQualityCheck/testRun/{testRunId}/rerun':
    post:
      tags:
        - 'AI Quality Check'
      summary: 'Re-run a previously executed test run'
      description: "Re-executes the same set of test tickets that the original test run used. The new run is\ncreated in `scheduled` state and processed asynchronously. Compare its result against the\noriginal to measure regressions across AI agent versions.\n"
      operationId: rerunAiQualityTestRun
      parameters:
        -
          name: testRunId
          in: path
          required: true
          description: 'ID of the test run to re-execute.'
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Re-run scheduled'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  testRun:
                    type: object
                    description: 'The newly scheduled test run.'
        '404':
          description: 'Original test run not found'
  '/aiQualityCheck/testRun/{testRunId}/updateExpectedResult/{ticketId}':
    patch:
      tags:
        - 'AI Quality Check'
      summary: 'Update the expected result of a test ticket'
      description: 'Update the expected value for specific ticket in a run to a value passed in the payload. This also updates the expected results for all future runs, and updates the statistics of the test run.'
      operationId: updateExpectedResultAiQualityTestTicket
      parameters:
        -
          name: testRunId
          in: path
          required: true
          schema:
            type: integer
          example: 123
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          example: 456
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                expectedResult:
                  $ref: '#/components/schemas/AiTestTicketResult'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Test ticket expected result updated successfully'
                  testTicket:
                    $ref: '#/components/schemas/AiTestTicket'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiQualityCheck/testRun/{testRunId}/acceptExpectedResult/{ticketId}':
    patch:
      tags:
        - 'AI Quality Check'
      summary: 'Accept the expected result of a test ticket'
      description: 'Set the expected value for specific ticket in a run to the observed value. This also updates the expected results for all future runs, and updates the statistics of the test run.'
      operationId: acceptExpectedResultAiQualityTestTicket
      parameters:
        -
          name: testRunId
          in: path
          description: 'The numeric ID of the test run'
          required: true
          schema:
            type: integer
          example: 123
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
          example: 456
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'Test ticket expected result accepted successfully'
                  testTicket:
                    $ref: '#/components/schemas/AiTestTicket'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/aiQualityCheck/testRun/{testRunId}/acceptAllExpectedResults':
    post:
      tags:
        - 'AI Quality Check'
      summary: 'Accept ALL the expected results of for a test run'
      description: 'Set the expected values for all tickets in a run to their observed values. This also updates the expected results for all future runs, and updates the statistics of the test run.'
      operationId: acceptAllExpectedResultsAiQualityTestRun
      parameters:
        -
          name: testRunId
          in: path
          description: 'The numeric ID of the test run'
          required: true
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: 'All test ticket expected results accepted successfully'
                  testTickets:
                    type: array
                    items:
                      $ref: '#/components/schemas/AiTestTicket'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /aiQualityCheck/testCase:
    get:
      tags:
        - 'AI Quality Check'
      summary: 'List all test cases'
      operationId: listAiQualityTestCases
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AiTestCase'
    post:
      tags:
        - 'AI Quality Check'
      summary: 'Create an AI quality test case'
      description: "Create a new test case (an AI-agent-bound ticket whose expected result is locked in) so it\ncan be replayed by future test runs. The body specifies the ticket id, target AI agent and\nthe expected result.\n"
      operationId: createAiQualityTestCase
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketId:
                  type: integer
                  description: 'Ticket to use as the test case.'
                aiAgentId:
                  type: integer
                  description: 'AI agent the test case is bound to.'
                expectedResult:
                  $ref: '#/components/schemas/AiTestTicketResult'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  testCase:
                    $ref: '#/components/schemas/AiTestCase'
  /aiQualityCheck/testAiAgent:
    get:
      tags:
        - 'AI Quality Check'
      summary: 'Get all AI agents for which test runs can be triggered'
      operationId: aiQualityTestAiAgent
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TestAiAgent'
  '/aiQualityCheck/testCase/{id}':
    get:
      tags:
        - 'AI Quality Check'
      summary: 'Get test cases for a specific AI agent'
      description: "Get all test cases associated with the specified AI agent ID. Here `{id}` is treated as an\nAI agent id (digits, or `all` to list across every agent). This is a different overload of\n`{id}` than the `DELETE`/`PATCH` variants below, which take a numeric test case id.\n"
      operationId: getAiQualityTestCasesByAiAgentId
      parameters:
        -
          name: id
          in: path
          required: true
          description: "The AI agent ID (numeric, or 'all')."
          schema:
            type: string
          example: '123'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AiTestCase'
    post:
      tags:
        - 'AI Quality Check'
      summary: 'Add test cases for an AI agent'
      description: "Add multiple test cases (tickets) to the specified AI agent. Here `{id}` is the AI agent id —\nsee the note on `GET` above for the dual meaning of `{id}` on this path.\n"
      operationId: addAiQualityTestCases
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The AI agent ID.'
          schema:
            type: string
          example: '123'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketIds:
                  type: array
                  items:
                    type: integer
                  description: 'The ids of the tickets to add as test cases for the respective AI agent'
                  example:
                    - 233
                    - 237
                    - 238
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  createdTestCases:
                    type: array
                    items:
                      $ref: '#/components/schemas/AiTestCase'
    delete:
      tags:
        - 'AI Quality Check'
      summary: 'Delete a test case by ID'
      description: "Delete a specific test case using its numeric test case ID. Here `{id}` is a test case id\n(numeric only). The Dispatcher pins this method to the `\\d+` regex constraint so AI-agent\n`all` requests cannot reach this handler.\n"
      operationId: deleteAiQualityTestCase
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The test case ID (numeric).'
          schema:
            type: integer
          example: 456
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
    patch:
      tags:
        - 'AI Quality Check'
      summary: 'Update a test case by ID'
      description: "Update a specific test case using its numeric test case ID. As with `DELETE`, `{id}` here is\na test case id and is pinned to `\\d+` by the Dispatcher.\n"
      operationId: updateAiQualityTestCase
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The test case ID (numeric).'
          schema:
            type: integer
          example: 456
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                description:
                  type: string
                  example: 'Updated test case for bank data change'
                expectedResult:
                  type: object
                  example:
                    contractId: 715382
                    sentiment: disappointed
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    $ref: '#/components/schemas/AiTestCase'
  /template:
    get:
      tags:
        - Template
      summary: 'List response templates'
      description: "Returns the paginated list of response templates that agents can insert into tickets. When no template matches the provided filters the API responds with HTTP 404.\n"
      operationId: listTemplates
      parameters:
        -
          name: offset
          in: query
          description: 'Number of templates to skip before starting to collect the result set.'
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
          example: 100
        -
          name: limit
          in: query
          description: 'Maximum number of templates to return.'
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
          example: 25
        -
          name: q
          in: query
          description: 'Optional full-text search term applied to description and message fields.'
          required: false
          schema:
            type: string
          example: Zählerstand
        -
          name: aiAgentId
          in: query
          description: "Filter templates that are linked to a specific AI Agent. Acts as a tag filter internally.\n"
          required: false
          schema:
            type: integer
          example: 7
      responses:
        '200':
          description: 'Templates found'
          content:
            application/json:
              schema:
                type: object
                properties:
                  templates:
                    type: array
                    items:
                      $ref: '#/components/schemas/Template'
                  success:
                    type: boolean
                    example: true
              examples:
                default:
                  summary: 'First page of filtered templates'
                  value:
                    success: true
                    templates:
                      -
                        id: 12
                        tagId: 101
                        emailTemplateId: 5
                        description: 'Reminder: please send your meter reading'
                        message: '<p>Hallo {{contract.firstName}},</p><p>bitte sende uns den Stand.</p>'
                        subject: 'Wir benötigen deinen Zählerstand'
                        exampleTicketIds:
                          - 8812
                          - 9100
                        attachments: []
                      -
                        id: 13
                        tagId: null
                        emailTemplateId: 5
                        description: 'Default closing'
                        message: '<p>Vielen Dank für deine Nachricht.</p>'
                        subject: null
                        exampleTicketIds: []
                        attachments:
                          -
                            name: instructions.pdf
                            url: 'https://cdn.example.com/template/instructions.pdf'
        '403':
          description: Unauthorized
        '404':
          description: 'No templates match the provided filters'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - Template
      summary: 'Create a response template'
      description: 'Requires the `manageTemplates` permission.'
      operationId: createTemplate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - message
              properties:
                description:
                  type: string
                  description: 'Human readable description of the template.'
                  example: 'Reminder: missing meter reading'
                message:
                  type: string
                  description: "The template body in HTML/Handlebars format. Must contain the actual customer-facing text. In addition to the standard [Handlebars syntax](https://handlebarsjs.com/guide/), a set of custom helpers is available (`contains`, `compare`, `gt`, `lt`, `formatDateDE`, `addDays`, `extractFirstName`, `last4digits`, …). See the [Templates documentation](https://docs.enneo.ai/en/work-resources/templates) for the full list. Note: helpers like `contains`, `compare`, `gt`, and `lt` compare values as strings — wrap numeric IDs in quotes (`contains in.tagIds \"22\"`, not `contains in.tagIds 22`).\n"
                  example: '<p>Hallo {{contract.firstName}}, bitte sende uns den Stand.</p>'
                tagId:
                  type: integer
                  nullable: true
                  description: 'Optional tag used to categorize the template.'
                emailTemplateId:
                  type: integer
                  nullable: true
                  description: "Wrapper template ID used for greetings/signature. Defaults to the generic template configured in settings.\n"
                subject:
                  type: string
                  nullable: true
                  description: 'Optional subject used for proactive emails.'
                exampleTicketIds:
                  type: array
                  description: 'Ticket IDs that showcase how this template is used.'
                  items:
                    type: integer
                attachments:
                  type: array
                  description: 'Files that should be attached when inserting the template.'
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                        description: 'Optional filename override.'
                      url:
                        type: string
                        format: uri
                        description: 'Existing file to fetch and attach.'
                      base64:
                        type: string
                        description: 'Base64 encoded file content (alternative to url).'
            example:
              description: 'Zählerstand erfolgreich verarbeitet'
              message: '<p>Wir haben deinen Stand {{intent.data.reading}} kWh erfasst.</p>'
              tagId: 42
              subject: 'Wir haben deinen Zählerstand'
              emailTemplateId: 5
              exampleTicketIds:
                - 9001
              attachments:
                -
                  base64: SGVsbG8gd29ybGQ=
                  name: hinweis.txt
      responses:
        '200':
          description: 'Template created successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Template'
              examples:
                default:
                  summary: 'Newly created template'
                  value:
                    success: true
                    id: 123
                    tagId: 42
                    emailTemplateId: 5
                    description: 'Zählerstand erfolgreich verarbeitet'
                    message: '<p>Wir haben deinen Stand {{intent.data.reading}} kWh erfasst.</p>'
                    subject: 'Wir haben deinen Zählerstand'
                    exampleTicketIds:
                      - 9001
                    attachments: []
        '400':
          description: 'Validation error (e.g., invalid handlebars syntax)'
        '403':
          description: 'Unauthorized / missing permission'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/template/{id}':
    parameters:
      -
        name: id
        in: path
        required: true
        description: 'Template ID'
        schema:
          type: integer
        example: 12
    get:
      tags:
        - Template
      summary: 'Get template details'
      description: "Returns the template including the underlying generic wrapper and merged HTML. Example ticket IDs are filtered to existing tickets.\n"
      operationId: getTemplate
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Template'
              examples:
                default:
                  summary: 'Template with merged HTML'
                  value:
                    success: true
                    id: 12
                    tagId: 101
                    emailTemplateId: 5
                    description: 'Reminder: please send your meter reading'
                    message: '<p>Hallo {{contract.firstName}},</p><p>bitte sende uns den Stand.</p>'
                    template: '<p>Hallo {{contract.firstName}},</p>%MESSAGE%<p>Viele Grüße</p>'
                    mergedTemplate: '<p>Hallo {{contract.firstName}},</p><p>bitte sende uns den Stand.</p><p>Viele Grüße</p>'
                    subject: 'Wir benötigen deinen Zählerstand'
                    exampleTicketIds:
                      - 8812
                    attachments: []
        '403':
          description: Unauthorized
        '404':
          description: 'Template not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Template
      summary: 'Update a template'
      description: 'Requires the `manageTemplates` permission.'
      operationId: updateTemplate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                description:
                  type: string
                message:
                  type: string
                tagId:
                  type: integer
                  nullable: true
                emailTemplateId:
                  type: integer
                  nullable: true
                subject:
                  type: string
                  nullable: true
                exampleTicketIds:
                  type: array
                  items:
                    type: integer
                attachments:
                  type: array
                  items:
                    type: object
                    additionalProperties: true
            example:
              description: 'Reminder (updated wording)'
              message: '<p>Hallo {{contract.firstName}},</p><p>wir brauchen deinen Stand bis {{addDays intent.data.date 3}}.</p>'
              subject: 'Wir benötigen deinen Zählerstand (Update)'
              exampleTicketIds:
                - 8812
                - 9100
      responses:
        '200':
          description: 'Template updated successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Template'
        '400':
          description: 'Validation error'
        '403':
          description: 'Unauthorized / missing permission'
        '404':
          description: 'Template not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Template
      summary: 'Delete a template'
      description: 'Soft-deletes the template. Requires the `manageTemplates` permission.'
      operationId: deleteTemplate
      responses:
        '200':
          description: 'Template deleted successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
              examples:
                default:
                  summary: 'Confirmation response'
                  value:
                    success: true
        '400':
          description: 'Template cannot be deleted (e.g., used as generic template)'
        '403':
          description: 'Unauthorized / missing permission'
        '404':
          description: 'Template not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /template/preview:
    post:
      tags:
        - Template
      summary: 'Preview a template with ticket data'
      description: "Renders a template fragment using the optional ticket or contract data. When `ticketId` is provided, AI extracted intent data is injected before rendering. The response returns HTML with `<br>` tags to preserve formatting in downstream clients.\n"
      operationId: previewTemplate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - templateText
              properties:
                templateText:
                  type: string
                  description: "Template text to render. Supports [Handlebars syntax](https://handlebarsjs.com/guide/) plus a set of Enneo-specific helpers — see the [Templates documentation](https://docs.enneo.ai/en/work-resources/templates) for the full list.\n"
                  example: '<p>Wir haben den Stand {{intent.data.reading}} kWh erfasst.</p>'
                ticketId:
                  type: integer
                  description: 'Ticket used to hydrate variables (intent data, customer data, etc.).'
                  example: 8821
                contractId:
                  type: string
                  description: "Contract ID used to load customer context when no ticket is available.\n"
                  example: 4711-2023
      responses:
        '200':
          description: 'Template preview rendered successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  preview:
                    type: string
                    description: 'Rendered HTML preview (line breaks already converted to `<br>`).'
                  variables:
                    type: object
                    description: "Replacement data derived from the ticket or contract (returned only when contextual data was available).\n"
              examples:
                ticketContext:
                  summary: 'Preview rendered with ticket data'
                  value:
                    success: true
                    preview: '<p>Vielen Dank für die Meldung deines Zählerstandes.</p><br><p>Wir haben 20112 kWh für den 31.01.2023 erfasst.</p>'
                    variables:
                      intent:
                        data:
                          reading: 20112
                          date: '2023-01-31'
                      contract:
                        id: 4711-2023
        '400':
          description: 'Invalid template or missing required data'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /profiles:
    get:
      tags:
        - Profile
      summary: 'Search profiles'
      operationId: getProfiles
      parameters:
        -
          name: q
          in: query
          required: false
          description: 'Search string. It searches in any part of firstName, lastName or email. Defaults to all users'
          schema:
            type: string
          example: 'John Doe'
        -
          name: 'userIds[]'
          in: query
          required: false
          description: 'List of specific user IDs to retrieve. When provided, only profiles with these IDs will be returned. Works in combination with other filters (e.g., show, teamIds).'
          schema:
            type: array
            items:
              type: integer
          example:
            - 1
            - 4
        -
          name: 'show[]'
          in: query
          required: false
          description: "Which profiles to show. Can be provided as a single string value (?show=users) or as array values (?show[]=users&show[]=enneo). Defaults to 'users'"
          schema:
            type: array
            items:
              type: string
              enum:
                - users
                - all
                - enneo
                - serviceWorkers
                - codeExecutors
                - enneoPartners
          examples:
            single:
              value:
                - users
              summary: 'Single type'
            multiple:
              value:
                - users
                - enneo
              summary: 'Multiple types combined'
        -
          name: format
          in: query
          required: false
          description: "Determines the format of the profiles. Possible values are 'full' or 'short'. In 'full' format, the settings and last login date of the user are included, provided the user has sufficient permissions."
          schema:
            type: string
            enum:
              - full
              - short
          example: full
        -
          name: limit
          in: query
          required: false
          description: 'Maximum number of profiles to return. Defaults to 100.'
          schema:
            type: integer
            minimum: 1
          example: 50
        -
          name: offset
          in: query
          required: false
          description: 'Number of profiles to skip. Useful for pagination. Defaults to 0.'
          schema:
            type: integer
            minimum: 0
          example: 100
        -
          name: 'teamIds[]'
          in: query
          required: false
          description: 'List of team IDs. If provided, only users from these teams will be returned. When not specified, all users are returned. When "unassigned", only users without a team are returned.'
          schema:
            nullable: true
            type: array
            items:
              type: integer
          example:
            - 1
            - 2
        -
          name: 'tagIds[]'
          in: query
          required: false
          description: 'List of tag IDs. If provided, only users with these skills will be returned'
          schema:
            type: array
            items:
              type: integer
          example:
            - 1
            - 2
        -
          name: 'channels[]'
          in: query
          required: false
          description: 'List of channel IDs. If provided, only users with these channels will be returned'
          schema:
            type: array
            items:
              type: string
          example:
            - email
            - chat
        -
          name: roleId
          in: query
          required: false
          description: 'Filter profiles by role ID'
          schema:
            type: integer
          example: 1
        -
          name: lastSeen
          in: query
          required: false
          description: "Filter users by their last seen time. Can be either a datetime value (e.g. \"2023-12-01 14:30:00\") \nor \"online\" to show only currently active users. When \"online\" is used, it shows users active in the last 10 minutes.\nNote: Requires 'readUserLastSeenDate' or 'readAnyUserProfile' permission.\n"
          schema:
            type: string
            example: online
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  profiles:
                    type: array
                    items:
                      allOf:
                        -
                          $ref: '#/components/schemas/AuthProfile'
                        -
                          $ref: '#/components/schemas/MindProfile'
                        -
                          type: object
                          properties:
                            lastSeen:
                              type: string
                              format: DateTime
                              description: 'The last time the user was seen online'
                              example: '2021-08-12 12:21:21'
    patch:
      tags:
        - Profile
      summary: 'Update multiple profiles at once'
      description: "Bulk update multiple user profiles. Each profile update can include settings and auth service fields.\nRequires 'updateSpecificProfile' permission. For enneo users, also requires 'enneoAdmin' permission.\n"
      operationId: bulkUpdateSpecificProfiles
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - profiles
              properties:
                profiles:
                  type: array
                  items:
                    type: object
                    allOf:
                      -
                        $ref: '#/components/schemas/MindProfile'
                      -
                        $ref: '#/components/schemas/AuthProfile'
                      -
                        type: object
                        properties:
                          rawData:
                            type: object
                            description: 'Any additional, client-defined custom data to store with the profile'
                          callRoutingStatus:
                            $ref: '#/components/schemas/RoutingStatus'
                          chatRoutingStatus:
                            $ref: '#/components/schemas/RoutingStatus'
      responses:
        '200':
          description: 'Results of bulk update operation'
          content:
            application/json:
              schema:
                type: object
                required:
                  - results
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 1
                        success:
                          type: boolean
                          example: true
        '400':
          description: 'Invalid request format'
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 1
                        success:
                          type: boolean
                          example: false
                        message:
                          type: string
                          example: 'profiles array is required'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
  /profiles/onlineSummary:
    get:
      tags:
        - Profile
      summary: 'Concurrent users + telephony/chat counters (admin polling)'
      description: "Compact summary of users who were active in the last 10 minutes, broken\ndown by user type, plus current live-call and in-queue counters from ACD\nand the number of ongoing chats (open chat tickets with a message in the\nlast 5 minutes).\nUsed by the admin portal for concurrent-user monitoring; replaces the\ndeprecated `?lastSeen=online` summary fast-path on `GET /profiles`.\nRequires `readUserLastSeenDate` or `readAnyUserProfile`.\n"
      operationId: getProfilesOnlineSummary
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  total:
                    type: integer
                    description: 'Total number of users active in the last 10 minutes'
                    example: 42
                  preview:
                    type: array
                    description: 'Up to 10 users sorted by name'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 12
                        name:
                          type: string
                          example: 'Alice Smith'
                  byType:
                    type: object
                    description: 'Count of online users grouped by authData.type'
                    additionalProperties:
                      type: integer
                    example:
                      user: 35
                      enneo: 4
                      serviceWorker: 2
                  lastUserActivity:
                    type: string
                    nullable: true
                    description: "Timestamp of the most recent `lastActivity` across all users\nof type `user` (regardless of the 10-minute online window).\n`null` if no such user exists.\n"
                    example: '2026-05-12 14:32:01'
                  telephony:
                    type: object
                    properties:
                      currentLiveCalls:
                        type: integer
                        example: 3
                      currentInQueue:
                        type: integer
                        example: 7
                  chats:
                    type: object
                    properties:
                      currentOngoing:
                        type: integer
                        description: 'Open chat tickets with a message in the last 5 minutes'
                        example: 2
        '403':
          description: 'Permission denied'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /profiles/routingStatus:
    patch:
      tags:
        - Profile
      summary: 'Bulk-update routing status'
      description: "Update routing status (calls / chats) for multiple profiles in a single call. The body lists\nprofile ids and the routing-status object to apply to each.\n"
      operationId: bulkUpdateRoutingStatus
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                profileIds:
                  type: array
                  items:
                    type: integer
                  description: 'Profiles to update.'
                routingStatus:
                  type: object
                  description: 'Routing-status payload (same shape as the per-profile endpoint).'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Permission denied'
  '/jwt/{id}':
    get:
      tags:
        - Profile
      summary: 'Get a JWT for a user (browser-friendly)'
      description: "Returns a JWT scoped to the given user. The GET form is intended for browser navigation\nduring dev / impersonation flows — `POST` is the canonical form used by tooling.\n"
      operationId: getJwtForProfile
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Profile id to issue the JWT for.'
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                    description: 'JWT scoped to the requested profile.'
        '403':
          description: Unauthorized
        '404':
          description: 'Profile not found'
    post:
      tags:
        - Profile
      summary: 'Get a JWT for a user'
      description: "Returns a JWT scoped to the given user. Use this form from tooling; GET exists for browser\naccess in dev.\n"
      operationId: postJwtForProfile
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Profile id to issue the JWT for.'
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                    description: 'JWT scoped to the requested profile.'
        '403':
          description: Unauthorized
        '404':
          description: 'Profile not found'
  /profile:
    get:
      tags:
        - Profile
      summary: "Get currently logged in user's profile"
      operationId: getProfile
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/MindProfile'
                  -
                    type: object
                    properties:
                      permissions:
                        type: array
                        items:
                          type: string
                        example:
                          - updateTicket
                          - updateIntent
                          - executeIntent
                      openTickets:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: number
                            channel:
                              $ref: '#/components/schemas/Channel'
                            blinking:
                              type: boolean
                            subject:
                              type: string
                        readOnly: true
                        example:
                          -
                            id: 376189
                            channel: email
                            blinking: false
                            subject: Kündigung
                          -
                            id: 659332
                            channel: email
                            blinking: false
                            subject: 'Meine Rechnung'
                          -
                            id: 613771
                            channel: email
                            blinking: false
                            subject: 'Mein Zählerstand'
                      sideConversations:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: number
                            type:
                              type: string
                              description: 'Is the side conversation 1-on-1 with a specific user, or a group?'
                              enum:
                                - individual
                                - group
                            groupId:
                              type: number
                              nullable: true
                            userId:
                              type: integer
                              nullable: true
                        readOnly: true
                        example:
                          -
                            id: 123
                            type: group
                            groupId: 312121
                            userId: null
                          -
                            id: 234
                            type: individual
                            groupId: null
                            userId: 2123
                      unreadNotifications:
                        type: integer
                        readOnly: true
                        example: 1
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - Profile
      summary: 'Create a new user'
      operationId: createSpecificProfile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                -
                  $ref: '#/components/schemas/AuthProfile'
                -
                  $ref: '#/components/schemas/MindProfile'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  id:
                    type: integer
                    example: 1
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /profile/ping:
    get:
      tags:
        - Profile
      summary: 'Ping endpoint to track user activity'
      operationId: pingProfile
      parameters:
        -
          name: ticketId
          in: query
          description: 'ID of the ticket the user has currently opened, if any'
          required: false
          schema:
            type: integer
        -
          name: action
          in: query
          description: 'Any special action the user might have performed, such as closing the browser window/tab or another reason to stop tacking'
          required: false
          schema:
            type: string
            enum:
              - browserClosed
              - stop
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /profile/showAccessToken:
    get:
      tags:
        - Profile
      summary: 'Show the current access token'
      description: 'Returns the access token the user is currently using to authenticate. No specific permissions required, the user just needs to be logged in.'
      operationId: showAccessToken
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  accessToken:
                    type: string
                    description: 'The Bearer/Cookie auth token used for the current request'
                    example: eyJhbGciOiJSUash1Ah1ashja...
        '401':
          description: 'Not authenticated'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /profile/timeTrackingStatusOptions:
    get:
      tags:
        - Profile
      summary: 'Get special time tracking status options'
      description: "Returns the key time tracking status options configured in the system:\n- `active`: First status with autoActiveMode set to true\n- `away`: First status with autoAwayMode set to true\n- `absent`: First status with both allowTicketDelegation and interactionsBlocked set to true, \n  or if none exist, the first status with only allowTicketDelegation set to true\n"
      operationId: getTimeTrackingStatusOptions
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  active:
                    type: string
                    description: 'Status ID for active mode (first status with autoActiveMode=true)'
                    example: active
                  away:
                    type: string
                    description: 'Status ID for away mode (first status with autoAwayMode=true)'
                    example: away
                  absent:
                    type: string
                    description: 'Status ID for long absent mode (first status with allowTicketDelegation=true and interactionsBlocked=true)'
                    example: absent
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /profile/generatePortrait:
    post:
      tags:
        - Profile
      summary: 'Generate a portrait image from a prompt'
      description: "Calls the image-generation backend (Grok via the LiteLLM proxy)\nusing the provided prompt to produce a single 512x512 portrait. The\ngenerated image is fetched into Mind's storage so the response never\nleaks the provider URL. Use `/profile/generatePortraitPrompt` first to\nbuild a polished prompt from descriptive selections.\n"
      operationId: generatePortrait
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - prompt
              properties:
                prompt:
                  type: string
                  description: 'The full image-generation prompt to send to the image API'
                  example: 'A cheerful watercolor portrait of a pixie at the edge of a glowing enchanted forest...'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  portrait:
                    type: object
                    properties:
                      imageUrl:
                        type: string
                        description: 'Internal Mind storage URL for the generated image'
                        example: /api/mind/storage/temp/2026-04-30_a1b2c3d4e5f6g7h8.png
                      imageBase64:
                        type: string
                        description: 'Base64-encoded bytes of the generated image'
                      prompt:
                        type: string
                        description: 'The prompt that was sent to the image API (echo of the request)'
                        example: 'A cheerful watercolor portrait of a pixie...'
        '400':
          description: 'Missing or empty prompt'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /profile/generatePortraitPrompt:
    post:
      tags:
        - Profile
      summary: 'Build a polished portrait prompt from four selections'
      description: "Takes four playful dropdown selections (person, style, mood, setting) and\nuses an LLM to turn them into a polished image-generation prompt. The\nresulting string is intended to be shown in an editable textarea in the\nUI and then submitted to `/profile/generatePortrait`.\n"
      operationId: generatePortraitPrompt
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                person:
                  type: string
                  description: 'Type of person/character'
                  example: pixie
                style:
                  type: string
                  description: 'Visual / artistic style'
                  example: Watercolor
                mood:
                  type: string
                  description: 'Emotional mood / atmosphere'
                  example: cheerful
                setting:
                  type: string
                  description: 'Setting or background'
                  example: 'enchanted forest'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  prompt:
                    type: string
                    description: 'LLM-refined image-generation prompt'
                    example: 'A cheerful watercolor portrait of a pixie at the edge of a glowing enchanted forest...'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/profile/{id}':
    get:
      tags:
        - Profile
      summary: 'Get specific profile'
      operationId: getSpecificProfile
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the profile to retrieve'
          schema:
            type: integer
          example: 1
        -
          name: forceInheritance
          in: query
          required: false
          description: "If true, the profile will inherit the permissions of the user's team, even if the user is not configured to inherit permissions from a team"
          schema:
            type: boolean
            default: false
          example: true
        -
          name: 'teamIds[]'
          in: query
          required: false
          description: 'Only applicable if forceInheritance is true. If provided, the profile will use only specific team IDs for inheritance'
          schema:
            type: array
            items:
              type: integer
          example:
            - 1
            - 2
        -
          name: includeRawData
          in: query
          required: false
          description: 'If true, includes the client-defined custom raw data object stored with the profile in the response'
          schema:
            type: boolean
            default: false
          example: false
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/AuthProfile'
                  -
                    $ref: '#/components/schemas/MindProfile'
                  -
                    type: object
                    properties:
                      lastSeen:
                        type: string
                        format: DateTime
                        description: 'The last time the user was seen online'
                        example: '2021-08-12 12:21:21'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Profile
      summary: 'Update specific profile'
      operationId: updateSpecificProfile
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the profile to retrieve'
          schema:
            type: integer
          example: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                -
                  $ref: '#/components/schemas/AuthProfile'
                -
                  $ref: '#/components/schemas/MindProfile'
                -
                  type: object
                  properties:
                    rawData:
                      type: object
                      description: 'Any additional, client-defined custom data to store with the profile'
                    callRoutingStatus:
                      $ref: '#/components/schemas/RoutingStatus'
                    chatRoutingStatus:
                      $ref: '#/components/schemas/RoutingStatus'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Profile
      summary: 'Delete specific profile'
      operationId: deleteSpecificProfile
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the profile to delete'
          schema:
            type: integer
            example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/profile/{id}/routing':
    get:
      tags:
        - Profile
      summary: 'Explain routing priorities for a user'
      description: "Explain the ticket routing considering the routing priorities. Useful when a user wants to know why they are being pushed a certain ticket.\n\nThe response format is controlled by `limit`:\n\n- **`limit=1` (short format)**: returns `{\"details\":[{\"routingPriority\":<int|string>,\"id\":<int>}]}`.\n  `routingPriority` is an integer when the ticket will be routed to this user, or a localized human-readable string reason when it will not.\n  An empty `details` array means the ticket is not in the routing queue (closed, AI-only, or non-existent).\n\n- **`limit>1` or default (full format)**: returns full routing metadata including `routingPriority` (ordered ticket IDs), `details` (all fields), `total`, and `query`.\n\nPermission: `updateUserProfileSkills` is required, EXCEPT for a single-ticket self-check —\n`limit=1` + numeric `q` (ticket id) + own profile — where the permission check is bypassed.\n"
      operationId: getUserRouting
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the profile to explain routing for'
          schema:
            type: integer
          example: 1
        -
          name: ticketId
          in: query
          required: false
          description: 'If provided, explain routing relative to this ticket ID ("next" button). If omitted, explains routing for the earliest ticket.'
          schema:
            type: integer
        -
          name: id
          in: query
          required: false
          description: "Scope the explain query to a single ticket via an exact `t.id` match. Used by the FE\nself-check on `/ticket/{id}`. Combined with `limit=1` on your own profile this bypasses\nthe `updateUserProfileSkills` permission.\n"
          schema:
            type: integer
        -
          name: q
          in: query
          required: false
          description: 'Full-text search filter across ticket id, contractId, customerId and fulltext fields.'
          schema:
            type: string
        -
          name: limit
          in: query
          required: false
          description: "Maximum number of tickets to return. Defaults to 500.\nWhen set to `1`, activates the short (stripped) response format.\n"
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 500
          example: 500
        -
          $ref: '#/components/parameters/offsetParam'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                oneOf:
                  -
                    description: 'Short format response (limit=1). Contains only details array with routingPriority and id per element. No total or query fields.'
                    type: object
                    required:
                      - details
                    properties:
                      details:
                        type: array
                        items:
                          type: object
                          required:
                            - routingPriority
                            - id
                          properties:
                            routingPriority:
                              oneOf:
                                -
                                  type: integer
                                  description: 'Routing position (1-based). Ticket will be routed to this user.'
                                -
                                  type: string
                                  description: 'Human-readable reason why the ticket will not be routed (e.g. "Colleague assigned", "Missing required skills").'
                            id:
                              type: integer
                              description: 'Ticket ID'
                  -
                    description: 'Full format response (limit>1 or default).'
                    type: object
                    properties:
                      routingPriority:
                        type: array
                        description: 'Ordered list of ticket IDs by routing priority'
                        items:
                          type: integer
                      details:
                        type: array
                        items:
                          type: object
                          properties:
                            routingPriority:
                              oneOf:
                                -
                                  type: integer
                                -
                                  type: string
                            id:
                              type: integer
                            ticketNotAssigned:
                              type: boolean
                            ticketNotWorkedOnByColleague:
                              type: boolean
                            aiSupportLevel:
                              type: string
                              enum:
                                - unprocessed
                                - human
                                - bot
                                - automated
                            priority:
                              $ref: '#/components/schemas/Priority'
                            channel:
                              $ref: '#/components/schemas/Channel'
                            dueBy:
                              type: string
                              format: DateTime
                              nullable: true
                            lastCustomerMessageAt:
                              type: string
                              format: DateTime
                              nullable: true
                            tagExplanation:
                              type: string
                      total:
                        type: integer
                        description: 'Total number of tickets that can be routed for this user'
                      query:
                        type: string
                        description: 'SQL-like query used to compute the routing'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/profile/{id}/image':
    get:
      tags:
        - Profile
      summary: 'Get profile image'
      description: "Returns the profile image for a user. If the user has the permission 'readUserNamesOfNonTeamMates' or the requested user is in the same team, returns the actual image from Auth service. Otherwise returns a stub image."
      operationId: getProfileImage
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the profile to retrieve the image for'
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            'image/*':
              schema:
                type: string
                format: binary
        '404':
          description: 'Image not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/profile/{id}/supersetRoles':
    get:
      tags:
        - Profile
      summary: 'Get available options for analytics roles'
      operationId: getSupersetRoles
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the profile for which the available superset roles should be shown'
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                    label:
                      type: string
                example:
                  -
                    id: none
                    label: 'Kein Reporting-Zugriff'
                  -
                    id: analyticsReadAll
                    label: 'Voller Datenzugriff (lesend)'
                  -
                    id: analyticsEditor
                    label: 'Voller Datenzugriff (schreibend)'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/profile/{id}/routingStatus':
    patch:
      tags:
        - Profile
      summary: 'Update profile routing status'
      description: "Update the routing status for calls and chats for a specific profile. Necessary to manually control which agents should be able to take calls or chats when integrating a custom telephony solution. Requires having 'updateSpecificProfile' permission."
      operationId: updateRoutingStatus
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the profile to update routing status for'
          schema:
            type: integer
          example: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                callRoutingStatus:
                  $ref: '#/components/schemas/RoutingStatus'
                chatRoutingStatus:
                  $ref: '#/components/schemas/RoutingStatus'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: "Unauthorized - user does not have permission to update this profile's routing status"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /team:
    post:
      tags:
        - Team
      summary: 'Create a new team'
      operationId: createTeam
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/team'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /team/tree:
    get:
      tags:
        - Team
      summary: 'Get all teams'
      operationId: getTeams
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/teamTree'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /team/list:
    get:
      tags:
        - Team
      summary: 'Get all teams'
      operationId: getTeamList
      parameters:
        -
          name: roleId
          in: query
          required: false
          description: 'Filter teams by role ID'
          schema:
            type: integer
          example: 1
        -
          name: q
          in: query
          required: false
          description: 'Search query to filter teams by name'
          schema:
            type: string
          example: Support
        -
          name: ids
          in: query
          required: false
          description: 'Array of team IDs to filter by'
          schema:
            type: array
            items:
              type: integer
          example:
            - 1
            - 2
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/teamList'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/team/{id}':
    get:
      tags:
        - Team
      summary: 'Get team by id'
      operationId: getTeamById
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: forceInheritance
          description: 'If true, the team will inherit the permissions of its parent team'
          in: query
          required: false
          schema:
            type: boolean
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/team'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Team
      summary: 'Update a team'
      operationId: updateTeam
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/team'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Team
      summary: 'Delete a team'
      operationId: deleteTeam
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /settings:
    get:
      tags:
        - Settings
      summary: 'Get settings of a client'
      parameters:
        -
          name: filterByName
          in: query
          description: 'Filter by name of the setting'
          required: false
          schema:
            type: string
        -
          name: filterByUsedBy
          in: query
          description: 'Filter by which module uses the setting'
          required: false
          schema:
            type: string
        -
          name: filterByCategory
          in: query
          description: 'Filter by category of the setting'
          required: false
          schema:
            type: string
        -
          name: showSecrets
          in: query
          description: 'Whether to show secrets in the result'
          required: false
          schema:
            type: boolean
      operationId: getSettings
      responses:
        '200':
          description: "List of client-specific configruation settings.\n"
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Settings'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - Settings
      summary: 'Update multiple settings at once'
      description: "Update multiple settings in a single request.\n\n**Special setting formats:**\n- Tag properties: Use `_tag[{id}].{property}` format (e.g., `_tag[123].name`, `_tag[456].visibility`)\n- See Tag schema for available properties\n"
      operationId: updateSettings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'A key-value object with the new settings. Every setting needs to have the unique setting name as key, and the setting value as value.'
              additionalProperties:
                oneOf:
                  -
                    type: string
                  -
                    type: integer
                  -
                    type: array
                  -
                    type: object
                    additionalProperties:
                      type: string
            examples:
              regularSettings:
                value:
                  setting1: some-password
                  setting2: 12
                  setting3:
                    setting-json-prop-1: value1
                    setting-json-prop-2: value2
                summary: 'Regular settings update'
              tagProperties:
                value:
                  '_tag[123].name': 'Customer Support'
                  '_tag[123].visibility': enabled
                  '_tag[456].priority': high
                summary: 'Update multiple tag properties'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /settings/compact:
    get:
      tags:
        - Settings
      summary: 'Get settings of a client and return the results in a compact form'
      parameters:
        -
          name: filterByName
          in: query
          description: 'Filter by name of the setting'
          required: false
          schema:
            type: string
        -
          name: filterByUsedBy
          in: query
          description: 'Filter by which module uses the setting'
          required: false
          schema:
            type: string
        -
          name: filterByCategory
          in: query
          description: 'Filter by category of the setting'
          required: false
          schema:
            type: string
        -
          name: showSecrets
          in: query
          description: 'Whether to show secrets in the result'
          required: false
          schema:
            type: boolean
      operationId: getSettingsCompact
      responses:
        '200':
          description: "List of client-specific configruation settings in a compact key-value format.\n"
          content:
            application/json:
              schema:
                type: object
                description: 'Settings with their values in key-value format'
                additionalProperties:
                  type: string
                  example: settingValue
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/settings/category/{category}':
    get:
      tags:
        - Settings
      summary: 'Get settings of a client in a grouped way for a category'
      parameters:
        -
          name: category
          in: path
          description: 'Filter by category of the setting'
          required: true
          schema:
            type: string
          example: email
      operationId: getSettingsCategory
      responses:
        '200':
          description: 'List of client-specific configruation settings.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  title:
                    type: string
                    example: 'E-Mail Settings'
                  groups:
                    type: array
                    items:
                      type: object
                      description: 'Group of settings.'
                      properties:
                        name:
                          type: string
                          example: 'Generic E-Mail Settings'
                        description:
                          type: string
                          example: 'You can configure your general email settings here'
                        settings:
                          type: array
                          items:
                            $ref: '#/components/schemas/Settings'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /settings/uiOverview:
    get:
      tags:
        - Settings
      summary: 'Get UI overview of settings sections'
      description: "Returns the sections-and-categories tree that powers the Settings landing page in ops-fe.\nThe response is filtered by the caller's permissions: categories whose `requiredPermissions`\n/ `requiredPermissionsAny` don't match are omitted. Labels are translated to the caller's\nlocale.\n"
      operationId: getSettingsUiOverview
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  sections:
                    type: array
                    items:
                      type: object
                      properties:
                        label:
                          type: string
                        description:
                          type: string
                        categories:
                          type: array
                          items:
                            type: object
        '403':
          description: 'Unauthorized — missing `settingsUiOverview` permission'
  '/settings/{name}':
    get:
      tags:
        - Settings
      summary: 'Get a single setting by name'
      operationId: getSetting
      parameters:
        -
          name: name
          in: path
          required: true
          description: 'The name of the setting to retrieve'
          schema:
            type: string
          example: lastAgentRouting
        -
          name: showSecrets
          in: query
          description: 'Whether to show secrets in the result'
          required: false
          schema:
            type: boolean
      responses:
        '200':
          description: 'The requested setting object.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Settings'
        '403':
          description: Unauthorized
        '404':
          description: 'Setting not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      tags:
        - Settings
      summary: 'Update setting'
      description: "Update a single setting value.\n\n**Special setting formats:**\n- Tag properties: Use `_tag[{id}].{property}` format (e.g., `_tag[123].name`, `_tag[123].visibility`)\n- See Tag schema for available properties\n"
      operationId: updateSetting
      parameters:
        -
          name: name
          in: path
          required: true
          description: 'The name of the setting to update'
          schema:
            type: string
          examples:
            regularSetting:
              value: genericTemplateId
              summary: 'Regular setting'
            tagProperty:
              value: '_tag[123].name'
              summary: 'Tag property (update tag name)'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
            examples:
              regularSetting:
                value:
                  toleranceGas: 800
                  toleranceElectricity: 300
                summary: 'Update regular setting with JSON object'
              tagName:
                value: 'Customer Support'
                summary: 'Update tag name (when name is _tag[123].name)'
              tagVisibility:
                value: enabled
                summary: 'Update tag visibility (when name is _tag[123].visibility)'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /settings/search:
    get:
      tags:
        - Settings
      summary: 'Search settings'
      description: 'Search through settings by name, description, or category'
      operationId: searchSettings
      parameters:
        -
          name: q
          in: query
          required: true
          description: 'The search term to filter settings by'
          schema:
            type: string
          example: 'last agent routing'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SearchResult'
        '400':
          description: 'Bad request - No search term provided'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /settings/subchannel:
    get:
      tags:
        - Settings
      summary: 'Get Subchannels'
      description: "A subchannel is a further differentiation of the channel, and is - For emails, they are the mailboxes, e.g. support@enneo.ai and sales@enneo.ai - For chat, they are the chatbots, e.g. the chatbot on the website and the chatbot on the customer portal - For voice, they are the phone numbers, e.g. the phone number for support and the phone number for sales.\n"
      operationId: getSubchannels
      parameters:
        -
          name: channels
          in: query
          required: false
          description: "Filter by one or more channels. Accepts array format (?channels[]=email&channels[]=chat). Use 'all' to get subchannels from all channels (?channels[]=all). For backward compatibility, single channel parameter (?channel=email) is also supported."
          schema:
            type: array
            items:
              type: string
              enum:
                - all
                - email
                - chat
                - voice
                - phone
                - letter
                - internal
          style: form
          explode: true
        -
          name: channel
          in: query
          required: false
          deprecated: true
          description: "(Deprecated) Filter by a single channel. Use 'channels' parameter instead for better flexibility. Use 'all' to get subchannels from all channels."
          schema:
            type: string
            enum:
              - all
              - email
              - chat
              - voice
              - phone
              - letter
              - internal
        -
          name: id
          in: query
          required: false
          description: 'Filter by a specific subchannel ID'
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      subchannels:
                        type: array
                        items:
                          $ref: '#/components/schemas/Subchannel'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      summary: 'Add a new Subchannel'
      tags:
        - Settings
      operationId: createSubchannel
      requestBody:
        description: 'The new Subchannel that should be created. The ID does not need to be included in the payload, as it will be generated automatically.'
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Subchannel'
      responses:
        '200':
          description: 'Subchannel added successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: 'Indicates if the operation was successful'
                    example: true
                  id:
                    type: integer
                    description: 'The ID of the added Subchannel'
                    example: 12345
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/settings/subchannel/{id}':
    delete:
      summary: 'Delete a Subchannel'
      tags:
        - Settings
      operationId: deleteSubchannel
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Subchannel deleted successfully'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /settings/user-defined-function:
    get:
      tags:
        - Settings
      summary: 'Get User Defined Functions'
      description: "A user-defined function (UDF) is a function that you can define yourself and use in your queries. It is a way to extend the functionality of the database.\n"
      operationId: getUserDefinedFunctions
      parameters:
        -
          name: filter
          in: query
          required: false
          description: "Filter by type of user defined function. If 'tool', only tool functions are returned. If 'udf', only non-tool functions are returned."
          schema:
            type: string
            enum:
              - tool
              - udf
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      userDefinedFunctions:
                        type: array
                        items:
                          $ref: '#/components/schemas/UserDefinedFunction'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      summary: 'Add a new User Defined Function'
      tags:
        - Settings
      operationId: createUserDefinedFunction
      requestBody:
        description: 'The new User Defined Function that should be created. The ID does not need to be included in the payload, as it will be generated automatically.'
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserDefinedFunction'
      responses:
        '200':
          description: 'User Defined Function added successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: 'Indicates if the operation was successful'
                    example: true
                  id:
                    type: integer
                    description: 'The ID of the added User Defined Function'
                    example: 12345
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/settings/user-defined-function/{id}':
    delete:
      summary: 'Delete a User Defined Function'
      tags:
        - Settings
      operationId: deleteUserDefinedFunction
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'User Defined Function deleted successfully'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /settings/event-hook:
    get:
      summary: 'List event hooks'
      description: 'Returns all event hooks (outbound webhooks fired on Mind events) configured for this client.'
      tags:
        - Settings
      operationId: listEventHooks
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  eventHooks:
                    type: array
                    items:
                      type: object
    post:
      summary: 'Create an event hook'
      description: 'Create a new outbound event hook bound to one or more Mind events.'
      tags:
        - Settings
      operationId: createEventHook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'Event hook payload — event types, target URL, headers/secret.'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  eventHook:
                    type: object
        '403':
          description: Unauthorized
  '/settings/event-hook/{id}':
    delete:
      summary: 'Delete an event hook'
      tags:
        - Settings
      operationId: deleteEventHook
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          example: 12
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '404':
          description: 'Event hook not found'
  /settings/mailbox/microsoft/authorization:
    get:
      summary: 'Redirect to Microsoft authorization page'
      tags:
        - Settings
      operationId: microsoftAuthorization
      responses:
        '200':
          description: 'Operation successful'
        '302':
          description: 'Redirect to Microsoft authorization page'
        '400':
          description: 'Bad request'
        '403':
          description: Unauthorized
  /settings/mailbox/microsoft/authorization/callback:
    get:
      summary: 'Callback for Microsoft authorization'
      tags:
        - Settings
      operationId: microsoftAuthorizationCallback
      responses:
        '200':
          description: 'Operation successful'
        '302':
          description: 'Redirect to /success page'
        '400':
          description: 'Bad request'
        '403':
          description: Unauthorized
  /settings/mailbox/microsoft/graph/authorization:
    get:
      summary: 'Redirect to Microsoft Graph authorization page'
      description: "Starts the OAuth flow for the modern Microsoft Graph mailbox integration (`/api/mind/settings/mailbox/microsoft/graph/...`).\nThe user is redirected to Microsoft to grant Graph mailbox scopes; on success Microsoft calls\nback to `microsoftGraphCallback`.\n"
      tags:
        - Settings
      operationId: microsoftGraphAuthorization
      responses:
        '302':
          description: 'Redirect to Microsoft authorization page'
        '400':
          description: 'Bad request'
        '403':
          description: Unauthorized
  /settings/mailbox/microsoft/graph/callback:
    get:
      summary: 'Callback for Microsoft Graph authorization'
      description: 'Microsoft Graph OAuth callback. Persists the issued tokens against the mailbox subchannel.'
      tags:
        - Settings
      operationId: microsoftGraphCallback
      parameters:
        -
          name: code
          in: query
          required: false
          description: 'Authorization code returned by Microsoft.'
          schema:
            type: string
        -
          name: state
          in: query
          required: false
          description: 'Opaque state set during the authorization request.'
          schema:
            type: string
      responses:
        '302':
          description: 'Redirect to the post-callback success page'
        '400':
          description: 'Bad request — missing or invalid code'
        '403':
          description: Unauthorized
  /settings/mailbox/google/authorization:
    get:
      summary: 'Redirect to Google authorization page'
      description: 'Starts the OAuth flow for the Google (Gmail) mailbox integration.'
      tags:
        - Settings
      operationId: googleAuthorization
      responses:
        '302':
          description: 'Redirect to Google authorization page'
        '400':
          description: 'Bad request'
        '403':
          description: Unauthorized
  /settings/mailbox/google/authorization/callback:
    get:
      summary: 'Callback for Google authorization'
      description: 'Google OAuth callback. Persists the issued tokens against the mailbox subchannel.'
      tags:
        - Settings
      operationId: googleAuthorizationCallback
      parameters:
        -
          name: code
          in: query
          required: false
          description: 'Authorization code returned by Google.'
          schema:
            type: string
        -
          name: state
          in: query
          required: false
          description: 'Opaque state set during the authorization request.'
          schema:
            type: string
      responses:
        '302':
          description: 'Redirect to the post-callback success page'
        '400':
          description: 'Bad request'
        '403':
          description: Unauthorized
  '/settings/mailbox/{subchannelId}/test/receive':
    get:
      summary: 'Test receive on a mailbox subchannel'
      description: "Triggers a one-off mailbox fetch for the given subchannel to verify that authentication and\nmailbox connectivity are working. Returns the count and metadata of messages that would be\npulled, but does not persist them as tickets.\n"
      tags:
        - Settings
      operationId: testReceiveMailbox
      parameters:
        -
          name: subchannelId
          in: path
          required: true
          description: 'ID of the mailbox subchannel to test.'
          schema:
            type: integer
          example: 42
      responses:
        '200':
          description: 'Operation successful'
          content:
            application/json:
              schema:
                type: object
        '400':
          description: 'Bad request'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
  /tag:
    get:
      tags:
        - Tag
      summary: 'List all tags'
      description: 'Get all tags (skills, products, brands) available'
      operationId: listAllTags
      parameters:
        -
          name: q
          in: query
          description: 'Search for tags matching this search string'
          required: false
          schema:
            type: string
        -
          name: type
          in: query
          description: 'Show only tags of a specific type. If not set, shows all tags.'
          required: false
          schema:
            type: string
            description: 'Filter by tag type'
            enum:
              - skill
              - product
              - brand
              - customerProperty
              - contractProperty
              - other
        -
          name: includeDisabled
          in: query
          description: 'Also include disabled intents'
          required: false
          schema:
            type: boolean
        -
          name: format
          in: query
          description: 'Level of detail of output. Defaults to full'
          required: false
          schema:
            type: string
            description: Format
            enum:
              - compact
              - full
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      tags:
                        type: array
                        items:
                          $ref: '#/components/schemas/Tag'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      summary: 'Add a new tag'
      tags:
        - Tag
      operationId: createTag
      requestBody:
        description: 'The new tag that should be created. The only mandatory parameters are name, refernce and type. If the other parameters are not provided, they will revert to defaults.'
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Tag'
      responses:
        '200':
          description: 'Tag added successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: 'Indicates if the operation was successful'
                    example: true
                  id:
                    type: integer
                    description: 'The ID of the added Tag'
                    example: 12345
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /tag/tree:
    get:
      tags:
        - Tag
      summary: 'Get tag tree structure'
      description: 'Retrieve a hierarchical tree structure of all tags'
      operationId: getTagTree
      parameters:
        -
          name: type
          in: query
          description: 'Show only tags of a specific type. If not set, shows all tags.'
          required: false
          schema:
            type: string
            description: 'Filter by tag type'
            enum:
              - skill
              - product
              - brand
              - customerProperty
              - contractProperty
              - other
        -
          name: includeDisabled
          in: query
          description: 'Also include disabled tags'
          required: false
          schema:
            type: boolean
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TagTree'
        '403':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/tag/{id}':
    get:
      summary: 'Get a Tag'
      tags:
        - Tag
      operationId: getTag
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tag'
        '403':
          description: Unauthorized
        '404':
          description: 'Tag not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      summary: 'Delete a Tag'
      tags:
        - Tag
      operationId: deleteTag
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Tag deleted successfully'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/tag/{id}/detect':
    post:
      summary: 'Detect a Tag'
      tags:
        - Tag
      operationId: detectTag
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketId:
                  type: integer
                  description: 'The id of the ticket against which to compare the intent examples'
                  example: 1
                testTags:
                  type: array
                  description: 'The tags to test against'
                  items:
                    $ref: '#/components/schemas/Tag'
                data:
                  type: object
                  description: 'Additional data to be used for the detection'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  match:
                    type: boolean
                    description: 'Indicates if the tag was detected'
                    example: true
                  dataOutcome:
                    type: object
                    description: 'The outcome of the data preview'
        '403':
          description: Unauthorized
        '404':
          description: 'Tag not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /roles:
    get:
      tags:
        - Role
      summary: 'Get roles'
      operationId: getRoles
      parameters:
        -
          name: q
          in: query
          required: false
          description: 'Search term to filter roles by name or description'
          schema:
            type: string
          example: admin
        -
          name: baseRoleId
          in: query
          required: false
          description: 'Filter roles by base role ID'
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      roles:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              example: 1
                            name:
                              type: string
                              example: Agent
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      summary: 'Add a new role'
      tags:
        - Role
      operationId: createRole
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                -
                  $ref: '#/components/schemas/Role'
                -
                  type: object
                  properties:
                    permissions:
                      type: array
                      description: 'The permissions to assign to the new role'
                      items:
                        type: string
                        description: 'The name of the permission to assign to the new role'
                        example: updateClientSettings
      responses:
        '200':
          description: 'Role added successfully'
  '/roles/{id}':
    get:
      tags:
        - Role
      summary: 'Get role by id'
      operationId: getRole
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Role'
        '403':
          description: Unauthorized
        '404':
          description: 'Role not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      summary: 'Update a role'
      tags:
        - Role
      operationId: updateRole
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Role'
      responses:
        '200':
          description: 'Role updated successfully'
        '403':
          description: Unauthorized
        '404':
          description: 'Role not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      summary: 'Delete a role'
      tags:
        - Role
      operationId: deleteRole
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Role deleted successfully'
        '403':
          description: Unauthorized
        '404':
          description: 'Role not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /customer/search:
    get:
      tags:
        - Customer
      summary: 'Search a customer'
      description: 'Search a customer by firstname, lastname contractId'
      operationId: searchCustomer
      parameters:
        -
          name: q
          in: query
          required: true
          description: 'The keyword to search'
          schema:
            type: string
          example: Richard
        -
          $ref: '#/components/parameters/limitParam'
        -
          $ref: '#/components/parameters/offsetParam'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                    firstname:
                      type: string
                    lastname:
                      type: string
                    address:
                      type: string
                    phone:
                      type: string
                    email:
                      type: string
                    contractIds:
                      type: array
                      items:
                        type: string
              example:
                -
                  id: 826907
                  firstname: Richard
                  lastname: Lohwasser
                  address: 'Hauptstraße 1, 12345 Berlin'
                  phone: 49123456789
                  email: richard@lohwasser.de
                  contractIds:
                    - '715559'
        '403':
          description: Unauthorized
        '404':
          description: 'Customer not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/customer/byCustomerId/{customerId}':
    get:
      tags:
        - Customer
      summary: 'Get a customer by customer id'
      description: 'Get a customer and associated contracts'
      operationId: getCustomerByCustomerId
      parameters:
        -
          name: customerId
          in: path
          required: true
          description: 'The id of the customer to retrieve'
          schema:
            type: string
          example: '83771'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
        '403':
          description: Unauthorized
        '404':
          description: 'Customer not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/customer/byContractId/{contractId}':
    get:
      tags:
        - Customer
      summary: 'Get a customer by contract id'
      description: 'Get a customer and associated contracts'
      operationId: getCustomerByContractId
      parameters:
        -
          name: contractId
          in: path
          required: true
          description: 'The id of the contract for which the associated customer is requested'
          schema:
            type: string
          example: '746839'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
        '403':
          description: Unauthorized
        '404':
          description: 'Contract not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/customer/byTicketId/{ticketId}':
    get:
      tags:
        - Customer
      summary: 'Get a customer by ticket id'
      description: "Get a customer and associated contracts by ticket id. Tickets are mapped to customer using enneo.MIND's AI, or when manually assigned by a user"
      operationId: getCustomerByTicketId
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket for which the associated customer is requested'
          schema:
            type: integer
          example: 376189
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
        '403':
          description: Unauthorized
        '404':
          description: 'No customer id associated with ticket (yet)'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/customer/{customerId}':
    patch:
      tags:
        - Customer
      summary: 'Get a customer by customer id'
      description: 'Get a customer and associated contracts'
      operationId: updateCustomerByCustomerId
      parameters:
        -
          name: customerId
          in: path
          required: true
          description: 'The id of the customer to retrieve'
          schema:
            type: string
          example: '83771'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Customer'
        '403':
          description: Unauthorized
        '404':
          description: 'No customer id associated with ticket (yet)'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /customer/invalidateCache:
    post:
      summary: 'Invalidate cache for a contract or customer'
      description: 'This endpoint invalidates the cache for a given contract or customer by their IDs.'
      operationId: invalidateCache
      tags:
        - Customer
      requestBody:
        description: 'Contract or Customer ID to invalidate cache for.'
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                contractId:
                  type: string
                  description: 'The ID of the contract to invalidate cache for.'
                  nullable: true
                customerId:
                  type: string
                  description: 'The ID of the customer to invalidate cache for.'
                  nullable: true
            examples:
              example1:
                value:
                  contractId: '1234'
                  customerId: '5678'
      responses:
        '200':
          description: 'Cache invalidated successfully.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  contractCacheInvalidated:
                    type: boolean
                    description: 'Indicates if the contract cache was invalidated.'
                  customerCacheInvalidated:
                    type: boolean
                    description: 'Indicates if the customer cache was invalidated.'
        '403':
          description: Unauthorized
        '404':
          description: 'Customer not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/contract/{contractId}':
    get:
      tags:
        - Contract
      summary: 'Get contract by id'
      operationId: getContractById
      parameters:
        -
          name: contractId
          in: path
          required: true
          description: 'The id of the contract to retrieve'
          schema:
            type: string
          example: '123'
        -
          name: includeRawData
          in: query
          required: false
          description: 'If set to true, then the raw response from the ERP system will be included'
          schema:
            type: boolean
          example: false
        -
          name: refresh
          in: query
          required: false
          description: 'If set to true, then enneo will fetch the data from the underlying ERP system first'
          schema:
            type: boolean
          example: false
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '403':
          description: Unauthorized
        '404':
          description: 'Contract not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/contract/{contractId}/history':
    get:
      tags:
        - Contract
      summary: 'Get ticket history for a contract'
      description: 'Returns the list of tickets ever opened against the given contract, oldest first.'
      operationId: getContractHistory
      parameters:
        -
          name: contractId
          in: path
          required: true
          description: 'Contract id.'
          schema:
            type: string
          example: '123'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  tickets:
                    type: array
                    items:
                      $ref: '#/components/schemas/Ticket'
        '404':
          description: 'Contract not found'
  /contract/search:
    get:
      tags:
        - Contract
      summary: 'Search for contracts'
      operationId: searchContracts
      parameters:
        -
          name: contractId
          in: query
          description: 'The ID of the contract, e.g. 829211'
          required: false
          schema:
            type: string
        -
          name: customerId
          in: query
          description: 'The ID of the customer, e.g. 101020120'
          required: false
          schema:
            type: string
        -
          name: firstname
          in: query
          description: 'The first name to search for, e.g. John'
          required: false
          schema:
            type: string
        -
          name: lastname
          in: query
          description: 'The last name to search for, e.g. Smith'
          required: false
          schema:
            type: string
        -
          name: fullname
          in: query
          description: 'The full name to search for, e.g. John Smith'
          required: false
          schema:
            type: string
        -
          name: birthday
          in: query
          description: 'The birthday to search for, e.g. 1980-01-01'
          required: false
          schema:
            type: string
        -
          name: company
          in: query
          description: 'The company to search for, e.g. enneo GmbH'
          required: false
          schema:
            type: string
        -
          name: email
          in: query
          description: 'The email to search for, e.g. john@smith.com'
          required: false
          schema:
            type: string
        -
          name: meterNumber
          in: query
          description: 'The meter number to search for, e.g. 1ESY1160669167'
          required: false
          schema:
            type: string
        -
          name: address
          in: query
          description: 'The address to search for. Format is e.g. Eppendorfer Landstr. 32, 20249 Hamburg'
          required: false
          schema:
            type: string
        -
          name: postalCode
          in: query
          description: 'The postal code to search for, e.g. 20249'
          required: false
          schema:
            type: string
        -
          name: city
          in: query
          description: 'The city to search for, e.g. Hamburg'
          required: false
          schema:
            type: string
        -
          name: includeRawData
          in: query
          description: 'If yes, then the raw source data coming from the ERP is included in the response'
          required: false
          example: false
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: 'An array of the search results, sorted by similarity. The best match is always first.'
                  properties:
                    id:
                      type: integer
                    similarity:
                      type: number
                      description: 'How well the contract fits to the search parameters. Exact match on contractId always returns 1 (=100%)'
                      format: float
                    contract:
                      $ref: '#/components/schemas/Contract'
        '400':
          description: 'Bad request'
  /contract/legitimation/preview:
    post:
      tags:
        - Contract
      summary: 'Dry-run customer legitimation check'
      description: "Evaluates the configured legitimation rules (`customerLegitimationRuleAsyncChannels` /\n`customerLegitimationRuleSyncChannels`) against a synthetic ticket built from the supplied\nfields. No data is written. Useful for testing legitimation settings without a real ticket.\n"
      operationId: contractLegitimationPreview
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - channel
              properties:
                channel:
                  type: string
                  description: 'Ticket channel. Determines which rule set is used (sync: chat/phone, async: all others).'
                  example: email
                contractId:
                  type: string
                  description: 'Optional. If provided, the real contract is loaded from the ERP and used for criteria evaluation.'
                  example: '715559'
                customerId:
                  type: string
                  description: 'Optional override for the customer ID on the loaded contract.'
                  example: C-987
                from:
                  type: string
                  description: 'Sender address (email or phone number).'
                  example: kunde@example.com
                subject:
                  type: string
                  description: 'Ticket subject line.'
                  example: 'Question about my contract'
                bodyPlain:
                  type: string
                  description: 'Plain-text ticket body.'
                  example: "My contract number is 715559 and I'd like to change my address."
                rawData:
                  type: object
                  description: 'Synthetic ticket.rawData JSON object — same shape as the raw payload Mind writes to the ticket table on incoming tickets.'
                  additionalProperties: true
                subchannelId:
                  type: string
                  description: 'Optional subchannel ID. If the subchannel has `assistantAuthenticationInstructions`, the deprecated path is used.'
                  example: '3'
      responses:
        '200':
          description: 'Legitimation check result'
          content:
            application/json:
              schema:
                type: object
                properties:
                  legitimationLevel:
                    type: integer
                    description: '0=no customer, 10=identified but not legitimized, 20=legitimized'
                    example: 10
                  legitimationMessage:
                    type: string
                    nullable: true
                    description: 'Human-readable explanation of why legitimation failed. null on level 0 and 20.'
                    example: '<b>Group 1</b>: needed 2, got 1 criteria.'
                  detectionLog:
                    type: array
                    items:
                      type: string
                    description: 'Step-by-step evaluation log for debugging.'
        '400':
          description: 'Missing required field'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Unauthorized
        '404':
          description: 'Contract not found'
  /knowledgeSource:
    get:
      tags:
        - 'Wiki knowledge source'
      summary: 'Get knowledgeSource'
      operationId: getKnowledgeSource
      description: "Returns a list of knowledge sources. **Excluded website pages** (whose source URL matches\nthe parent connector's `sourceConfig.excludePaths`) are filtered out of the response —\nthis is required so Cortex's `sync_knowledge_base` does not re-index excluded pages.\n\n**Folder listing** (`parent` present): paginated. `limit` defaults to 50, maximum 500.\nResponse includes `total`, `offset`, and `limit` for cursor-free pagination.\n\n**Global / search** (`parent` absent, or `q` present): not paginated. `limit` defaults\nto 1000 and is not capped. `total`/`offset`/`limit` are not returned.\n"
      parameters:
        -
          name: limit
          in: query
          required: false
          description: "Maximum number of items to return. When `parent` is present (folder listing),\ndefaults to 50 and is capped at 500. When `parent` is absent, defaults to 1000\nwith no server-side cap.\n"
          schema:
            type: integer
            minimum: 1
            default: 50
        -
          name: offset
          in: query
          required: false
          description: 'Number of items to skip (zero-based). Only meaningful with `parent`.'
          schema:
            type: integer
            minimum: 0
            default: 0
        -
          name: parent
          in: query
          required: false
          description: 'The parent of the knowledgeSource to filter by'
          schema:
            type: integer
        -
          name: q
          in: query
          required: false
          description: 'The search query to filter by. When specified other parameters are ignored'
          schema:
            type: string
          example: 'Wie kann ich kündigen?'
        -
          name: modifiedAfter
          in: query
          required: false
          description: 'Filter knowledge sources modified on or after this date. Must be in MySQL datetime format (Y-m-d H:i:s)'
          schema:
            type: string
            format: date-time
          example: '2025-10-08 12:30:00'
        -
          name: orderByField
          in: query
          required: false
          description: 'Field to sort by. Applied to all list results, including fulltext search results.'
          schema:
            type: string
            enum:
              - name
              - views
              - modifiedAt
              - createdAt
            default: name
        -
          name: orderByDirection
          in: query
          required: false
          description: "Sort direction applied to `orderByField`. Defaults to `asc`; for non-name fields\n(`views`, `modifiedAt`, `createdAt`) callers typically want `desc`\n(most popular / most recent first) and should pass it explicitly.\n"
          schema:
            type: string
            enum:
              - asc
              - desc
            default: asc
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    description: 'The name of the parent'
                    nullable: true
                  title:
                    type: string
                    description: 'The title of the parent'
                    nullable: true
                  answer:
                    type: string
                    description: 'The answer to the search query'
                    nullable: true
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/KnowledgeSource'
                  total:
                    type: integer
                    description: "Total number of visible items in the folder (after ACL filtering).\nPresent only when `parent` is specified. Use with `limit`/`offset`\nto render pagination controls.\n"
                    example: 42
                  offset:
                    type: integer
                    description: 'The offset used for this page. Present only when `parent` is specified.'
                    example: 0
                  limit:
                    type: integer
                    description: 'The limit used for this page. Present only when `parent` is specified.'
                    example: 50
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - 'Wiki knowledge source'
      summary: 'Create knowledgeSource'
      operationId: createKnowledgeSource
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KnowledgeSource'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    $ref: '#/components/schemas/KnowledgeSource'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /knowledgeSource/dashboard:
    get:
      tags:
        - 'Knowledge Sources'
      summary: 'Get knowledge source dashboard'
      description: "Returns aggregated KPIs for the knowledge base: total entries per type/structure node,\nprocessing status, and recent ingestion errors. Used to power the dashboard widget on the\nKnowledge Sources landing page.\n"
      operationId: getKnowledgeSourceDashboard
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '403':
          description: Unauthorized
  /knowledgeSource/archived:
    get:
      summary: 'Get archived knowledge sources'
      description: 'Retrieves a paginated list of archived knowledge sources'
      operationId: getArchivedKnowledgeSources
      tags:
        - 'Wiki knowledge source'
      parameters:
        -
          $ref: '#/components/parameters/limitParam'
        -
          $ref: '#/components/parameters/offsetParam'
        -
          in: query
          name: orderByDirection
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
          description: 'Sort direction by modifiedAt'
        -
          in: query
          name: parent
          schema:
            type: integer
          description: 'Filter by parent (connector/group) id'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      allOf:
                        -
                          $ref: '#/components/schemas/KnowledgeSource'
                        -
                          type: object
                          properties:
                            status:
                              type: string
                              enum:
                                - archived
                              default: archived
                  total:
                    type: integer
                    description: 'Total number of archived knowledge sources (filtered)'
                  offset:
                    type: integer
                  limit:
                    type: integer
        '401':
          description: Unauthorized
        '403':
          description: "Forbidden - User doesn't have the 'viewArchivedKnowledge' permission"
  '/knowledgeSource/{id}':
    get:
      tags:
        - 'Wiki knowledge source'
      summary: 'Get knowledgeSource by id'
      operationId: getKnowledgeSourceById
      description: "Returns a single knowledge source. **Excluded website pages** (whose source URL matches the\nparent connector's `sourceConfig.excludePaths`) return `404 Not Found` — this matches the\nbulk list filter so Cortex sync stays consistent.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the knowledgeSource to retrieve'
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KnowledgeSource'
        '404':
          description: 'Not found, or the page is an excluded website page'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - 'Wiki knowledge source'
      summary: 'Update knowledgeSource'
      operationId: updateKnowledgeSource
      description: "For file-type rows (uploaded via the files connector) only `name`, `status`, and `text`\nare accepted — other fields return 422. Editing `text` sets `data.hasManualMarkdownEdits = true`\nso the FE can warn before a re-upload would overwrite manual edits. The `embeddingHash`\nis intentionally NOT recomputed on text-only edits — it represents the parse hash of the\noriginal binary and stays stable until a real reindex / replace.\n\nWebsite-type pages cannot be edited through this endpoint.\n\n**Teams (per-article ACL):** sending `teams: [1,2]` replaces the full team-restriction set;\nomitting `teams` entirely leaves the existing set unchanged. An empty array (`teams: []`)\nremoves all restrictions and makes the article visible to everyone.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the knowledgeSource to retrieve'
          schema:
            type: integer
          example: 123
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KnowledgeSource'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    $ref: '#/components/schemas/KnowledgeSource'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - 'Wiki knowledge source'
      summary: 'Delete knowledgeSource'
      operationId: deleteKnowledgeSource
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'The id of the knowledgeSource to retrieve'
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /knowledgeSourceStructure:
    get:
      tags:
        - 'Wiki knowledge source structure'
      summary: 'Get the knowledge source structure'
      operationId: getKnowledgeSourceStructure
      description: "Returns the knowledge source structure tree. By default, website tree nodes whose `source` URL\nmatches the parent connector's `sourceConfig.excludePaths` (`fnmatch` patterns) are omitted —\nthis is the sidebar view. Pass `withExcluded=true` to include those nodes (each website\nnode then carries `excluded: true|false`); used by the connector configuration view.\n"
      parameters:
        -
          name: withExcluded
          in: query
          required: false
          description: "When true, include website tree nodes whose source matches the parent connector's\nexcludePaths and tag each website node with `excluded: true|false`. When false (default)\nor omitted, those nodes are dropped from the response. Truthy values: `true`, `1`, `yes`, `on`.\n"
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/knowledgeSourceStructure'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - 'Wiki knowledge source structure'
      summary: 'Create a new knowledge source structure'
      operationId: createKnowledgeSourceStructure
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/knowledgeSourceStructure'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/knowledgeSourceStructure'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSourceStructure/{id}':
    put:
      tags:
        - 'Wiki knowledge source structure'
      summary: 'Update a knowledge source structure'
      operationId: updateKnowledgeSourceStructure
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/knowledgeSourceStructure'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - 'Wiki knowledge source structure'
      summary: 'Delete a knowledge source structure'
      operationId: deleteKnowledgeSourceStructure
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSourceStructure/{id}/teams':
    put:
      tags:
        - 'Wiki knowledge source structure'
      summary: 'Set team ACL for a structure node'
      operationId: setKnowledgeSourceStructureTeams
      description: "Replace-set the team access list for a structure node (folder, file-connector root,\nor website connector — any type). Passing an empty array removes all restrictions\nand makes the node visible to everyone. When non-empty, only users whose\n`actualTeamIds` intersect with this list (or users with no teams at all) can see\nthe node and all its descendants.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - teams
              properties:
                teams:
                  type: array
                  items:
                    type: integer
                  description: 'Team IDs to allow. Empty array removes all restrictions.'
                  example:
                    - 1
                    - 5
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '404':
          description: 'Structure node not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /knowledgeSource/websiteConnector:
    get:
      tags:
        - 'Website connector'
      summary: 'List website connectors'
      operationId: getWebsiteConnectors
      description: 'Returns all website connectors (excluding deleted ones). Each connector includes its page count and data (with webhookSecret removed).'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WebsiteConnector'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - 'Website connector'
      summary: 'Create a website connector'
      operationId: createWebsiteConnector
      description: 'Creates a new website connector with the given URL and configuration. Generates a webhook secret automatically.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - url
              properties:
                url:
                  type: string
                  description: 'The URL to crawl'
                  example: 'https://example.com'
                name:
                  type: string
                  description: 'Display name for the connector. Defaults to the hostname of the URL.'
                  example: 'Example Website'
                parent:
                  type: integer
                  description: 'Parent folder ID in knowledge source structure'
                  example: 0
                maxPages:
                  type: integer
                  description: 'Maximum number of pages to crawl'
                  default: 100
                  example: 50
                frequency:
                  type: string
                  description: 'Crawl frequency'
                  enum:
                    - daily
                    - weekly
                  default: weekly
                description:
                  type: string
                  description: 'Description of the connector'
                includePaths:
                  type: array
                  items:
                    type: string
                  description: 'URL path patterns to include'
                excludePaths:
                  type: array
                  items:
                    type: string
                  description: 'URL path patterns to exclude'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  id:
                    type: integer
                    description: 'ID of the created connector'
                    example: 42
        '422':
          description: 'Validation error (missing URL or unconfigured webhook URL)'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/websiteConnector/{id}':
    get:
      tags:
        - 'Website connector'
      summary: 'Get a website connector'
      operationId: getWebsiteConnector
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebsiteConnector'
        '404':
          description: 'Connector not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - 'Website connector'
      summary: 'Update a website connector'
      operationId: updateWebsiteConnector
      description: "Updates a website connector. The `excludePaths` field has three mutually compatible inputs:\n  - `excludePaths` — full replace (operator-edit form).\n  - `addExcludePaths` — append to the current excludePaths (set-union, deduped server-side).\n  - `removeExcludePaths` — remove from the current excludePaths (set-difference).\n\n`excludePaths` is **mutually exclusive** with `addExcludePaths`/`removeExcludePaths` —\nsending both in the same request returns 422. `addExcludePaths` and `removeExcludePaths`\ncan be combined in one request (add applied first, then remove).\n\nWhenever excludePaths actually changes, Mind diffs old-vs-new and emits\n`knowledgeSourceChanged` events: `delete` for newly-matching pages (now excluded),\n`create` for newly-unmatching pages (now included). This keeps Cortex's Weaviate index\nin sync immediately rather than waiting for the next periodic resync.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                maxPages:
                  type: integer
                includePaths:
                  type: array
                  items:
                    type: string
                excludePaths:
                  type: array
                  description: "Full replace of the connector's excludePaths. Mutually exclusive with addExcludePaths/removeExcludePaths."
                  items:
                    type: string
                addExcludePaths:
                  type: array
                  description: 'Paths to append to the current excludePaths (set-union, deduped). Mutually exclusive with `excludePaths`.'
                  items:
                    type: string
                removeExcludePaths:
                  type: array
                  description: 'Paths to remove from the current excludePaths (set-difference). Mutually exclusive with `excludePaths`.'
                  items:
                    type: string
                scheduleEnabled:
                  type: boolean
                frequency:
                  type: string
                  enum:
                    - daily
                    - weekly
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '404':
          description: 'Connector not found'
        '422':
          description: 'Invalid frequency value, or excludePaths combined with addExcludePaths/removeExcludePaths'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - 'Website connector'
      summary: 'Delete a website connector'
      operationId: deleteWebsiteConnector
      description: 'Cancels any active crawl, soft-deletes all child pages (with knowledgeSourceChanged events), and marks the connector as deleted.'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '404':
          description: 'Connector not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/websiteConnector/{id}/crawl':
    post:
      tags:
        - 'Website connector'
      summary: 'Trigger a crawl'
      operationId: startWebsiteConnectorCrawl
      description: 'Triggers a Firecrawl crawl for the specified connector. Resets crawl stats and sets status to running.'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Crawl started'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  jobId:
                    type: string
                    description: 'Firecrawl job ID'
                    example: fc-abc123
        '404':
          description: 'Connector not found'
        '422':
          description: 'Firecrawl settings not configured'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/websiteConnector/{id}/stop':
    post:
      tags:
        - 'Website connector'
      summary: 'Stop a running crawl'
      operationId: stopWebsiteConnectorCrawl
      description: 'Cancels the active Firecrawl job and sets the connector status to idle.'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Crawl stopped'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '404':
          description: 'Connector not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/webhook/websiteConnector/{id}':
    post:
      tags:
        - Webhook
      summary: 'Firecrawl webhook receiver'
      operationId: websiteConnectorWebhook
      description: "Receives crawl events from Firecrawl. Authenticated via x-webhook-token header (per-connector secret).\nHandles crawl.page (upsert/delete pages), crawl.completed, and crawl.failed events.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Website connector ID'
          schema:
            type: integer
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  description: 'Event type'
                  enum:
                    - crawl.page
                    - crawl.completed
                    - crawl.failed
                  example: crawl.page
                data:
                  type: object
                  description: 'Page data (for crawl.page events)'
      responses:
        '200':
          description: 'Event processed'
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
        '401':
          description: 'Invalid webhook token'
        '404':
          description: 'Connector not found'
  /knowledgeSource/filesConnector:
    get:
      tags:
        - 'Files connector'
      summary: 'Get files connector'
      operationId: getFilesConnector
      description: 'Returns the files connector row (auto-created if missing), including all folder structure rows and file knowledge_source rows underneath. The connector is a singleton per installation.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FilesConnector'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /knowledgeSource/filesConnector/upload:
    post:
      tags:
        - 'Files connector'
      summary: 'Upload a file (or batch of files)'
      operationId: uploadFilesConnectorFile
      description: "Uploads one or more files to the files connector. Each file is:\n1. Validated (extension allowlist + 50 MB per-file cap + MIME-type sniff)\n2. Stored in MinIO under the `knowledgeSources/` prefix\n3. Converted to Markdown (Firecrawl for office/PDF, Cortex vision LLM for images, direct read for .md/.txt)\n4. Saved as a `knowledge_sources` row with `type='file'`\n5. Triggers a `knowledgeSourceChanged` event for Cortex indexing\n\n**Single-file mode** (backward-compatible): send `name=\"file\"` — response is `{success, id}`.\n\n**Batch mode**: send `name=\"files[]\"` with multiple parts — response is `{successful: [{id, name}], failed: [{name, error}]}`. Max 20 files and 50 MB total per request. Files are processed in parallel via Amp futures. Per-file errors land in `failed` without aborting the batch.\n"
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: 'Single-file upload (backward-compatible). Mutually exclusive with `files[]`.'
                'files[]':
                  type: array
                  items:
                    type: string
                    format: binary
                  description: 'Batch upload — multiple files as `name="files[]"`. Max 20 files, 50 MB total.'
                folderId:
                  type: integer
                  description: 'Folder structure ID to place the file(s) in. Defaults to the connector root.'
                  example: 11
      responses:
        '200':
          description: "Single-file: `{success: true, id: N}`.\nBatch: `{successful: [{id, name}], failed: [{name, error}]}` — HTTP 200 even when some files failed.\n"
          content:
            application/json:
              schema:
                oneOf:
                  -
                    $ref: '#/components/schemas/FileUploadResponse'
                  -
                    $ref: '#/components/schemas/BatchFileUploadResponse'
        '400':
          description: 'Incomplete upload (partial transfer)'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: 'File or total batch size exceeds limit'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'No file provided, unsupported type, MIME spoofing, or too many files in batch'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/filesConnector/{id}/replace':
    post:
      tags:
        - 'Files connector'
      summary: 'Replace the binary of a file'
      operationId: replaceFilesConnectorFile
      description: "Replaces the binary backing an existing file knowledge_source. The KS id is preserved\n(so embeddings keep their identity in downstream stores); the row's `name`, `title`,\n`source` and `data.original*` fields are refreshed to match the new file.\n\nThe new file may have a different filename and extension from the previous one — replace\nis keyed by KS id, not by filename. A 422 is returned if the new filename collides with\nanother active file in the same folder.\n\nSide-effects mirror an initial upload: the old S3 object is removed, the new binary is\nstored under the same KS id, Markdown is re-parsed (Firecrawl / Cortex vision / plaintext\ndepending on extension), page thumbnails are regenerated, and `data.hasManualMarkdownEdits`\nis reset to `false`. A `knowledgeSourceChanged action=update` event is emitted so Cortex\nre-embeds the new content.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Knowledge source ID'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: 'New binary that should back this knowledge source.'
      responses:
        '200':
          description: 'File replaced successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileUploadResponse'
        '400':
          description: 'Incomplete upload (partial transfer)'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: 'File knowledge source not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: 'File size exceeds limit'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: "No file provided, unsupported type, MIME spoofing, archived file, or the new filename\ncollides with another active file in the same folder.\n"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /knowledgeSource/filesConnector/folders:
    post:
      tags:
        - 'Files connector'
      summary: 'Create a folder'
      operationId: createFilesConnectorFolder
      description: 'Creates a new folder under the files connector hierarchy.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: 'Folder display name'
                  example: Sales
                parentId:
                  type: integer
                  description: 'Parent folder structure ID. Defaults to the connector root.'
                  example: 10
      responses:
        '200':
          description: 'Folder ensured (created or already existed)'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  id:
                    type: integer
                    description: 'ID of the folder row (newly created or pre-existing)'
                    example: 11
                  created:
                    type: boolean
                    description: 'True when a new folder row was inserted; false when a same-name folder already existed under the same parent and is being returned idempotently.'
                    example: true
        '404':
          description: 'Parent folder not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'Folder name is required'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/filesConnector/folders/{id}':
    patch:
      tags:
        - 'Files connector'
      summary: 'Rename or move a folder'
      operationId: updateFilesConnectorFolder
      description: "Renames and/or moves a folder. After the change, rebuilds the `source` column for all descendant files and emits one `knowledgeSourceChanged action=update` event per descendant. Cortex handles these via `update_properties` (no re-embedding).\n"
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Folder structure ID'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'New folder name'
                  example: Vertrieb
                parentId:
                  type: integer
                  description: 'New parent folder structure ID'
                  example: 10
      responses:
        '200':
          description: 'Folder updated successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '404':
          description: 'Folder or parent folder not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - 'Files connector'
      summary: 'Delete a folder'
      operationId: deleteFilesConnectorFolder
      description: "Deletes a folder and all its contents recursively. For each contained file article, calls `KnowledgeSource::delete()` and emits a `knowledgeSourceChanged action=delete` event so Cortex removes the vectors. Sub-folders are also deleted.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Folder structure ID'
      responses:
        '200':
          description: 'Folder deleted successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '404':
          description: 'Folder not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/filesConnector/{id}/resetToOriginal':
    post:
      tags:
        - 'Files connector'
      summary: 'Reset file to original (re-parse)'
      operationId: resetFilesConnectorFileToOriginal
      description: "Re-downloads the original binary from storage, re-converts it to Markdown, and updates the knowledge_sources row. **Destructive** — discards any manual Markdown edits and clears `hasManualMarkdownEdits`. If the embedding hash is unchanged, skips firing the `knowledgeSourceChanged` event (saves a needless Weaviate upsert) but still bumps `modifiedAt`. Use `refreshIndex` to re-embed the current text non-destructively.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Knowledge source ID'
      responses:
        '200':
          description: 'File re-indexed successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '404':
          description: 'File not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Could not download original file'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/filesConnector/{id}/refreshIndex':
    post:
      tags:
        - 'Files connector'
      summary: 'Refresh the index for a file'
      operationId: refreshFilesConnectorFileIndex
      description: "Forces Cortex to re-embed the file's current `text` without re-parsing the original binary. Preserves manual Markdown edits. Bumps `modifiedAt` unconditionally and fires `knowledgeSourceChanged action=update`. Use this when the AI search seems stale or after an embedding-model change; use `reindex` to discard manual edits and re-parse the original.\n"
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Knowledge source ID'
      responses:
        '200':
          description: 'Index refresh queued successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  id:
                    type: integer
                  modifiedAt:
                    type: string
                    format: date-time
        '404':
          description: 'File not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/filesConnector/{id}/move':
    patch:
      tags:
        - 'Files connector'
      summary: 'Move a file to a different folder'
      operationId: moveFilesConnectorFile
      description: 'Moves a file article to a new folder and rebuilds its `source` path. Fires a `knowledgeSourceChanged action=update` event.'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Knowledge source ID'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - folderId
              properties:
                folderId:
                  type: integer
                  description: 'Target folder structure ID'
                  example: 12
      responses:
        '200':
          description: 'File moved successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '404':
          description: 'File or folder not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/knowledgeSource/filesConnector/{id}/original':
    get:
      tags:
        - 'Files connector'
      summary: 'Download original file'
      operationId: getFilesConnectorFileOriginal
      description: 'Returns a 302 redirect to the internal storage URL of the original uploaded binary. The storage endpoint performs regular-vs-archive lookup.'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
          description: 'Knowledge source ID'
      responses:
        '302':
          description: 'Redirect to the original file URL'
          headers:
            Location:
              schema:
                type: string
              description: 'Internal storage URL, e.g. /api/mind/storage/knowledgeSources/42-abc12345/Pricing-2026.pdf'
        '404':
          description: 'File not found or no original URL stored'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /partner/list:
    get:
      tags:
        - Partner
      summary: 'Get all partner'
      operationId: getPartners
      parameters:
        -
          name: q
          in: query
          required: false
          description: 'The search query to filter by.'
          schema:
            type: string
          example: Enneo
        -
          name: limit
          in: query
          required: false
          description: 'Maximum number of records to return'
          schema:
            type: integer
            default: 30
          example: 50
        -
          name: offset
          in: query
          required: false
          description: 'Number of records to skip'
          schema:
            type: integer
            default: 0
          example: 0
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/partner'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /partner:
    post:
      tags:
        - Partner
      summary: 'Create partner'
      operationId: createPartner
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/partner'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    type: object
                    properties:
                      success:
                        type: boolean
                        example: true
                      id:
                        type: integer
                        example: 1
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/partner/{id}':
    get:
      tags:
        - Partner
      summary: 'Get partner'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      operationId: getPartnerById
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/partner'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Partner
      summary: 'Update partner'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      operationId: updatePartner
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/partner'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Partner
      summary: 'Delete partner'
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      operationId: deletePartner
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /event:
    post:
      tags:
        - Event
      summary: 'Create an event'
      description: "Manually emit a Mind event. The created event is enqueued for processing by the standard\nevent pipeline (event hooks, automation, traces).\n"
      operationId: createEvent
      security:
        -
          bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'Event payload (type, subType, ticketId/contractId binding, data).'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  event:
                    $ref: '#/components/schemas/EventActivity'
        '400':
          description: 'Invalid payload'
        '403':
          description: Unauthorized
  /event/search:
    post:
      tags:
        - Event
      summary: 'Search events with filtering and pagination'
      description: 'For technical personnel to search and analyze events across the system. Allows searching, filtering, and paginating through events independent of ticket ID. Technical information is always included in the results.'
      operationId: searchEvents
      security:
        -
          bearerAuth: []
      parameters:
        -
          name: offset
          in: query
          description: 'Pagination offset'
          required: false
          schema:
            type: integer
            default: 0
        -
          name: limit
          in: query
          description: 'Number of items to return (max 100)'
          required: false
          schema:
            type: integer
            default: 20
            maximum: 100
        -
          name: orderByField
          in: query
          description: 'Field to sort by (only indexed fields are supported)'
          required: false
          schema:
            type: string
            enum:
              - e.id
              - e.type
              - e.subType
              - e.contractId
              - e.ticketId
              - e.status
              - e.createdAt
              - e.createdBy
            default: e.createdAt
        -
          name: orderByDirection
          in: query
          description: 'Sort direction'
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
        -
          name: includeTraces
          in: query
          description: 'Whether to include event traces in the response'
          required: false
          schema:
            type: boolean
            default: true
        -
          name: format
          in: query
          description: "Response format. 'formatted' returns processed events with typed data and transformations. 'raw' returns raw database objects with JSON fields decoded but without additional postprocessing."
          required: false
          schema:
            type: string
            enum:
              - formatted
              - raw
            default: formatted
      requestBody:
        description: 'Filters for searching events'
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                filters:
                  type: array
                  description: "Array of filter objects for searching events. Supported filter fields (indexed fields only): e.id, e.type, e.subType, e.contractId, e.ticketId, e.status, e.createdAt, e.createdBy Note: e.subType can only be used in combination with e.type due to the way the database is indexed.\nSupported comparators: - \"=\" (equals) - \"!=\" (not equals) - \">\" (greater than) - \"<\" (less than) - \">=\" (greater than or equal) - \"<=\" (less than or equal) - \"in\" (in list, use with 'values' array) - \"between\" (between range, use with 'from' and 'to')\nSpecial key 'q': - A filter with key 'q' will search across all JSON data fields (data, outcome, hookOutcome) - Example: {\"key\":\"q\",\"comparator\":\"equal\",\"value\":\"abc\"} searches for \"abc\" in all JSON fields - Must be used with at least one other non-LIKE filter (e.g. status, id, date) - An exception will be thrown if used without other non-LIKE filters - The 'value' parameter is required\nSpecial date values for date comparisons: - \"CURRENT_TIME\" - Current time - \"CURRENT_DATE\" - Current date - \"-1 DAY\" - Yesterday - \"1 DAY\" - Tomorrow - \"-1 HOUR\" - One hour ago - \"1 HOUR\" - One hour from now\nExample: - {\"filters\":[{\"key\":\"e.ticketId\",\"value\":\"8\",\"comparator\":\"=\"},{\"key\":\"e.type\",\"values\":[\"TicketUpdated\"],\"comparator\":\"in\"}]}\n"
                  items:
                    $ref: '#/components/schemas/Filter'
            example:
              filters:
                -
                  key: e.status
                  comparator: in
                  values:
                    - open
                    - closed
                -
                  key: e.createdAt
                  comparator: between
                  from: '2023-01-01'
                  to: '2023-12-31'
                -
                  key: q
                  comparator: equal
                  value: abc
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      $ref: '#/components/schemas/EventActivity'
                  pagination:
                    type: object
                    properties:
                      total:
                        type: integer
                        description: 'Total number of matching events'
                      offset:
                        type: integer
                        description: 'Current pagination offset'
                      limit:
                        type: integer
                        description: 'Current pagination limit'
              examples:
                formatted:
                  summary: 'Response with format=formatted (default)'
                  value:
                    events:
                      -
                        id: 3665
                        type: ticketUpdated
                        subType: null
                        status: closed
                        data: null
                        user:
                          id: 2
                          email: alice@enneo.dev
                          gender: female
                          firstName: Alice
                          lastName: Anderson
                          companyIds:
                            - 1
                          image: null
                          type: user
                        createdAt: '2026-01-13 18:28:51'
                        activity: 'closed the ticket'
                        ticketId: 8
                        details:
                          -
                            label: 'Event ID'
                            value: '3665'
                            tooltip: null
                            url: null
                            type: string
                          -
                            label: 'Event type'
                            value: ticketUpdated
                            tooltip: null
                            url: null
                            type: string
                          -
                            label: 'Duration (s)'
                            value: '0.018'
                            tooltip: null
                            url: null
                            type: string
                          -
                            label: Trace-ID
                            value: ee0897510b4f0ef773dd007d2e6dda66
                            tooltip: null
                            url: 'https://signoz.enneo.ai/trace/ee0897510b4f0ef773dd007d2e6dda66'
                            type: string
                        tabs: []
                        showToUser: true
                        eventTraces: []
                    pagination:
                      total: 3
                      offset: 0
                      limit: 1
                raw:
                  summary: 'Response with format=raw'
                  value:
                    events:
                      -
                        id: 3665
                        type: ticketUpdated
                        subType: null
                        data:
                          status: closed
                          oldValues:
                            status: open
                        contractId: '715559'
                        ticketId: 8
                        status: closed
                        outcome:
                          tagsAssigned: []
                          refreshScheduled: false
                          webhooksExecuted: []
                        hookOutcome: null
                        retries: 0
                        traceIdCreation: ee0897510b4f0ef773dd007d2e6dda66
                        traceIdExecution: ee0897510b4f0ef773dd007d2e6dda66
                        duration: 0.018003
                        createdBy: 2
                        createdAt: '2026-01-13 18:28:51'
                        modifiedAt: '2026-01-13 18:28:51'
                        traces: []
                    pagination:
                      total: 3
                      offset: 0
                      limit: 1
        '403':
          description: 'Forbidden - Insufficient permissions'
  /event/getEventTypes:
    get:
      tags:
        - Event
      summary: 'Get available event types'
      description: 'Returns the catalogue of supported event types with their subtypes and human-readable labels.'
      operationId: getEventTypes
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  eventTypes:
                    type: array
                    items:
                      type: object
  '/event/{id}':
    get:
      tags:
        - Event
      summary: 'Get an event by id'
      operationId: getEvent
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  event:
                    $ref: '#/components/schemas/EventActivity'
        '404':
          description: 'Event not found'
    patch:
      tags:
        - Event
      summary: 'Update an event'
      description: 'Patch metadata on an event — usually `status`, `outcome`, or `hookOutcome`. The body contains the partial object to merge.'
      operationId: updateEvent
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'Partial event to merge.'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  event:
                    $ref: '#/components/schemas/EventActivity'
        '404':
          description: 'Event not found'
  /eventTrace:
    post:
      tags:
        - Event
      summary: 'Create an event trace'
      description: 'Persist a single event trace attached to a parent event.'
      operationId: createEventTrace
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'Trace payload (eventId, type, data, timestamps).'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  eventTrace:
                    type: object
        '400':
          description: 'Invalid payload'
        '403':
          description: Unauthorized
  /eventTrace/batch:
    post:
      tags:
        - Event
      summary: 'Create event traces in batch'
      description: "Same as `POST /eventTrace`, but accepts an array of trace payloads. Used by Cortex and other\nservices that buffer traces and flush them periodically to reduce request overhead.\n"
      operationId: createEventTracesBatch
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                traces:
                  type: array
                  items:
                    type: object
                    description: 'Single trace payload (eventId, type, data, timestamps).'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  eventTraces:
                    type: array
                    items:
                      type: object
        '400':
          description: 'Invalid payload'
        '403':
          description: Unauthorized
  '/eventTrace/{id}':
    get:
      tags:
        - Event
      summary: 'Get an event trace by id'
      operationId: getEventTrace
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  eventTrace:
                    type: object
        '404':
          description: 'Event trace not found'
    patch:
      tags:
        - Event
      summary: 'Update an event trace'
      operationId: updateEventTrace
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'Partial trace to merge.'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  eventTrace:
                    type: object
        '404':
          description: 'Event trace not found'
  '/export/{key}':
    get:
      tags:
        - Export
      summary: 'Generic export by key'
      description: "Dispatches to the per-key export handler that matches `key`. Use this endpoint for keys that\ndon't have a dedicated path on this page (e.g. `reporting_tickets`, `reporting_messages`,\n`reporting_worklog`, `survey`). The accepted query parameters and response shape depend on\nthe chosen key — see the dedicated schema blocks (`exportReportingTickets`,\n`exportReportingMessages`, `exportReportingWorklog`, `exportSurveyResponses`) in\n`endpoints/export.yaml` for the per-key contract.\n\nLarge result sets are queued asynchronously and delivered to the current user's email; small\nsets stream the file in the response body.\n"
      operationId: genericExport
      parameters:
        -
          name: key
          in: path
          required: true
          description: 'Export key — identifies the dataset and column projection to export.'
          schema:
            type: string
          example: reporting_tickets
        -
          name: format
          in: query
          required: false
          description: 'Export format. Defaults to the backend `defaultDateExportFormat` setting.'
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
          example: json
      responses:
        '200':
          description: 'Successful operation — file body or async-confirmation payload.'
          content:
            application/json:
              schema:
                type: object
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: 'Unknown or invalid export key.'
        '403':
          description: Unauthorized.
        '500':
          description: 'Internal error.'
  '/export/{key}/preview':
    get:
      tags:
        - Export
      summary: 'Preview a generic export by key'
      description: "Returns a preview of the data that `GET /export/{key}` would produce — the first rows and\ncolumn metadata, used to power the export-overview UI in ops-fe.\n"
      operationId: genericExportPreview
      parameters:
        -
          name: key
          in: path
          required: true
          description: 'Export key — identifies the dataset to preview.'
          schema:
            type: string
          example: reporting_tickets
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  columns:
                    type: array
                    items:
                      type: string
                    description: 'Column names in the order they appear in the export.'
                  rows:
                    type: array
                    description: 'First N rows of the dataset.'
                    items:
                      type: object
                  total:
                    type: integer
                    description: 'Total number of rows that would be exported.'
        '400':
          description: 'Unknown or invalid export key.'
        '403':
          description: Unauthorized.
        '500':
          description: 'Internal error.'
  /export/callLog:
    get:
      tags:
        - Export
      summary: 'Export call log'
      description: 'Export the call log as a downloadable file (xlsx/csv/json).'
      operationId: exportCallLog
      parameters:
        -
          name: format
          in: query
          required: false
          description: 'Export format. Defaults to the backend `defaultDateExportFormat` setting.'
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '403':
          description: Unauthorized.
        '500':
          description: 'Internal error.'
  /export/conversations:
    get:
      tags:
        - Export
      summary: 'Export conversations'
      operationId: exportConversationsList
      parameters:
        -
          name: limit
          in: query
          required: false
          description: 'The maximum number of records to return'
          schema:
            type: integer
            default: 100
        -
          name: offset
          in: query
          required: false
          description: 'The number of records to skip'
          schema:
            type: integer
            default: 0
        -
          name: minTicketId
          in: query
          required: false
          description: 'Minimum ticket ID to include in results'
          schema:
            type: integer
            default: 0
        -
          name: channel
          in: query
          required: false
          description: 'Filter by channel (comma-separated list)'
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
        '403':
          description: Unauthorized
        '500':
          description: 'Internal server error'
  /export/users:
    get:
      tags:
        - Export
      summary: 'Export user profiles and their permissions'
      operationId: exportUsers
      parameters:
        -
          name: format
          in: query
          required: false
          description: 'The format of the export'
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
            default: xlsx
      description: "Export all user profiles and their associated data including permissions.\nUsed primarily by workforce managers to quickly view all profiles.\n"
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
            text/csv:
              schema:
                type: string
                format: binary
        '403':
          description: "Unauthorized - User doesn't have permission to export user profiles"
        '500':
          description: 'Internal server error'
  /export/customData:
    get:
      tags:
        - Export
      summary: 'Export custom client reports'
      operationId: exportCustomData
      description: "Export data using custom SQL queries with dynamic parameters. This endpoint allows you to execute \npre-configured SQL queries with customizable parameters that replace placeholders in the query.\n\nThis endpoint should only be used if standard export endpoints (for tickets, messages, worklog, survey, etc.) do not suffice. It is an adanced feature and every query requires prior approval by your Enneo account manager.\n\n## How Parameters Work\n\n1. **SQL Query Placeholders**: Custom exports contain SQL queries with placeholders like `{limit}`, `{offset}`, `{customerId}`\n2. **Parameter Resolution**: Parameters are resolved in this order:\n   - GET query parameters (highest priority)\n   - Default values from export configuration\n   - Required parameters will throw an error if missing\n3. **Security**: All parameters are safely bound using prepared statements\n\n## Example Custom Export Configuration\n```json\n{\n  \"label\": \"Customer Events Report\",\n  \"sqlQuery\": \"SELECT id, type, createdAt FROM event WHERE customerId = {customerId} ORDER BY id DESC LIMIT {limit} OFFSET {offset}\",\n  \"parameters\": [\n    {\n      \"key\": \"customerId\",\n      \"label\": \"Customer ID\",\n      \"description\": \"The customer to export events for\",\n      \"defaultValue\": null\n    },\n    {\n      \"key\": \"limit\", \n      \"label\": \"Limit\",\n      \"description\": \"Maximum number of records to return\",\n      \"defaultValue\": \"100\"\n    },\n    {\n      \"key\": \"offset\",\n      \"label\": \"Offset\", \n      \"description\": \"Number of records to skip\",\n      \"defaultValue\": \"0\"\n    }\n  ]\n}\n```\n\n## Example Usage\n```\nGET /api/mind/export/customData?exportId=0&format=json&customerId=12345&limit=50&offset=0\n```\n\nThis would execute:\n```sql\nSELECT id, type, createdAt FROM event WHERE customerId = ? ORDER BY id DESC LIMIT ? OFFSET ?\n```\nWith parameters: `[12345, 50, 0]`\n"
      parameters:
        -
          name: format
          in: query
          required: false
          description: 'The format of the export'
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
            default: 'As configured in the settings'
          example: json
        -
          name: exportId
          in: query
          required: false
          description: "The ID of the export configuration to use. This corresponds to the index in the \n`customDataExports` settings array. If null, the default export as configured \nin the `customDataExportSelected` setting will be used.\n"
          schema:
            type: integer
            nullable: true
            default: null
          example: 0
        -
          name: '{parameterName}'
          in: query
          required: false
          description: "**Dynamic parameters that replace placeholders in the SQL query.**\n\nAny parameter defined in the export configuration can be overridden via GET parameters.\nParameter names should match the placeholders in the SQL query (without the curly braces).\n\n**Parameter Resolution Logic:**\n1. If provided as GET parameter → use that value\n2. If not provided but has default value → use default value  \n3. If not provided and no default → throw error (parameter required)\n\n**Common Parameter Examples:**\n- `limit=100` → replaces `{limit}` placeholder\n- `offset=0` → replaces `{offset}` placeholder  \n- `customerId=12345` → replaces `{customerId}` placeholder\n- `startDate=2024-01-01` → replaces `{startDate}` placeholder\n- `endDate=2024-12-31` → replaces `{endDate}` placeholder\n- `status=active` → replaces `{status}` placeholder\n- `departmentId=5` → replaces `{departmentId}` placeholder\n"
          schema:
            type: string
          examples:
            limit:
              summary: 'Limit parameter'
              value: '100'
            offset:
              summary: 'Offset parameter'
              value: '0'
            customerId:
              summary: 'Customer ID parameter'
              value: '12345'
            dateRange:
              summary: 'Date parameter'
              value: '2024-01-01'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: 'The structure depends on the SQL query used in the custom export'
                example:
                  -
                    id: 1
                    type: login
                    createdAt: '2024-01-15T10:30:00Z'
                    customerId: 12345
                  -
                    id: 2
                    type: purchase
                    createdAt: '2024-01-14T15:45:00Z'
                    customerId: 12345
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
                description: 'Excel file containing the export data'
            text/csv:
              schema:
                type: string
                format: binary
                description: 'CSV file containing the export data'
        '400':
          description: 'Bad request - required parameter missing or invalid export configuration'
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: 'Error message'
              examples:
                missingParameter:
                  summary: 'Missing required parameter'
                  value:
                    error: "Parameter 'customerId' is required"
                invalidExport:
                  summary: 'Invalid export ID'
                  value:
                    error: 'Export 5 not found'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal server error'
  /export/gdprPrivateData:
    get:
      tags:
        - Export
      summary: 'Export GDPR private data for a customer'
      description: "Export all personally identifiable data tied to a given customer/contract for GDPR data-portability\nand right-to-access requests. The call requires the requesting user to hold the relevant GDPR\npermission.\n"
      operationId: exportGdprPrivateData
      parameters:
        -
          name: contractId
          in: query
          required: false
          description: 'Contract ID to export. Either `contractId` or `customerId` must be provided.'
          schema:
            type: string
        -
          name: customerId
          in: query
          required: false
          description: 'Customer ID to export. Either `contractId` or `customerId` must be provided.'
          schema:
            type: string
        -
          name: format
          in: query
          required: false
          description: 'Export format. Defaults to the backend `defaultDateExportFormat` setting.'
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: 'Missing `contractId`/`customerId` or invalid parameters.'
        '403':
          description: 'Unauthorized — caller lacks GDPR export permission.'
        '500':
          description: 'Internal error.'
  /export/aiAgentPerformance:
    get:
      tags:
        - Export
      summary: 'Export AI agent performance'
      description: 'Export per-AI-agent performance metrics (executions, success rate, latency, …) for a time range.'
      operationId: exportAiAgentPerformance
      parameters:
        -
          name: from
          in: query
          required: false
          description: 'Start of the reporting window (ISO 8601 date). Defaults to 30 days ago.'
          schema:
            type: string
            format: date
          example: '2026-04-21'
        -
          name: to
          in: query
          required: false
          description: 'End of the reporting window (ISO 8601 date). Defaults to today.'
          schema:
            type: string
            format: date
          example: '2026-05-21'
        -
          name: format
          in: query
          required: false
          description: 'Export format. Defaults to the backend `defaultDateExportFormat` setting.'
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '403':
          description: Unauthorized.
        '500':
          description: 'Internal error.'
  /export/messageSamples:
    get:
      tags:
        - Export
      summary: "Export training data that is used by the AI's default text answer"
      operationId: exportMessageSamples
      parameters:
        -
          name: format
          in: query
          required: false
          description: 'The format of the export'
          schema:
            type: string
            enum:
              - xlsx
              - json
            default: xlsx
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
        '403':
          description: Unauthorized
        '500':
          description: 'Internal server error'
  /export/qualityAssessments:
    get:
      tags:
        - Export
      summary: 'Export quality assessments'
      operationId: exportQualityAssessments
      description: "Export quality assessments with all criterion scores for a given scorecard.\n\nEach row represents one assessment with the following columns:\n- Date (createdAt - when the assessment was created)\n- Assessment Date (when the supervisor completed the assessment, null for AI-only assessments)\n- Reviewed Agent (name of the assessed user)\n- Reviewer (name of the reviewer or \"AI\" for AI-generated assessments)\n- State (translated to readable labels: \"Ready for Review\", \"Reviewed\", etc.)\n- Ticket ID\n- Total Score (absolute scored points as integer)\n- Total Score (%) (percentage score)\n- One column per criterion score (e.g., \"Communication Quality - Clarity & Readability\")\n\n**Default Scorecard Behavior:**\nIf `scorecardId` is omitted, the scorecard from the most recent valid assessment is used as default.\n\n**Multi-Sheet XLSX Export:**\nWhen format is `xlsx` and `scorecardId` is omitted, the export generates one Excel sheet per scorecard\nthat has assessments. Each sheet is named after the scorecard.\n\n**Excluded States:**\nAssessments in states `unprocessed`, `aiInProgress`, `deleted`, and `error` are excluded from the export.\n\nExample requests:\n```\nGET /api/mind/export/qualityAssessments?format=json\nGET /api/mind/export/qualityAssessments?format=xlsx&scorecardId=1\nGET /api/mind/export/qualityAssessments?format=xlsx&from=2024-01-01&to=2024-12-31\n```\n"
      parameters:
        -
          name: format
          in: query
          required: false
          description: 'Export format. Defaults to the backend `defaultDateExportFormat` setting.'
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
            default: xlsx
          example: xlsx
        -
          name: scorecardId
          in: query
          required: false
          description: "ID of the scorecard to export assessments for. If omitted:\n- For XLSX format: exports all scorecards as separate sheets\n- For other formats: uses the scorecard from the most recent valid assessment\n"
          schema:
            type: integer
          example: 1
        -
          name: from
          in: query
          required: false
          description: 'Start date filter (inclusive). Only assessments created on or after this date are included.'
          schema:
            type: string
            format: date
          example: '2024-01-01'
        -
          name: to
          in: query
          required: false
          description: 'End date filter (inclusive). Only assessments created on or before this date are included.'
          schema:
            type: string
            format: date
          example: '2024-12-31'
        -
          $ref: '#/components/parameters/limitParam'
        -
          $ref: '#/components/parameters/offsetParam'
      responses:
        '200':
          description: 'Export returned successfully.'
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    Date:
                      type: string
                      format: date-time
                      description: 'When the assessment was created'
                    'Assessment Date':
                      type: string
                      format: date-time
                      nullable: true
                      description: 'When the supervisor completed the assessment (null for AI-only assessments)'
                    'Reviewed Agent':
                      type: string
                      description: 'Name of the agent whose work was assessed'
                    Reviewer:
                      type: string
                      description: 'Name of the reviewer or "AI" for AI-generated assessments'
                    State:
                      type: string
                      description: 'Human-readable assessment state'
                      enum:
                        - 'Ready for Review'
                        - 'Review in Progress'
                        - Reviewed
                        - 'Discussed with Agent'
                    'Ticket ID':
                      type: integer
                      description: 'ID of the ticket that was assessed'
                    'Total Score':
                      type: integer
                      nullable: true
                      description: 'Absolute scored points'
                    'Total Score (%)':
                      type: number
                      format: float
                      description: 'Overall assessment score as percentage'
                  additionalProperties:
                    type: integer
                    nullable: true
                    description: 'Individual criterion scores (column names are dynamic based on scorecard criteria)'
              example:
                -
                  Date: '2024-05-12 09:00:00'
                  'Assessment Date': '2024-05-13 14:30:00'
                  'Reviewed Agent': 'John Doe'
                  Reviewer: 'Jane Smith'
                  State: Reviewed
                  'Ticket ID': 4711
                  'Total Score': 54
                  'Total Score (%)': 85.5
                  'Communication Quality - Clarity': 4
                  'Communication Quality - Grammar': 5
                  'Problem Resolution - Solution Quality': 8
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
                description: "Excel file containing the export data. When `scorecardId` is omitted,\nthe file contains multiple sheets (one per scorecard with assessments).\n"
            text/csv:
              schema:
                type: string
                format: binary
        '403':
          description: 'Unauthorized – user lacks `qualityViewAssessmentAll` permission.'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: 'Error message'
  '/export/knowledgeSources/{type}':
    get:
      tags:
        - Export
      summary: 'Export knowledge sources'
      operationId: exportKnowledgeSources
      parameters:
        -
          name: type
          in: path
          required: true
          description: 'The type of knowledge sources to export'
          schema:
            type: string
            enum:
              - all
              - faq
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '403':
          description: Unauthorized
        '500':
          description: 'Internal server error'
  /webhook/powercloud:
    post:
      tags:
        - Webhook
      summary: 'PowerCloud webhook'
      operationId: powerCloudWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                eventId:
                  type: integer
                  description: 'The id of the event'
                  example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      eventId:
                        type: integer
                        description: 'The id of the event'
                        example: 123
                      contractId:
                        type: string
                        description: 'The id of the contract'
                        example: '123'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /webhook/lynqtech:
    post:
      tags:
        - Webhook
      summary: 'Lynqtech webhook'
      operationId: lynqtechWebhook
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Contract not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /webhook/authSync:
    post:
      tags:
        - Webhook
      summary: 'AuthSync webhook'
      operationId: authSyncWebhook
      description: 'Sync auth data from auth.users to mind.users'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      affectedRows:
                        type: integer
                        description: 'The number of affected rows'
                        example: 123
        '403':
          description: Unauthorized
        '404':
          description: 'Contract not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/webhook/fetchEmails/{subchannelId}':
    post:
      tags:
        - Webhook
      summary: 'Trigger mailbox fetch'
      description: "External trigger that asks Mind to poll the given mailbox subchannel for new messages and\nconvert them into tickets. Without `subchannelId` all configured mailboxes are polled.\n"
      operationId: fetchEmailsWebhook
      parameters:
        -
          name: subchannelId
          in: path
          required: true
          description: 'ID of the mailbox subchannel to fetch.'
          schema:
            type: integer
          example: 42
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      fetched:
                        type: integer
                        description: 'Number of messages pulled in this run.'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /report/structure:
    get:
      tags:
        - Report
      summary: 'Get dashboard structure'
      description: "Returns dashboard structure based on user's role and permissions.\nStructure contains widget layout and configuration.\nAll available components and their configurations are described in the enneo documentation.\n"
      operationId: getReportStructure
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  contentSchema:
                    type: object
                    properties:
                      id:
                        type: string
                        example: content
                      type:
                        type: string
                        example: div
                      class:
                        type: string
                        example: contentContainer
                      children:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            type:
                              type: string
                            class:
                              type: string
                            children:
                              type: array
                              items:
                                type: object
                example:
                  contentSchema:
                    id: content
                    type: div
                    class: contentContainer
                    children:
                      -
                        id: headerContainer
                        type: div
                        class: headerContainer
                        children:
                          -
                            id: headerIconContainer
                            type: div
                            class: headerIconContainer
                            children:
                              -
                                id: icon
                                type: image
                                imageUrl: /icons/icon.svg
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/{reportCode}':
    get:
      tags:
        - Report
      summary: 'Get report data'
      description: 'Returns the data for a specific report type'
      operationId: getReport
      parameters:
        -
          name: reportCode
          in: path
          required: true
          description: 'The type of report to retrieve'
          schema:
            type: string
            enum:
              - openTickets
              - channelMix
              - solvedTicketsByTeam
              - solvedTicketsByResolution
              - incomingVolume
              - customerSurveys
              - telephonyLines
              - telephonyAgents
              - telephonyAiAgents
              - telephonyPerformance
              - telephonyCallInsights
              - telephonyLineTopPerformers
              - qualityAssessments
          example: openTickets
        -
          name: status
          in: query
          required: false
          description: "Filter by status. Supported values depend on the report type:\n- channelMix: open, closed, pending (ticket status)\n- telephonyAgents: all, assignedToPhone, online, onlineActive (user status categories)\n"
          schema:
            type: string
          example: open
        -
          name: channel
          in: query
          required: false
          description: 'Filter by channel (only for channelMix report)'
          schema:
            type: string
            enum:
              - email
              - portal
              - phone
              - system
              - chat
              - letter
          example: email
        -
          name: lastDays
          in: query
          required: false
          description: 'Number of days to look back (for solvedTicketsByTeam, solvedTicketsByResolution, incomingVolume, channelMix, telephonyPerformance, telephonyCallInsights, telephonyLineTopPerformers, and other telephony reports)'
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - '3'
              - '7'
              - '14'
              - '30'
              - '90'
              - '365'
          example: '14'
        -
          name: agent
          in: query
          required: false
          description: 'Filter by agent type (only for solvedTicketsByResolution, solvedTicketsByTeam, customerSurveys report)'
          schema:
            type: string
            enum:
              - own
              - team
              - all
          example: own
        -
          name: ticketScope
          in: query
          required: false
          description: "Restrict the report to tickets matching the agent's configured skill tags\n(only for openTickets, channelMix, solvedTicketsByTeam, customerSurveys reports).\n- all: no skill-based restriction\n- skills: restrict to tickets whose routing-relevant tags overlap the agent's `actualSkills.tagIds`\nWhen the agent has no skill tags configured, `skills` behaves like `all`.\n"
          schema:
            type: string
            enum:
              - all
              - skills
          example: all
        -
          name: teamIds
          in: query
          required: false
          description: "Filter by teams (only for telephonyAgents report). Can be 'all' or comma-separated team IDs."
          schema:
            type: string
          example: all
        -
          name: lineIds
          in: query
          required: false
          description: "Filter by telephony lines (for telephonyAgents and telephonyAiAgents reports). Can be 'all' or comma-separated line IDs."
          schema:
            type: string
          example: all
        -
          name: show
          in: query
          required: false
          description: "Filter by user type (only for telephonyAgents report). Defaults to 'users' if not provided."
          schema:
            type: string
            enum:
              - users
              - all
              - enneo
              - serviceWorkers
              - codeExecutors
              - enneoPartners
          example: users
        -
          name: q
          in: query
          required: false
          description: 'Search by agent name (case-insensitive substring, only for telephonyAgents report).'
          schema:
            type: string
          example: Alice
        -
          name: limit
          in: query
          required: false
          description: 'Maximum number of items to return (only for telephonyAgents report). Defaults to 1000.'
          schema:
            type: integer
            minimum: 1
          example: 50
        -
          name: offset
          in: query
          required: false
          description: 'Number of items to skip for pagination (only for telephonyAgents report). Defaults to 0.'
          schema:
            type: integer
            minimum: 0
          example: 0
        -
          name: lineId
          in: query
          required: false
          description: 'Filter by specific telephony line ID (for telephonyPerformance, telephonyCallInsights, telephonyLineTopPerformers reports). Required for telephonyLineTopPerformers.'
          schema:
            type: string
          example: '11'
        -
          name: agentsType
          in: query
          required: false
          description: "Filter by agent type (only for telephonyLineTopPerformers report). Defaults to 'all' if not provided."
          schema:
            type: string
            enum:
              - all
              - ai
              - human
          example: all
        -
          name: scorecardId
          in: query
          required: false
          description: 'Filter by scorecard base ID (only for qualityAssessments report).'
          schema:
            type: string
          example: '1'
        -
          name: assessedUserId
          in: query
          required: false
          description: "Restrict the report to a single assessed user (only for qualityAssessments report).\nOverrides the `agent` scope. The requested user must be within the caller's permitted\naudience (own < team < all) or a 403 is returned.\n"
          schema:
            type: string
          example: '1'
        -
          name: assessedTeamId
          in: query
          required: false
          description: "Restrict the report to the current members of a single team (only for qualityAssessments report).\nOverrides the `agent` scope; ignored when `assessedUserId` is also set. The team must be within\nthe caller's permitted audience (ViewAll → any team; ViewTeam → own teams) or a 403 is returned.\nFilters by current team membership, not the historical team snapshot shown in `teamBreakdown`.\n"
          schema:
            type: string
          example: '1'
        -
          name: dateFrom
          in: query
          required: false
          description: "Start date for custom date range (only for qualityAssessments report, overrides lastDays).\nFormat: YYYY-MM-DD\n"
          schema:
            type: string
            format: date
          example: '2024-01-01'
        -
          name: dateTo
          in: query
          required: false
          description: "End date for custom date range (only for qualityAssessments report, overrides lastDays).\nFormat: YYYY-MM-DD\n"
          schema:
            type: string
            format: date
          example: '2024-12-31'
        -
          name: granularity
          in: query
          required: false
          description: "Time bucket size for time-series reports.\nWhen omitted the response is byte-identical to the pre-granularity behaviour (including the legacy 0–23\nhour-of-day profile for callsAnsweredByHour).\nApplicable reports — hour/day/week/month: incomingVolume, telephonyPerformance,\ntelephonyCallInsights (aiHandledVsHuman + callsAnsweredByHour timeline), telephonyAgentPerformance,\ntelephonyAiAgentPerformance, telephonyAiAgentCallInsights.\nDay/week/month only (DATE column, no time-of-day): solvedTicketsByTeam, solvedTicketsByResolution.\n- hour: one bucket per clock-hour, keys in \"YYYY-MM-DD HH:00\" format; only allowed with lastDays ≤ 31\n- day: one bucket per calendar day, keys in dd.mm format (legacy default)\n- week: one bucket per ISO week, keys in YYYY-WNN format\n- month: one bucket per calendar month, keys in YYYY-MM format\n"
          schema:
            type: string
            enum:
              - hour
              - day
              - week
              - month
          example: week
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    oneOf:
                      -
                        type: object
                        description: 'For openTickets report'
                        properties:
                          dueInMoreThan24Hours:
                            type: integer
                            example: 10
                          dueInNext24Hours:
                            type: integer
                            example: 5
                          overdue24Hours:
                            type: integer
                            example: 3
                          overdue24To48Hours:
                            type: integer
                            example: 2
                          overdueMoreThan48Hours:
                            type: integer
                            example: 1
                          totalTickets:
                            type: integer
                            example: 21
                      -
                        type: object
                        description: 'For channelMix report'
                        additionalProperties:
                          type: integer
                        example:
                          email: 50
                          chat: 30
                          phone: 20
                      -
                        type: object
                        description: 'For time-based reports (solvedTicketsByTeam, solvedTicketsByResolution, incomingVolume)'
                        additionalProperties:
                          type: object
                        example:
                          '2023-01-01':
                            teamTicketsWorked: 10
                            ownTicketsWorked: 5
                      -
                        type: object
                        description: 'For customerSurveys report'
                        additionalProperties:
                          type: integer
                        example:
                          '1': 2
                          '3': 5
                          '4': 8
                          '5': 15
                      -
                        type: array
                        description: 'For telephonyLines report'
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'Unique identifier for the telephony line'
                              example: 1
                            name:
                              type: string
                              example: 'Main Service line'
                            status:
                              type: string
                              enum:
                                - active
                                - inactive
                              example: active
                            liveCallsTotal:
                              type: integer
                              example: 38
                            liveCallsHuman:
                              type: integer
                              example: 35
                            liveCallsBot:
                              type: integer
                              example: 3
                            asaHumanSeconds:
                              type: integer
                              description: 'Average Speed of Answer for human calls in seconds'
                              example: 141
                            inQueueCount:
                              type: integer
                              example: 4
                            inQueueTimeSeconds:
                              type: integer
                              description: 'Average time in queue in seconds'
                              example: 31
                            liveCalls:
                              type: array
                              description: 'Per-call breakdown behind the aggregate live-call counts on this line. Passthrough from ACD; same shape as the telephonyLineDetails report.'
                              items:
                                type: object
                                required:
                                  - callId
                                  - from
                                  - state
                                  - durationSeconds
                                properties:
                                  callId:
                                    type: string
                                    description: 'Unique call identifier'
                                    example: 550e8400-e29b-41d4-a716-446655440000
                                  ticketId:
                                    type: integer
                                    description: 'Associated ticket ID (omitted if not yet linked)'
                                    example: 12345
                                  from:
                                    type: string
                                    description: 'Caller phone number'
                                    example: '+4930754358631'
                                  state:
                                    type: string
                                    enum:
                                      - waiting
                                      - bot
                                      - human
                                      - forwarded
                                    description: 'Current call state'
                                    example: human
                                  agentUserId:
                                    type: integer
                                    description: 'ID of the connected agent (omitted if not applicable)'
                                    example: 42
                                  agentName:
                                    type: string
                                    description: 'Name of the connected agent (omitted if not applicable)'
                                    example: 'Jenny Kaspar'
                                  waitSeconds:
                                    type: integer
                                    description: 'Seconds spent waiting in queue (present only when state is waiting)'
                                    example: 12
                                  durationSeconds:
                                    type: integer
                                    description: 'Total call duration in seconds since call started'
                                    example: 30
                      -
                        type: array
                        description: "For telephonyAgents report - returns enriched agent identity, routing state, and today's telephony metrics."
                        items:
                          type: object
                          properties:
                            agent:
                              type: object
                              description: 'Agent identity and current routing state'
                              properties:
                                id:
                                  type: integer
                                  description: 'Unique identifier for the agent'
                                  example: 1
                                name:
                                  type: string
                                  description: 'Full name of the agent'
                                  example: 'Alice Anderson'
                                status:
                                  type: string
                                  description: 'Current profile status of the agent'
                                  example: active
                                channels:
                                  type: array
                                  description: 'Channels the agent is skilled for'
                                  items:
                                    type: string
                                  example:
                                    - email
                                    - phone
                                    - chat
                                callRoutingStatus:
                                  type: string
                                  description: 'Current call routing status'
                                  example: idle
                                chatRoutingStatus:
                                  type: string
                                  description: 'Current chat routing status'
                                  example: idle
                            metrics:
                              type: object
                              description: "Today's telephony statistics for the agent"
                              properties:
                                callsToday:
                                  type: integer
                                  description: 'Number of calls handled today'
                                  example: 13
                                talkTimeSeconds:
                                  type: integer
                                  description: 'Total talk time in seconds today'
                                  example: 1934
                                acwTimeSeconds:
                                  type: integer
                                  description: 'After Call Work time in seconds today'
                                  example: 767
                                missedCalls:
                                  type: integer
                                  description: 'Number of missed calls today'
                                  example: 1
                      -
                        type: array
                        description: 'For telephonyAiAgents report'
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'Unique identifier for the AI agent'
                              example: 1
                            aiAgentName:
                              type: string
                              description: 'AI agent name'
                              example: Basis-Agent
                            callsToday:
                              type: integer
                              description: 'Number of calls handled today'
                              example: 13
                            talkTimeSeconds:
                              type: integer
                              description: 'Total talk time in seconds'
                              example: 1934
                            transferred:
                              type: integer
                              description: 'Number of calls transferred to human agents'
                              example: 1
                            appearance:
                              type: object
                              description: 'Visual appearance configuration'
                              properties:
                                avatar:
                                  type: object
                                  properties:
                                    image:
                                      type: string
                                      description: 'Path to avatar image'
                                      example: /pics/avatars/enneo_AI-Agent_10.png
                                    background:
                                      type: string
                                      description: 'CSS gradient for background'
                                      example: 'linear-gradient(35deg, #DED8F4 20.66%, #8267CC 81.17%)'
                      -
                        type: object
                        description: 'For telephonyPerformance report - returns object with camelCase keys, each containing data/description/filters/summary (similar to aiPerformance)'
                        properties:
                          answeredCalls:
                            type: object
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    'Answered Calls':
                                      type: integer
                                example:
                                  '28.11':
                                    'Answered Calls': 73
                                  '29.11':
                                    'Answered Calls': 75
                              description:
                                type: string
                                example: 'Number of answered calls per day in the selected period.'
                              filters:
                                type: object
                              summary:
                                type: object
                                properties:
                                  total:
                                    type: integer
                                  average:
                                    type: integer
                          missedCalls:
                            type: object
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    'Missed Calls':
                                      type: integer
                              description:
                                type: string
                              filters:
                                type: object
                              summary:
                                type: object
                          reachability:
                            type: object
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    Reachability:
                                      type: integer
                              description:
                                type: string
                              filters:
                                type: object
                              summary:
                                type: object
                          asa:
                            type: object
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    ASA:
                                      type: integer
                              description:
                                type: string
                              filters:
                                type: object
                              summary:
                                type: object
                          aht:
                            type: object
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    AHT:
                                      type: integer
                              description:
                                type: string
                              filters:
                                type: object
                              summary:
                                type: object
                          sla:
                            type: object
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    SLA:
                                      type: integer
                              description:
                                type: string
                              filters:
                                type: object
                              summary:
                                type: object
                      -
                        type: object
                        description: 'For telephonyCallInsights report - returns object with 3 data items (aiHandledVsHuman, detectedIntents, callsAnsweredByHour)'
                        properties:
                          aiHandledVsHuman:
                            type: object
                            description: 'Daily breakdown of AI vs Human handled calls'
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    Human:
                                      type: integer
                                    AI:
                                      type: integer
                                example:
                                  '28.11':
                                    Human: 52
                                    AI: 23
                                  '29.11':
                                    Human: 48
                                    AI: 19
                              description:
                                type: string
                                example: 'Daily breakdown of calls handled by AI versus human agents in the selected period.'
                              filters:
                                type: object
                              summary:
                                type: object
                                properties:
                                  totalHuman:
                                    type: integer
                                  totalAI:
                                    type: integer
                                  aiPercentage:
                                    type: integer
                          detectedIntents:
                            type: object
                            description: 'Distribution of detected customer intents (pie chart data)'
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: integer
                                example:
                                  Allgemein: 25
                                  Mako: 30
                                  Rechnung: 13
                                  Lorem: 23
                                  Ipsum: 9
                              description:
                                type: string
                                example: 'Distribution of detected customer intents across all calls in the selected period.'
                              filters:
                                type: object
                              summary:
                                type: object
                                properties:
                                  total:
                                    type: integer
                                  percentages:
                                    type: object
                                    additionalProperties:
                                      type: integer
                                  counts:
                                    type: object
                                    description: 'Absolute per-intent call counts (raw numbers before percentage conversion)'
                                    additionalProperties:
                                      type: integer
                          callsAnsweredByHour:
                            type: object
                            description: "Breakdown of call outcomes by hour-of-day or chronological timeline.\nWithout granularity: fixed 0–23 slots (hour-of-day aggregate, legacy).\nWith granularity: chronological timeline buckets keyed by the granularity format\n(e.g. \"2026-06-02 14:00\" for hour, \"02.06\" for day, \"2026-W23\" for week).\n"
                            properties:
                              data:
                                type: object
                                additionalProperties:
                                  type: object
                                  properties:
                                    'AI Bot finished':
                                      type: integer
                                    'Handed over to human':
                                      type: integer
                                    Declined/Missed:
                                      type: integer
                                    'Short call (<10s)':
                                      type: integer
                                example:
                                  '9':
                                    'AI Bot finished': 12
                                    'Handed over to human': 18
                                    Declined/Missed: 4
                                    'Short call (<10s)': 2
                                  '2026-06-02 09:00':
                                    'AI Bot finished': 12
                                    'Handed over to human': 18
                                    Declined/Missed: 4
                                    'Short call (<10s)': 2
                              description:
                                type: string
                                example: 'Hourly breakdown of call outcomes showing how calls were handled throughout the day.'
                              filters:
                                type: object
                              summary:
                                type: object
                                properties:
                                  aiBotFinished:
                                    type: integer
                                  aiBotHandedOver:
                                    type: integer
                                  transferredDropped:
                                    type: integer
                                  customerDeclinedDPA:
                                    type: integer
                                  shortCall:
                                    type: integer
                  description:
                    type: string
                    description: 'Description of what the report shows'
                    example: "Tickets in status 'open', broken down by due date compared to the current time."
                  summary:
                    type: object
                    description: 'Summary statistics for the report (optional, only for some reports)'
                    oneOf:
                      -
                        type: object
                        description: 'For channelMix report'
                        properties:
                          title:
                            type: string
                            example: 'Open tickets'
                          amount:
                            type: integer
                            example: 63
                          description:
                            type: string
                            example: ''
                      -
                        type: object
                        description: 'For solvedTicketsByTeam report'
                        properties:
                          ownAmount:
                            type: integer
                            example: 10
                          teamAmount:
                            type: integer
                            example: 15
                          totalAmount:
                            type: integer
                            example: 8
                          amount:
                            type: integer
                            example: 33
                      -
                        type: object
                        description: 'For customerSurveys report'
                        properties:
                          totalShown:
                            type: integer
                            description: 'Total number of surveys sent'
                            example: 100
                          responseRate:
                            type: number
                            description: 'Response rate as percentage'
                            example: 85.5
                          currentScale:
                            type: integer
                            description: 'Current scale setting used for normalization'
                            example: 5
                      -
                        type: object
                        description: 'For telephonyLines report'
                        properties:
                          currentLiveCalls:
                            type: integer
                            description: 'Total number of current live calls across all lines'
                            example: 59
                          currentInQueue:
                            type: integer
                            description: 'Total number of calls currently in queue across all lines'
                            example: 5
                      -
                        type: object
                        description: 'For telephonyAgents report'
                        properties:
                          availableAgents:
                            type: integer
                            description: 'Number of agents currently available'
                            example: 3
                          onlineAgents:
                            type: integer
                            description: 'Total number of agents online'
                            example: 15
                          callsToday:
                            type: integer
                            description: 'Total number of calls handled today across all agents'
                            example: 49
                          missedCalls:
                            type: integer
                            description: 'Total number of missed calls across all agents'
                            example: 16
                      -
                        type: object
                        description: 'For telephonyAiAgents report'
                        properties:
                          totalAiAgents:
                            type: integer
                            description: 'Total number of AI agents'
                            example: 3
                          callsToday:
                            type: integer
                            description: 'Total number of calls handled today across all AI agents'
                            example: 40
                          transferred:
                            type: integer
                            description: 'Total number of calls transferred to human agents'
                            example: 4
                  total:
                    type: integer
                    description: 'Total number of items after filters and search, before pagination (only present for reports that support pagination, e.g. telephonyAgents).'
                    example: 42
                  filters:
                    type: object
                    description: 'The filters that were applied to generate this report'
                    example:
                      lastDays: 14
                      agent: own
        '400':
          description: 'Invalid filter parameters'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /report/aiPerformance:
    get:
      tags:
        - Report
      summary: 'Get overall AI performance metrics'
      description: "Returns overall AI performance metrics across the workspace for a selected time window.\n"
      operationId: getAiPerformance
      parameters:
        -
          name: lastDays
          in: query
          required: false
          description: 'Number of days to look back. If omitted, defaults to 7.'
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - '3'
              - '7'
              - '14'
              - '30'
              - '90'
              - '365'
          example: '14'
        -
          name: outputHandlingCase
          in: query
          required: false
          description: 'Optional output handling case to filter metrics by.'
          schema:
            type: string
          example: confirm
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                description: 'Map of metric name to report item'
                additionalProperties:
                  type: object
                  properties:
                    data:
                      description: 'Metric data (time-series or aggregated values)'
                      additionalProperties: true
                    summary:
                      type: object
                      description: 'Aggregated numbers for the metric'
                      additionalProperties: true
                    description:
                      type: string
                    filters:
                      type: object
                      description: 'Filters applied to generate this metric'
                      additionalProperties: true
                    links:
                      type: object
                      description: 'Optional links for drilldown'
                      additionalProperties:
                        type: string
                  required:
                    - data
                example:
                  autoProcessableAwaitingApproval:
                    data: []
                    summary:
                      value: 12
                      valueTotal: 180
                    description: 'Open tickets marked for auto-processing that still require human approval, compared to all open tickets (excluding base agent tickets).'
                    filters:
                      lastDays: '14'
                  requireManualProcessing:
                    data: []
                    summary:
                      value: 45
                      valueTotal: 180
                    description: 'Open tickets where an AI agent was assigned but manual processing is required, compared to all open tickets (excluding base agent tickets).'
                    filters:
                      lastDays: '14'
                  customerCorrectlyIdentified:
                    summary:
                      'Correctly identified': 0.87
                      totalCorrect: 260
                      totalInputs: 300
                    data:
                      '01.01':
                        'Correctly identified': 0.82
                      '02.01':
                        'Correctly identified': 0.9
                    description: 'Work sessions where the customer was correctly identified.'
                    filters:
                      lastDays: '14'
                  correctlyCategorizedWithTags:
                    summary:
                      'Correctly categorized': 0.79
                      totalCorrect: 230
                      totalInputs: 290
                    data:
                      '01.01':
                        'Correctly categorized': 0.75
                      '02.01':
                        'Correctly categorized': 0.83
                    description: 'Work sessions where tags were correctly assigned.'
                    filters:
                      lastDays: '14'
                  textAssistantAccuracy:
                    summary:
                      averageAccuracy: 0.84
                      totalInputs: 150
                    data:
                      '01.01':
                        'Average accuracy': 0.8
                      '02.01':
                        'Average accuracy': 0.88
                    description: 'Average accuracy of the text assistant.'
                    filters:
                      lastDays: '14'
                  autoProcessing:
                    description: 'Daily counts of auto-processed tickets (with approval L4 and fully autonomous L5).'
                    summary:
                      amount: 34
                      withApproval: 20
                      fullyAutonomous: 14
                    data:
                      '01.01':
                        'With Approval': 2
                        'Fully autonomous': 1
                      '02.01':
                        'With Approval': 4
                        'Fully autonomous': 3
                    filters:
                      lastDays: '14'
                  automationLevel:
                    description: 'Daily distribution across automation levels L0..L5.'
                    data:
                      '01.01':
                        L0: 3
                        L1: 2
                        L2: 1
                        L3: 0
                        L4: 2
                        L5: 1
                      '02.01':
                        L0: 2
                        L1: 3
                        L2: 1
                        L3: 1
                        L4: 3
                        L5: 2
                    filters:
                      lastDays: '14'
                  topAiAgents:
                    description: 'Top AI agents for the selected period, including available output handling cases for rule-based agents.'
                    data:
                      -
                        name: Basis-Agent
                        agentId: 1
                        intelligence: rulebased
                        processed: 42
                        awaitingApproval: 12
                        outputHandlingCases:
                          confirm: 'Adjust input parameters'
                          apply: 'Apply text template'
                      -
                        name: Konto-Abgleich
                        agentId: 5
                        intelligence: smart
                        processed: 31
                        awaitingApproval: 8
                        outputHandlingCases: []
                    filters:
                      lastDays: '14'
        '400':
          description: 'Invalid filter parameters'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/aiAgentPerformance/{aiAgentId}':
    get:
      tags:
        - Report
      summary: 'Get AI performance metrics for a specific AI agent'
      description: "Returns performance metrics scoped to a specific AI agent for a selected time window.\n"
      operationId: getAiAgentPerformance
      parameters:
        -
          name: aiAgentId
          in: path
          required: true
          description: 'The AI agent ID'
          schema:
            type: integer
          example: 1
        -
          name: lastDays
          in: query
          required: false
          description: 'Number of days to look back. If omitted, defaults to 7.'
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - '3'
              - '7'
              - '14'
              - '30'
              - '90'
              - '365'
          example: '14'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                description: 'Map of metric name to agent-scoped report item'
                additionalProperties:
                  type: object
                  properties:
                    data:
                      description: 'Metric data (time-series or aggregated values)'
                      additionalProperties: true
                    summary:
                      type: object
                      description: 'Aggregated numbers for the metric'
                      additionalProperties: true
                    description:
                      type: string
                    filters:
                      type: object
                      description: 'Filters applied to generate this metric, including aiAgentId and optionally outputHandlingCase'
                      additionalProperties: true
                  required:
                    - data
                example:
                  autoProcessing:
                    description: 'Number of tickets auto-processed by this AI Agent per day.'
                    summary:
                      amount: 12
                      withApproval: 7
                      fullyAutonomous: 5
                    data:
                      '01.01':
                        'With Approval': 1
                        'Fully autonomous': 0
                      '02.01':
                        'With Approval': 2
                        'Fully autonomous': 2
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                      outputHandlingCase: confirm
                  automationLevel:
                    description: 'Daily distribution across automation levels L0..L5 for this agent.'
                    data:
                      '01.01':
                        L0: 1
                        L1: 1
                        L2: 0
                        L3: 1
                        L4: 1
                        L5: 0
                      '02.01':
                        L0: 0
                        L1: 2
                        L2: 1
                        L3: 0
                        L4: 2
                        L5: 1
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  avgHandlingTimeHuman:
                    data: []
                    summary:
                      averageDurationInSeconds: 320.5
                      totalCount: 14
                      previousAverageDurationInSeconds: 355.2
                      previousTotalCount: 12
                    description: 'Average handling time (seconds) for human cases (L2+L3) for this agent.'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  autoProcessingShare:
                    data: []
                    summary:
                      value: 9
                      valueTotal: 28
                      previousValue: 5
                      previousValueTotal: 30
                    description: 'Share of auto-processed cases (L4+L5) out of all cases worked by this AI Agent.'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  autoProcessingApprovalRate:
                    data: []
                    summary:
                      value: 6
                      valueTotal: 11
                      previousValue: 4
                      previousValueTotal: 10
                    description: 'Rate of auto-processing attempts that were approved for this AI Agent.'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  autoProcessingSuccessRate:
                    data: []
                    summary:
                      value: 8
                      valueTotal: 12
                      previousValue: 6
                      previousValueTotal: 11
                    description: 'Rate of auto-processing attempts that were successful for this AI Agent.'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  approvalTimeL4:
                    data: []
                    summary:
                      averageDurationInSeconds: 210.1
                      totalCount: 7
                      previousAverageDurationInSeconds: 240.0
                      previousTotalCount: 6
                    description: 'Average approval time (seconds) for L4 cases for this AI Agent.'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  autoProcessingErrorRate:
                    data: []
                    summary:
                      value: 2
                      valueTotal: 20
                      previousValue: 3
                      previousValueTotal: 18
                    description: 'Share of cases with errors for this AI Agent.'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  outputHandlingCases:
                    description: 'Available output handling cases for this AI Agent.'
                    data:
                      confirm: 'Adjust input parameters'
                      apply: 'Apply text template'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
                  customerSatisfaction:
                    data: []
                    summary: []
                    description: 'Customer satisfaction results for this AI Agent (placeholder).'
                    filters:
                      aiAgentId: 1
                      lastDays: '14'
        '400':
          description: 'Invalid filter parameters'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /report/topAiAgentsPerformance:
    get:
      tags:
        - Reports
      summary: 'Top-performing AI agents across the time window'
      description: "Ranked list of AI agents by execution volume and quality metrics for the requested period.\nPowers the \"Top AI agents\" widget on the reporting dashboard.\n"
      operationId: getTopAiAgentsPerformance
      parameters:
        -
          name: from
          in: query
          required: false
          description: 'Start of the reporting window (ISO 8601). Defaults to 30 days ago.'
          schema:
            type: string
            format: date
        -
          name: to
          in: query
          required: false
          description: 'End of the reporting window (ISO 8601). Defaults to today.'
          schema:
            type: string
            format: date
        -
          name: limit
          in: query
          required: false
          description: 'Maximum number of agents to return. Defaults to 10.'
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  aiAgents:
                    type: array
                    items:
                      type: object
        '403':
          description: Unauthorized
  '/report/telephonyLine/{lineId}':
    get:
      tags:
        - Report
      summary: 'Get telephony line details'
      description: "Returns detailed information about a specific telephony line (subchannel) including voicebot configuration and connected phone numbers.\n"
      operationId: getTelephonyLineDetails
      parameters:
        -
          name: lineId
          in: path
          required: true
          description: 'The ID of the telephony line (subchannel)'
          schema:
            type: integer
          example: 9
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'Unique identifier for the telephony line'
                        example: 9
                      name:
                        type: string
                        description: 'Internal name of the line'
                        example: 'ACD Bot-to-Agent'
                      status:
                        type: string
                        enum:
                          - active
                          - disabled
                          - error
                        description: 'Current status of the line'
                        example: active
                      liveOverview:
                        type: object
                        description: 'Real-time statistics for this line'
                        properties:
                          inQueue:
                            type: integer
                            description: 'Number of calls currently waiting in queue'
                            example: 3
                          ongoingAiCalls:
                            type: integer
                            description: 'Number of ongoing AI/bot calls'
                            example: 4
                          agentsConnected:
                            type: integer
                            description: 'Number of agents currently connected (not offline)'
                            example: 3
                      phoneNumbers:
                        type: object
                        description: 'Connected phone numbers'
                        properties:
                          inbound:
                            type: array
                            description: 'Inbound phone numbers for this line'
                            items:
                              type: object
                              properties:
                                id:
                                  type: string
                                  description: 'Phone number ID'
                                  example: 0dfe1e36-8145-4940-b41b-f4156447d2be
                                number:
                                  type: string
                                  description: 'Phone number in E.164 format'
                                  example: '+4930754358631'
                          outbound:
                            type: object
                            nullable: true
                            description: 'Outbound phone number for this line'
                            properties:
                              id:
                                type: string
                                description: 'Phone number ID'
                                example: 0dfe1e36-8145-4940-b41b-f4156447d2be
                              number:
                                type: string
                                description: 'Phone number in E.164 format'
                                example: '+4930754358631'
                      liveCalls:
                        type: array
                        description: 'Currently active calls on this line. Passthrough from ACD; empty until ACD MR 2 ships.'
                        items:
                          type: object
                          required:
                            - callId
                            - from
                            - state
                            - durationSeconds
                          properties:
                            callId:
                              type: string
                              description: 'Unique call identifier'
                              example: 550e8400-e29b-41d4-a716-446655440000
                            ticketId:
                              type: integer
                              description: 'Associated ticket ID (omitted if not yet linked)'
                              example: 12345
                            from:
                              type: string
                              description: 'Caller phone number'
                              example: '+4930754358631'
                            state:
                              type: string
                              enum:
                                - waiting
                                - bot
                                - human
                                - forwarded
                              description: 'Current call state'
                              example: human
                            agentUserId:
                              type: integer
                              description: 'ID of the connected agent (omitted if not applicable)'
                              example: 42
                            agentName:
                              type: string
                              description: 'Name of the connected agent (omitted if not applicable)'
                              example: 'Jenny Kaspar'
                            waitSeconds:
                              type: integer
                              description: 'Seconds spent waiting in queue (present only when state is waiting)'
                              example: 12
                            durationSeconds:
                              type: integer
                              description: 'Total call duration in seconds since call started'
                              example: 30
                  description:
                    type: string
                    description: 'Description of what this report shows'
                    example: 'Detailed information about a specific telephony line including voicebot configuration and connected phone numbers.'
                  filters:
                    type: object
                    description: 'Filters applied to this report'
                example:
                  data:
                    id: 11
                    name: 'ACD Bot-to-Agent'
                    status: active
                    liveOverview:
                      inQueue: 3
                      ongoingAiCalls: 4
                      agentsConnected: 3
                    phoneNumbers:
                      inbound:
                        -
                          id: 0dfe1e36-8145-4940-b41b-f4156447d2be
                          number: '+4930754358631'
                      outbound:
                        id: 0dfe1e36-8145-4940-b41b-f4156447d2be
                        number: '+4930754358631'
                    liveCalls:
                      -
                        callId: 550e8400-e29b-41d4-a716-446655440000
                        ticketId: 12345
                        from: '+4930754358631'
                        state: human
                        agentUserId: 42
                        agentName: 'Jenny Kaspar'
                        durationSeconds: 30
                  description: 'Detailed information about a specific telephony line including voicebot configuration and connected phone numbers.'
                  filters: []
        '403':
          description: Unauthorized
        '404':
          description: 'Telephony line not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/telephonyAgent/{agentId}':
    get:
      tags:
        - Report
      summary: 'Get telephony agent details'
      description: "Returns detailed information about a specific telephony agent including their status and team assignments.\n"
      operationId: getTelephonyAgentDetails
      parameters:
        -
          name: agentId
          in: path
          required: true
          description: 'The ID of the telephony agent (user)'
          schema:
            type: integer
          example: 3
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    description: 'Agent ID'
                    example: 3
                  name:
                    type: string
                    description: 'Agent name'
                    example: 'Dennis Sommer'
                  callRoutingStatus:
                    type: string
                    enum:
                      - idle
                      - interacting
                      - beingConnected
                      - notResponding
                      - unavailable
                      - offline
                      - acw
                    description: 'Current telephony routing status'
                    example: idle
                  teamIds:
                    type: array
                    items:
                      type: integer
                    description: 'Array of team IDs the agent belongs to'
                    example:
                      - 1
                      - 2
                  channels:
                    type: array
                    items:
                      type: string
                      enum:
                        - email
                        - portal
                        - phone
                        - system
                        - chat
                        - walkIn
                        - letter
                    description: 'Array of channels the agent is skilled to handle'
                    example:
                      - phone
                      - chat
                  liveOverview:
                    type: object
                    description: 'Real-time statistics for this agent today'
                    properties:
                      callsToday:
                        type: integer
                        description: 'Number of calls handled today'
                        example: 13
                      talkTimeSeconds:
                        type: integer
                        description: 'Total talk time in seconds today'
                        example: 1934
                      acwTimeSeconds:
                        type: integer
                        description: 'Total After Call Work time in seconds today'
                        example: 767
                      missed:
                        type: integer
                        description: 'Number of missed calls today'
                        example: 1
                example:
                  id: 3
                  name: 'Dennis Sommer'
                  callRoutingStatus: idle
                  teamIds:
                    - 1
                    - 2
                  channels:
                    - phone
                    - chat
                  liveOverview:
                    callsToday: 13
                    talkTimeSeconds: 1934
                    acwTimeSeconds: 767
                    missed: 1
        '403':
          description: Unauthorized
        '404':
          description: 'Telephony agent not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/telephonyAgentPerformance/{agentId}':
    get:
      tags:
        - Report
      summary: 'Get telephony agent performance metrics'
      description: "Returns performance metrics for a specific telephony agent over time.\nSimilar to telephonyPerformance, returns 5 metrics: answered calls, missed calls, answer rate, average wait time, and average handling time.\n"
      operationId: getTelephonyAgentPerformance
      parameters:
        -
          name: agentId
          in: path
          required: true
          description: 'The ID of the telephony agent (user)'
          schema:
            type: integer
          example: 3
        -
          name: lastDays
          in: query
          required: false
          description: 'Number of days to look back'
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - '3'
              - '7'
              - '14'
              - '30'
              - '90'
              - '365'
            default: '7'
          example: '7'
        -
          name: granularity
          in: query
          required: false
          description: "Time bucket size for the time-series data. When omitted, defaults to 'day' (dd.mm keys, same as before).\n- hour: one bucket per clock-hour (YYYY-MM-DD HH:00 keys); only allowed with lastDays ≤ 31\n- day: one bucket per calendar day (dd.mm keys)\n- week: one bucket per ISO week (YYYY-WNN keys)\n- month: one bucket per calendar month (YYYY-MM keys)\n"
          schema:
            type: string
            enum:
              - hour
              - day
              - week
              - month
          example: week
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  answeredCalls:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Answered calls per day'
                        example:
                          '28.11':
                            'Answered Calls': 12
                          '29.11':
                            'Answered Calls': 15
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          total:
                            type: integer
                  missedCalls:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Missed calls per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          total:
                            type: integer
                  answerRate:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Answer rate percentage per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          average:
                            type: integer
                  averageWaitTime:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Average wait time in seconds per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          average:
                            type: integer
                  averageHandlingTime:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Average handling time in seconds per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          average:
                            type: integer
                example:
                  answeredCalls:
                    data:
                      '28.11':
                        'Answered Calls': 12
                      '29.11':
                        'Answered Calls': 15
                    description: 'Number of answered calls per day in the selected period.'
                    filters:
                      lastDays: '7'
                    summary:
                      total: 512
                  missedCalls:
                    data:
                      '28.11':
                        'Missed Calls': 2
                      '29.11':
                        'Missed Calls': 1
                    description: 'Number of missed calls per day in the selected period.'
                    filters:
                      lastDays: '7'
                    summary:
                      total: 49
                  answerRate:
                    data:
                      '28.11':
                        'Answer Rate': 89
                      '29.11':
                        'Answer Rate': 91
                    description: 'Answer rate percentage per day (answered / total calls).'
                    filters:
                      lastDays: '7'
                    summary:
                      average: 89
                  averageWaitTime:
                    data:
                      '28.11':
                        'Average Wait Time': 8
                      '29.11':
                        'Average Wait Time': 7
                    description: 'Average wait time in seconds before call was answered per day.'
                    filters:
                      lastDays: '7'
                    summary:
                      average: 8
                  averageHandlingTime:
                    data:
                      '28.11':
                        'Average Handling Time': 97
                      '29.11':
                        'Average Handling Time': 102
                    description: 'Average handling time in seconds per day.'
                    filters:
                      lastDays: '7'
                    summary:
                      average: 97
        '403':
          description: Unauthorized
        '404':
          description: 'Telephony agent not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/telephonyAgentCallInsights/{agentId}':
    get:
      tags:
        - Report
      summary: 'Get telephony agent call insights'
      description: "Returns detected intents data for a specific telephony agent.\nShows distribution of customer intents identified in the agent's calls.\n"
      operationId: getTelephonyAgentCallInsights
      parameters:
        -
          name: agentId
          in: path
          required: true
          description: 'The ID of the telephony agent (user)'
          schema:
            type: integer
          example: 3
        -
          name: lastDays
          in: query
          required: false
          description: 'Number of days to look back'
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - '3'
              - '7'
              - '14'
              - '30'
              - '90'
              - '365'
            default: '7'
          example: '7'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  detectedIntents:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: integer
                        description: 'Intent names with their counts'
                        example:
                          Allgemein: 27
                          Mako: 35
                          Rechnung: 15
                          Lorem: 28
                          Ipsum: 10
                      description:
                        type: string
                        example: 'Distribution of detected customer intents for this agent across all calls in the selected period.'
                      filters:
                        type: object
                        properties:
                          lastDays:
                            type: string
                            example: '7'
                      summary:
                        type: object
                        properties:
                          total:
                            type: integer
                            description: 'Total number of intents detected'
                            example: 115
                          percentages:
                            type: object
                            additionalProperties:
                              type: integer
                            description: 'Percentage distribution of intents'
                            example:
                              Allgemein: 23
                              Mako: 30
                              Rechnung: 13
                              Lorem: 24
                              Ipsum: 9
                          counts:
                            type: object
                            additionalProperties:
                              type: integer
                            description: 'Absolute per-intent call counts (raw numbers before percentage conversion)'
                            example:
                              Allgemein: 27
                              Mako: 35
                              Rechnung: 15
                              Lorem: 28
                              Ipsum: 10
                example:
                  detectedIntents:
                    data:
                      Allgemein: 27
                      Mako: 35
                      Rechnung: 15
                      Lorem: 28
                      Ipsum: 10
                    description: 'Distribution of detected customer intents for this agent across all calls in the selected period.'
                    filters:
                      lastDays: '7'
                    summary:
                      total: 115
                      percentages:
                        Allgemein: 23
                        Mako: 30
                        Rechnung: 13
                        Lorem: 24
                        Ipsum: 9
        '403':
          description: Unauthorized
        '404':
          description: 'Telephony agent not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/telephonyAiAgent/{aiAgentId}':
    get:
      tags:
        - Report
      summary: 'Get telephony AI agent details'
      description: "Returns detailed information about a specific telephony AI agent.\nIncludes AI agent ID and live overview statistics.\n"
      operationId: getTelephonyAiAgentDetails
      parameters:
        -
          name: aiAgentId
          in: path
          required: true
          description: 'The ID of the telephony AI agent'
          schema:
            type: integer
          example: 5
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    description: 'AI agent ID'
                    example: 5
                  liveOverview:
                    type: object
                    properties:
                      callsToday:
                        type: integer
                        description: 'Number of calls handled today'
                        example: 12
                      talkTimeSeconds:
                        type: integer
                        description: 'Total talk time in seconds today'
                        example: 1365
                      transferred:
                        type: integer
                        description: 'Number of calls transferred to human agents today'
                        example: 3
                example:
                  id: 5
                  liveOverview:
                    callsToday: 12
                    talkTimeSeconds: 1365
                    transferred: 3
        '403':
          description: Unauthorized
        '404':
          description: 'Telephony AI agent not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/telephonyAiAgentPerformance/{aiAgentId}':
    get:
      tags:
        - Report
      summary: 'Get telephony AI agent performance metrics'
      description: "Returns performance metrics for a specific telephony AI agent over time.\nReturns 5 metrics: answered calls, missed calls, service level, average wait time, and average handling time.\n"
      operationId: getTelephonyAiAgentPerformance
      parameters:
        -
          name: aiAgentId
          in: path
          required: true
          description: 'The ID of the telephony AI agent'
          schema:
            type: integer
          example: 5
        -
          name: lastDays
          in: query
          required: false
          description: 'Number of days to look back'
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - '3'
              - '7'
              - '14'
              - '30'
              - '90'
              - '365'
            default: '7'
          example: '7'
        -
          name: granularity
          in: query
          required: false
          description: "Time bucket size for the time-series data. When omitted, defaults to 'day' (dd.mm keys, same as before).\n- hour: one bucket per clock-hour (YYYY-MM-DD HH:00 keys); only allowed with lastDays ≤ 31\n- day: one bucket per calendar day (dd.mm keys)\n- week: one bucket per ISO week (YYYY-WNN keys)\n- month: one bucket per calendar month (YYYY-MM keys)\n"
          schema:
            type: string
            enum:
              - hour
              - day
              - week
              - month
          example: week
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  answeredCalls:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Answered calls per day'
                        example:
                          '28.11':
                            'Answered Calls': 45
                          '29.11':
                            'Answered Calls': 52
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          total:
                            type: integer
                  missedCalls:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Missed calls per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          total:
                            type: integer
                  serviceLevel:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Service level percentage per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          average:
                            type: integer
                  averageWaitTime:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Average wait time in seconds per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          average:
                            type: integer
                  averageHandlingTime:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                        description: 'Average handling time in seconds per day'
                      description:
                        type: string
                      filters:
                        type: object
                      summary:
                        type: object
                        properties:
                          average:
                            type: integer
                example:
                  answeredCalls:
                    data:
                      '28.11':
                        'Answered Calls': 45
                      '29.11':
                        'Answered Calls': 52
                    description: 'Number of calls answered by AI per day in the selected period.'
                    filters:
                      lastDays: '7'
                    summary:
                      total: 350
                  missedCalls:
                    data:
                      '28.11':
                        'Missed Calls': 4
                      '29.11':
                        'Missed Calls': 3
                    description: 'Number of missed calls per day in the selected period.'
                    filters:
                      lastDays: '7'
                    summary:
                      total: 28
                  serviceLevel:
                    data:
                      '28.11':
                        'Service Level': 92
                      '29.11':
                        'Service Level': 95
                    description: 'Service level percentage per day (calls answered within SLA).'
                    filters:
                      lastDays: '7'
                    summary:
                      average: 93
                  averageWaitTime:
                    data:
                      '28.11':
                        'Average Wait Time': 6
                      '29.11':
                        'Average Wait Time': 5
                    description: 'Average wait time in seconds before call was answered by AI per day.'
                    filters:
                      lastDays: '7'
                    summary:
                      average: 6
                  averageHandlingTime:
                    data:
                      '28.11':
                        'Average Handling Time': 85
                      '29.11':
                        'Average Handling Time': 92
                    description: 'Average handling time in seconds by AI per day.'
                    filters:
                      lastDays: '7'
                    summary:
                      average: 88
        '403':
          description: Unauthorized
        '404':
          description: 'Telephony AI agent not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/report/telephonyAiAgentCallInsights/{aiAgentId}':
    get:
      tags:
        - Report
      summary: 'Get telephony AI agent call insights'
      description: "Returns hourly breakdown of call outcomes for a specific telephony AI agent.\nShows how calls were handled throughout the day (0-23 hours).\n"
      operationId: getTelephonyAiAgentCallInsights
      parameters:
        -
          name: aiAgentId
          in: path
          required: true
          description: 'The ID of the telephony AI agent'
          schema:
            type: integer
          example: 5
        -
          name: lastDays
          in: query
          required: false
          description: 'Number of days to look back'
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - '3'
              - '7'
              - '14'
              - '30'
              - '90'
              - '365'
            default: '7'
          example: '7'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  callsAnsweredByHour:
                    type: object
                    properties:
                      data:
                        type: object
                        additionalProperties:
                          type: object
                          properties:
                            'AI Bot finished':
                              type: integer
                            'Handed over to human':
                              type: integer
                            Declined/Missed:
                              type: integer
                            'Short call (<10s)':
                              type: integer
                        description: 'Hourly breakdown (0-23) of call outcomes'
                        example:
                          '0':
                            'AI Bot finished': 2
                            'Handed over to human': 5
                            Declined/Missed: 0
                            'Short call (<10s)': 1
                          '1':
                            'AI Bot finished': 3
                            'Handed over to human': 4
                            Declined/Missed: 0
                            'Short call (<10s)': 1
                          '8':
                            'AI Bot finished': 2
                            'Handed over to human': 7
                            Declined/Missed: 8
                            'Short call (<10s)': 0
                      description:
                        type: string
                        example: 'Hourly breakdown of call outcomes showing how calls were handled by AI throughout the day.'
                      filters:
                        type: object
                        properties:
                          lastDays:
                            type: string
                            example: '7'
                      summary:
                        type: object
                        properties:
                          aiBotFinished:
                            type: integer
                            description: 'Total calls finished by AI bot'
                            example: 53
                          handedOver:
                            type: integer
                            description: 'Total calls handed over to human'
                            example: 113
                          declinedMissed:
                            type: integer
                            description: 'Total declined/missed calls'
                            example: 96
                          shortCall:
                            type: integer
                            description: 'Total short calls (<10s)'
                            example: 18
                example:
                  callsAnsweredByHour:
                    data:
                      '0':
                        'AI Bot finished': 2
                        'Handed over to human': 5
                        Declined/Missed: 0
                        'Short call (<10s)': 1
                      '1':
                        'AI Bot finished': 3
                        'Handed over to human': 4
                        Declined/Missed: 0
                        'Short call (<10s)': 1
                      '2':
                        'AI Bot finished': 3
                        'Handed over to human': 2
                        Declined/Missed: 0
                        'Short call (<10s)': 1
                      '8':
                        'AI Bot finished': 2
                        'Handed over to human': 7
                        Declined/Missed: 8
                        'Short call (<10s)': 0
                      '12':
                        'AI Bot finished': 3
                        'Handed over to human': 3
                        Declined/Missed: 8
                        'Short call (<10s)': 1
                      '20':
                        'AI Bot finished': 1
                        'Handed over to human': 9
                        Declined/Missed: 0
                        'Short call (<10s)': 1
                      '23':
                        'AI Bot finished': 3
                        'Handed over to human': 5
                        Declined/Missed: 0
                        'Short call (<10s)': 1
                    description: 'Hourly breakdown of call outcomes showing how calls were handled by AI throughout the day.'
                    filters:
                      lastDays: '7'
                    summary:
                      aiBotFinished: 53
                      handedOver: 113
                      declinedMissed: 96
                      shortCall: 18
        '403':
          description: Unauthorized
        '404':
          description: 'Telephony AI agent not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /quality/scorecard:
    get:
      tags:
        - 'Quality Management'
      summary: 'List quality scorecards'
      description: 'Get a list of all quality scorecards with optional state filter'
      operationId: listQualityScorecards
      parameters:
        -
          name: state
          in: query
          description: "Filter by scorecard state (comma-separated list). Allowed values: draft, active, retired, deleted. Defaults to only active scorecards if not provided.\n"
          required: false
          schema:
            type: string
            example: 'draft,active'
            default: active
      responses:
        '200':
          description: 'List of quality scorecards'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/QualityScorecard'
              examples:
                default:
                  summary: 'Active scorecards ordered by last modification'
                  value:
                    -
                      id: 42
                      baseId: 17
                      revision: 2
                      name: 'Email QA - Tier 1'
                      state: active
                      categories:
                        -
                          categoryId: 1
                          label: Opening
                          order: 1
                          criteria:
                            -
                              id: 10
                              label: Greeting
                              description: 'Did the agent greet the customer properly?'
                              maxPoints: 5
                              scoringType: numericScale
                              autoGenerateByAi: false
                              assessmentPrompt: 'Score the greeting quality'
                              makeOrBreakForCategory: false
                              makeOrBreakForAssessment: false
                      assignment:
                        ticketTags:
                          - 101
                          - 102
                        teams:
                          - 5
                        channels:
                          - email
                      createdAt: '2024-05-12T09:41:00Z'
                      modifiedAt: '2024-06-01T10:00:00Z'
                      canEdit: true
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
        - 'Quality Management'
      summary: 'Create a new quality scorecard'
      description: 'Creates a new quality scorecard. Requires qualityManageScorecards permission.'
      operationId: createQualityScorecard
      requestBody:
        required: true
        description: 'Provide the complete scorecard payload as JSON.'
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - categories
              properties:
                name:
                  type: string
                  description: 'Name of the scorecard'
                  example: 'Junior agent standard review'
                state:
                  type: string
                  enum:
                    - draft
                    - active
                  default: active
                  description: 'State of the new scorecard (defaults to active if omitted)'
                categories:
                  type: array
                  description: 'Categories with nested criteria definitions'
                  items:
                    type: object
                    additionalProperties: true
                  example:
                    -
                      categoryId: 1
                      label: Opening
                      order: 1
                      criteria:
                        -
                          id: 10
                          label: Greeting
                          description: 'Did the agent greet the customer properly?'
                          maxPoints: 5
                          scoringType: numericScale
                          autoGenerateByAi: false
                          assessmentPrompt: 'Evaluate the opening.'
                          makeOrBreakForCategory: false
                          makeOrBreakForAssessment: false
                assignment:
                  type: object
                  description: 'Assignment rules (optional). Omitted fields default to empty (no filter).'
                  properties:
                    ticketTags:
                      type: array
                      items:
                        type: integer
                    teams:
                      type: array
                      items:
                        type: integer
                    channels:
                      type: array
                      items:
                        type: string
                    responseContexts:
                      type: array
                      description: "Work-session contexts to match. Empty = no filter. Defaults to ['afterCustomerMessage'] for new scorecards.\n"
                      items:
                        type: string
                        enum:
                          - afterCustomerMessage
                          - withoutCustomerMessage
                    excludeTicketTags:
                      type: array
                      description: 'Negation of ticketTags — a ticket carrying any of these tags is never assessed.'
                      items:
                        type: integer
                    excludeTeams:
                      type: array
                      description: 'Negation of teams — an agent in any of these teams is never assessed, even if also in an included team.'
                      items:
                        type: integer
                    excludeChannels:
                      type: array
                      description: 'Negation of channels — tickets on any of these channels are never assessed.'
                      items:
                        type: string
                  example:
                    ticketTags:
                      - 101
                      - 102
                    teams:
                      - 5
                      - 7
                    channels:
                      - email
                    responseContexts:
                      - afterCustomerMessage
                    excludeTeams:
                      - 9
                liveCoach:
                  type: object
                  description: 'Live Quality Coach configuration (optional, defaults to disabled)'
                  properties:
                    enabled:
                      type: boolean
                      default: false
                    threshold:
                      type: integer
                      minimum: 0
                      maximum: 100
                      default: 85
                  example:
                    enabled: true
                    threshold: 80
            example:
              name: 'Voice - Tier 2 escalation'
              state: active
              categories:
                -
                  categoryId: 1
                  label: Greeting
                  order: 1
                  criteria:
                    -
                      id: 100
                      label: Tone
                      description: 'Agent greets the customer within 10 seconds'
                      maxPoints: 5
                      scoringType: numericScale
                      autoGenerateByAi: false
                      assessmentPrompt: 'Score how friendly the greeting was.'
                      makeOrBreakForCategory: false
                      makeOrBreakForAssessment: true
              assignment:
                ticketTags:
                  - 88
                teams:
                  - 12
                  - 14
                channels:
                  - phone
              liveCoach:
                enabled: false
                threshold: 85
      responses:
        '200':
          description: 'Scorecard created successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    description: 'ID of the created scorecard'
                  baseId:
                    type: integer
                    description: 'Base ID of the created scorecard (used for future revisions)'
              examples:
                default:
                  summary: 'Newly created scorecard identifiers'
                  value:
                    id: 58
                    baseId: 58
        '400':
          description: 'Bad request (e.g., missing required fields)'
        '403':
          description: 'Unauthorized (missing qualityManageScorecards permission)'
        '500':
          description: 'Internal error'
  '/quality/scorecard/{id}':
    get:
      tags:
        - 'Quality Management'
      summary: 'Get a specific quality scorecard'
      description: 'Retrieve full details of a quality scorecard by ID'
      operationId: getQualityScorecard
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Scorecard ID'
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QualityScorecard'
              examples:
                default:
                  summary: 'Detailed scorecard with criteria'
                  value:
                    id: 42
                    baseId: 17
                    revision: 3
                    name: 'Email QA - Tier 1'
                    state: active
                    categories:
                      -
                        categoryId: 1
                        label: Opening
                        order: 1
                        criteria:
                          -
                            id: 10
                            label: Greeting
                            description: 'Did the agent greet the customer properly?'
                            maxPoints: 5
                            scoringType: numericScale
                            autoGenerateByAi: false
                            assessmentPrompt: 'Evaluate the opening.'
                            makeOrBreakForCategory: false
                            makeOrBreakForAssessment: false
                    assignment:
                      ticketTags:
                        - 101
                        - 102
                      teams:
                        - 5
                      channels:
                        - email
                    createdAt: '2024-05-12T09:41:00Z'
                    modifiedAt: '2024-06-01T10:00:00Z'
                    canEdit: true
        '403':
          description: Unauthorized
        '404':
          description: 'Scorecard not found'
        '500':
          description: 'Internal error'
    patch:
      tags:
        - 'Quality Management'
      summary: 'Update a quality scorecard'
      description: 'Update an existing quality scorecard. Requires qualityManageScorecards permission.'
      operationId: updateQualityScorecard
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Scorecard ID'
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'Name of the scorecard'
                state:
                  type: string
                  enum:
                    - draft
                    - active
                    - retired
                    - deleted
                  description: 'State of the scorecard'
                categories:
                  type: array
                  description: 'Categories with nested criteria definitions'
                  items:
                    type: object
                    additionalProperties: true
                assignment:
                  type: object
                  description: 'Assignment rules (optional). Omitted fields are cleared to empty (no filter).'
                  properties:
                    ticketTags:
                      type: array
                      items:
                        type: integer
                    teams:
                      type: array
                      items:
                        type: integer
                    channels:
                      type: array
                      items:
                        type: string
                    responseContexts:
                      type: array
                      description: "Work-session contexts to match. Empty = no filter.\n"
                      items:
                        type: string
                        enum:
                          - afterCustomerMessage
                          - withoutCustomerMessage
                    excludeTicketTags:
                      type: array
                      description: 'Negation of ticketTags — a ticket carrying any of these tags is never assessed.'
                      items:
                        type: integer
                    excludeTeams:
                      type: array
                      description: 'Negation of teams — an agent in any of these teams is never assessed, even if also in an included team.'
                      items:
                        type: integer
                    excludeChannels:
                      type: array
                      description: 'Negation of channels — tickets on any of these channels are never assessed.'
                      items:
                        type: string
                liveCoach:
                  type: object
                  description: 'Live Quality Coach configuration. Only updated when the field is present in the payload.'
                  properties:
                    enabled:
                      type: boolean
                    threshold:
                      type: integer
                      minimum: 0
                      maximum: 100
                createNewRevision:
                  type: boolean
                  default: true
                  description: 'If true (default) and the scorecard is active, creates a new revision instead of overwriting.'
            example:
              name: 'Voice - Tier 2 escalation (v2)'
              categories:
                -
                  categoryId: 1
                  label: Greeting
                  order: 1
                  criteria:
                    -
                      id: 100
                      label: Tone
                      description: 'Agent greets the customer within 10 seconds'
                      maxPoints: 5
                      scoringType: numericScale
                      autoGenerateByAi: false
                      assessmentPrompt: 'Score how friendly the greeting was.'
                      makeOrBreakForCategory: false
                      makeOrBreakForAssessment: true
              assignment:
                teams:
                  - 12
                channels:
                  - phone
              createNewRevision: false
      responses:
        '200':
          description: 'Scorecard updated successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  scorecard:
                    $ref: '#/components/schemas/QualityScorecard'
                  message:
                    type: string
                    nullable: true
                    description: 'Set when the server transparently redirected the update to the latest revision.'
              examples:
                default:
                  summary: 'Active scorecard updated without new revision'
                  value:
                    scorecard:
                      id: 42
                      baseId: 17
                      revision: 3
                      name: 'Email QA - Tier 1'
                      state: active
                      categories: []
                      assignment:
                        teams:
                          - 5
                        channels:
                          - email
                      modifiedAt: '2024-06-01T10:30:00Z'
                      canEdit: true
                    message: null
                redirected:
                  summary: 'Update redirected to the latest revision'
                  value:
                    scorecard:
                      id: 50
                      baseId: 17
                      revision: 4
                      name: 'Email QA - Tier 1'
                      state: active
                      categories: []
                      assignment: null
                      modifiedAt: '2024-07-02T11:00:00Z'
                      canEdit: true
                    message: 'You want to update an old version of a scorecard. We gracefully use the most recent version (ID: 50) instead'
        '403':
          description: Unauthorized
        '404':
          description: 'Scorecard not found'
        '500':
          description: 'Internal error'
    delete:
      tags:
        - 'Quality Management'
      summary: 'Delete a quality scorecard'
      description: 'Soft-delete a quality scorecard. Requires qualityDeleteScorecard permission.'
      operationId: deleteQualityScorecard
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Scorecard ID'
          schema:
            type: integer
      responses:
        '200':
          description: 'Scorecard deleted successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: 'Scorecard deleted successfully'
              examples:
                default:
                  summary: 'Successful soft-delete confirmation'
                  value:
                    message: 'Scorecard deleted successfully'
        '403':
          description: 'Unauthorized (missing qualityDeleteScorecard permission)'
        '404':
          description: 'Scorecard not found'
        '500':
          description: 'Internal error'
  /quality/assessment:
    get:
      tags:
        - 'Quality Management'
      summary: 'List quality assessments'
      description: 'Get a list of quality assessments with optional filters. Respects view permissions.'
      operationId: listQualityAssessments
      parameters:
        -
          name: assessedUserId
          in: query
          description: "Filter by a single assessed user ID. Must be within the caller's viewable audience (own < teamMate < all) or a 403 is returned."
          required: false
          schema:
            type: integer
            example: 42
        -
          name: assessedTeamId
          in: query
          description: "Filter by the current members of a single team (resolved via `actualTeamIds`). Mutually exclusive with `assessedUserId` (`assessedUserId` wins). Must be within the caller's viewable audience (`qualityViewAssessmentAll` → any team; `qualityViewAssessmentTeamMate` → own teams) or a 403 is returned.\n"
          required: false
          schema:
            type: integer
            example: 30
        -
          name: scorecardId
          in: query
          description: "Filter by scorecard base ID (matches all revisions of that scorecard). Mirrors the QA report endpoint's `scorecardId`."
          required: false
          schema:
            type: integer
            example: 5
        -
          name: lastDays
          in: query
          description: 'Relative date window (days). Mirrors the QA report endpoints. Overridden by explicit `from`/`to`.'
          required: false
          schema:
            type: integer
            enum:
              - 0
              - 1
              - 3
              - 7
              - 14
              - 30
              - 90
              - 365
            default: 30
            example: 7
        -
          name: from
          in: query
          description: 'Inclusive start date (`YYYY-MM-DD`) — filters by `createdAt`. Optional override of `lastDays`.'
          required: false
          schema:
            type: string
            format: date
            example: '2024-06-01'
        -
          name: to
          in: query
          description: 'Inclusive end date (`YYYY-MM-DD`) — filters by `createdAt`, end-of-day. Optional override of `lastDays`.'
          required: false
          schema:
            type: string
            format: date
            example: '2024-06-30'
        -
          name: limit
          in: query
          description: 'Maximum number of assessments to return.'
          required: false
          schema:
            type: integer
            default: 100
            example: 100
        -
          name: offset
          in: query
          description: 'Number of assessments to skip (for pagination).'
          required: false
          schema:
            type: integer
            default: 0
            example: 0
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  total:
                    type: integer
                    description: 'Total number of assessments matching the filters (ignoring limit/offset), for pagination.'
                  assessments:
                    type: array
                    description: 'Lean assessment rows for the list/table view (one query, name JOINs, stored percentage).'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        ticketId:
                          type: integer
                          nullable: true
                        worklogId:
                          type: integer
                          nullable: true
                        conversationId:
                          type: integer
                          nullable: true
                        assessedUser:
                          type: integer
                        assessedUserName:
                          type: string
                          nullable: true
                        scorecardId:
                          type: integer
                        scorecardBaseId:
                          type: integer
                          nullable: true
                        scorecardName:
                          type: string
                          nullable: true
                        state:
                          type: string
                        percentage:
                          type: number
                          description: 'Overall score percentage (0 until scored).'
                        createdAt:
                          type: string
                        modifiedAt:
                          type: string
              examples:
                default:
                  summary: 'Lean rows matching filters and permissions'
                  value:
                    total: 1
                    assessments:
                      -
                        id: 901
                        ticketId: 8821
                        worklogId: 551
                        conversationId: 331
                        assessedUser: 23
                        assessedUserName: 'Alice Anderson'
                        scorecardId: 42
                        scorecardBaseId: 17
                        scorecardName: 'Agent Example Template (English)'
                        state: reviewOngoing
                        percentage: 72.0
                        createdAt: '2024-06-12T10:20:00Z'
                        modifiedAt: '2024-06-12T10:45:00Z'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
  '/quality/assessment/{id}':
    get:
      tags:
        - 'Quality Management'
      summary: 'Get a specific quality assessment'
      description: 'Retrieve a quality assessment in full format. User must have permission to view this assessment.'
      operationId: getQualityAssessment
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Assessment ID'
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QualityAssessment'
              examples:
                default:
                  summary: 'Full assessment with complete scoring data'
                  value:
                    id: 901
                    scorecardId: 42
                    scorecardBaseId: 17
                    state: reviewedBySupervisor
                    assessedUser: 23
                    assessedByUser: 12
                    ticketId: 8821
                    worklogId: 551
                    createdAt: '2024-06-12T10:20:00Z'
                    modifiedAt: '2024-06-12T11:05:00Z'
                    deletedAt: null
                    canEdit: true
                    data:
                      aiSummary: 'AI summary of the conversation...'
                      categories:
                        -
                          categoryId: 1
                          criteria:
                            -
                              id: 10
                              score: 4
                              reason: 'Greeting was warm.'
                              state: humanVerified
                              lastEditedByUserId: 12
                              lastEditedAt: '2024-06-12T10:55:00Z'
                          totals:
                            scoredPoints: 4
                            usedMaxPoints: 5
                            totalMaxPoints: 5
                            percentage: 80.0
                        -
                          categoryId: 2
                          criteria:
                            -
                              id: 22
                              score: 3
                              reason: 'Needs to confirm resolution steps.'
                              state: humanVerified
                              lastEditedByUserId: 12
                              lastEditedAt: '2024-06-12T10:57:00Z'
                          totals:
                            scoredPoints: 14
                            usedMaxPoints: 15
                            totalMaxPoints: 20
                            percentage: 93.3
                      totals:
                        scoredPoints: 18
                        usedMaxPoints: 20
                        totalMaxPoints: 25
                        percentage: 72.0
                      supervisorAssessment: 'Great rapport, focus on closing next time.'
                      assessmentDate: '2024-06-12T11:00:00Z'
                      discussionDate: null
                    editingHistory:
                      -
                        userId: 12
                        timestamp: '2024-06-12T10:55:00Z'
                        field: criteria.1.10.score
                        oldValue: 3
                        newValue: 4
                    worklog:
                      userId: 23
                      worklogId: 551
                      lastInteraction: '2024-06-12T09:59:00Z'
                      ticketId: 8821
                      conversationId: 331
                      action: reply
                      actionList:
                        - reply
                        - internal-note
                    aiUsage:
                      automationLevel: 2
                      automationLevelLabel: Assisted
                      aiAgents: []
                    time:
                      duration: 480
                      durationAfterWork: 120
                      segments: []
                    customerExperience:
                      slaMet: true
                      netSecondsClosedAfterSla: -30
                      closingDateForSLA: '2024-06-12T09:40:00Z'
                      dueDateForSLA: '2024-06-12T10:00:00Z'
                      reOpened: false
                      csat: null
        '403':
          description: 'Unauthorized (no permission to view this assessment)'
        '404':
          description: 'Assessment not found'
        '500':
          description: 'Internal error'
    patch:
      tags:
        - 'Quality Management'
      summary: 'Update a quality assessment'
      description: "Update an assessment including scores, reasons, supervisor notes, and dates.\nUser must have permission to edit this assessment (qualityDoAssessmentsTeam or qualityDoAssessmentsAll).\n\nAutomatically transitions state from aiReady to reviewOngoing when edits are made.\n"
      operationId: updateQualityAssessment
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Assessment ID'
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                aiSummary:
                  type: string
                  description: 'AI-generated summary (editable by supervisors)'
                supervisorAssessment:
                  type: string
                  description: "Supervisor's free-form assessment text"
                assessmentDate:
                  type: string
                  format: date-time
                  description: 'Date when assessment was completed'
                discussionDate:
                  type: string
                  format: date-time
                  description: 'Date when assessment was discussed with assessee'
                criteria:
                  type: array
                  description: 'Array of criterion updates'
                  items:
                    type: object
                    required:
                      - categoryId
                      - criterionId
                    properties:
                      categoryId:
                        type: integer
                        description: 'Category identifier from the scorecard definition'
                      criterionId:
                        type: integer
                        description: 'Criterion identifier within the category'
                      score:
                        type: number
                        description: 'Optional updated score'
                      reason:
                        type: string
                        description: 'Optional free-form reason'
                  example:
                    -
                      categoryId: 1
                      criterionId: 2
                      score: 5
                      reason: 'Great intro'
                state:
                  type: string
                  enum:
                    - reviewedBySupervisor
                    - discussedWithAssessee
                  description: 'Transition to a new state'
            example:
              aiSummary: 'Updated AI summary after listening to the call.'
              supervisorAssessment: 'Focus on clearer closing.'
              assessmentDate: '2024-06-12T11:00:00Z'
              discussionDate: '2024-06-13T09:00:00Z'
              criteria:
                -
                  categoryId: 1
                  criterionId: 2
                  score: 5
                  reason: 'Great intro'
                -
                  categoryId: 2
                  criterionId: 6
                  reason: 'Added more context for the closing paragraph.'
              state: reviewedBySupervisor
      responses:
        '200':
          description: 'Assessment updated successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QualityAssessment'
              examples:
                default:
                  summary: 'Full assessment after supervisor edit'
                  value:
                    id: 901
                    scorecardId: 42
                    scorecardBaseId: 17
                    state: reviewedBySupervisor
                    assessedUser: 23
                    assessedByUser: 12
                    ticketId: 8821
                    worklogId: 551
                    createdAt: '2024-06-12T10:20:00Z'
                    modifiedAt: '2024-06-12T11:05:00Z'
                    canEdit: true
                    data:
                      aiSummary: 'Updated AI summary after listening to the call.'
                      categories:
                        -
                          categoryId: 1
                          criteria:
                            -
                              id: 2
                              score: 5
                              reason: 'Great intro'
                              state: humanVerified
                              lastEditedByUserId: 12
                              lastEditedAt: '2024-06-12T10:55:00Z'
                          totals:
                            scoredPoints: 5
                            usedMaxPoints: 5
                            totalMaxPoints: 5
                            percentage: 100.0
                        -
                          categoryId: 2
                          criteria:
                            -
                              id: 6
                              score: 3
                              reason: 'Added more context for the closing paragraph.'
                              state: humanVerified
                              lastEditedByUserId: 12
                              lastEditedAt: '2024-06-12T11:00:00Z'
                          totals:
                            scoredPoints: 15
                            usedMaxPoints: 17
                            totalMaxPoints: 20
                            percentage: 88.2
                      totals:
                        scoredPoints: 20
                        usedMaxPoints: 22
                        totalMaxPoints: 25
                        percentage: 80.0
                      supervisorAssessment: 'Focus on clearer closing.'
                      assessmentDate: '2024-06-12T11:00:00Z'
                      discussionDate: '2024-06-13T09:00:00Z'
                    worklog:
                      userId: 23
                      worklogId: 551
                      lastInteraction: '2024-06-12T09:59:00Z'
                      ticketId: 8821
                      conversationId: 331
                      action: reply
                      actionList:
                        - reply
                        - internal-note
                    aiUsage:
                      automationLevel: 2
                      automationLevelLabel: Assisted
                      aiAgents: []
                    time:
                      duration: 480
                      durationAfterWork: 120
                      segments: []
                    customerExperience:
                      slaMet: true
                      netSecondsClosedAfterSla: -30
                      closingDateForSLA: '2024-06-12T09:40:00Z'
                      dueDateForSLA: '2024-06-12T10:00:00Z'
                      reOpened: false
                      csat: null
        '403':
          description: 'Unauthorized (no permission to edit this assessment)'
        '404':
          description: 'Assessment not found'
        '500':
          description: 'Internal error'
    delete:
      tags:
        - 'Quality Management'
      summary: 'Delete a quality assessment'
      description: 'Soft-delete a quality assessment. Requires qualityDeleteAssessment permission.'
      operationId: deleteQualityAssessment
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Assessment ID'
          schema:
            type: integer
      responses:
        '200':
          description: 'Assessment deleted successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: 'Assessment deleted successfully'
              examples:
                default:
                  summary: 'Successful soft-delete confirmation'
                  value:
                    message: 'Assessment deleted successfully'
        '403':
          description: 'Unauthorized (missing qualityDeleteAssessment permission)'
        '404':
          description: 'Assessment not found'
        '500':
          description: 'Internal error'
  '/quality/assessment/{id}/refresh':
    post:
      tags:
        - 'Quality Management'
      summary: 'Re-run AI processing for a quality assessment'
      description: "Re-runs the AI processing of an existing assessment, regenerating AI-scored criteria\nand the AI summary.\n\nBy default, the assessment is upgraded to the latest active scorecard revision\n(resolved by `baseId`). To pin processing to a specific revision, pass `scorecardId`\nin the request body — it must belong to the same scorecard family (same `baseId`)\nas the assessment's current revision.\n\nThe user must have permission to edit this assessment. Cannot be called when the\nassessment is in `discussedWithAssessee` or `reviewedBySupervisor` state.\n"
      operationId: refreshQualityAssessment
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Assessment ID'
          schema:
            type: integer
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                scorecardId:
                  type: integer
                  description: "Optional. Specific scorecard revision id to use for processing. Must share\nthe same `baseId` as the assessment's current scorecard. If omitted, the\nlatest active revision is used.\n"
            example:
              scorecardId: 42
      responses:
        '200':
          description: 'Assessment re-processed successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QualityAssessment'
        '400':
          description: 'Assessment is in a state that does not allow re-running AI processing, or the requested scorecard revision belongs to a different scorecard'
        '403':
          description: 'Unauthorized (no permission to edit this assessment)'
        '404':
          description: 'Assessment not found'
        '500':
          description: 'Internal error'
  '/ticket/{ticketId}/worklog':
    get:
      tags:
        - 'Quality Management'
      summary: 'Get all worklogs for a ticket'
      description: 'Retrieve all worklog entries associated with a specific ticket, including their linked assessments and detailed worklog information.'
      operationId: getTicketWorklogs
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'Ticket ID'
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: array
                description: 'Array of worklog entries with their assessments and details'
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                      description: 'Worklog ID'
                    ticketId:
                      type: integer
                      description: 'Ticket ID'
                    date:
                      type: string
                      format: date
                      description: 'Date of the worklog entry'
                    latestInteraction:
                      type: string
                      format: date-time
                      nullable: true
                      description: 'Latest interaction timestamp for this worklog'
                    assessments:
                      type: array
                      description: 'All quality assessments associated with this worklog'
                      items:
                        type: object
                        properties:
                          id:
                            type: integer
                            description: 'Assessment ID'
                          scorecardId:
                            type: integer
                            description: 'Scorecard ID used for this assessment'
                    worklog:
                      type: object
                      description: 'General worklog information'
                      properties:
                        userId:
                          type: integer
                        worklogId:
                          type: integer
                        latestInteraction:
                          type: string
                          format: date-time
                          nullable: true
                        ticketId:
                          type: integer
                        conversationId:
                          type: integer
                          nullable: true
                        action:
                          type: string
                          enum:
                            - closeAction
                            - readAction
                            - statusAction
                            - writeAction
                            - autoProcessAction
                        actionList:
                          type: array
                          items:
                            type: string
                        actionListText:
                          type: array
                          items:
                            type: string
                    aiUsage:
                      type: object
                      description: 'AI usage information for this worklog'
                      properties:
                        automationLevel:
                          type: integer
                          description: 'Automation level (0-5)'
                        automationLevelLabel:
                          type: string
                        aiAgents:
                          type: array
                          items:
                            type: object
                    time:
                      type: object
                      description: 'Time tracking information'
                      properties:
                        duration:
                          type: integer
                          description: 'Total duration in seconds'
                        durationAfterWork:
                          type: integer
                          description: 'After-work duration in seconds'
                        segments:
                          type: array
                          items:
                            type: object
                    customerExperience:
                      type: object
                      description: 'Customer experience metrics'
                      properties:
                        slaMet:
                          type: boolean
                        netSecondsClosedAfterSla:
                          type: integer
                        closingDateForSLA:
                          type: string
                          format: date-time
                          nullable: true
                        dueDateForSLA:
                          type: string
                          format: date-time
                          nullable: true
                        reOpened:
                          type: boolean
                        csat:
                          type: object
                          nullable: true
              examples:
                default:
                  summary: 'Two worklog entries for a ticket with their assessments'
                  value:
                    -
                      id: 551
                      ticketId: 8821
                      date: '2024-06-12'
                      latestInteraction: '2024-06-12T09:59:00Z'
                      assessments:
                        -
                          id: 901
                          scorecardId: 42
                        -
                          id: 902
                          scorecardId: 43
                      worklog:
                        userId: 23
                        worklogId: 551
                        latestInteraction: '2024-06-12T09:59:00Z'
                        ticketId: 8821
                        conversationId: 331
                        action: closeAction
                        actionList:
                          - reply
                          - closed
                        actionListText:
                          - 'Sent reply to customer'
                          - 'Closed ticket'
                      aiUsage:
                        automationLevel: 2
                        automationLevelLabel: Assisted
                        aiAgents:
                          -
                            id: 5
                            name: 'Email Assistant'
                            description: 'Generates email responses'
                            intelligence: smart
                            textAssistanceAccuracy: 0.92
                            actions: null
                      time:
                        duration: 480
                        durationAfterWork: 120
                        segments:
                          -
                            userTimeTrackingId: 1201
                            from: '2024-06-12T09:51:00Z'
                            to: '2024-06-12T09:59:00Z'
                            actionList:
                              - reply
                              - closed
                            actionListText:
                              - 'Sent reply to customer'
                              - 'Closed ticket'
                      customerExperience:
                        slaMet: true
                        netSecondsClosedAfterSla: -1800
                        closingDateForSLA: '2024-06-12T09:59:00Z'
                        dueDateForSLA: '2024-06-12T10:30:00Z'
                        reOpened: false
                        csat:
                          id: 88
                          scale: 5
                          answerStars: 5
                          answerText: 'Great service!'
                    -
                      id: 552
                      ticketId: 8821
                      date: '2024-06-13'
                      latestInteraction: '2024-06-13T07:45:00Z'
                      assessments: []
                      worklog:
                        userId: 23
                        worklogId: 552
                        latestInteraction: '2024-06-13T07:45:00Z'
                        ticketId: 8821
                        conversationId: 332
                        action: writeAction
                        actionList:
                          - reply
                        actionListText:
                          - 'Sent reply to customer'
                      aiUsage:
                        automationLevel: 1
                        automationLevelLabel: Monitor
                        aiAgents: []
                      time:
                        duration: 360
                        durationAfterWork: 60
                        segments:
                          -
                            userTimeTrackingId: 1215
                            from: '2024-06-13T07:39:00Z'
                            to: '2024-06-13T07:45:00Z'
                            actionList:
                              - reply
                            actionListText:
                              - 'Sent reply to customer'
                      customerExperience:
                        slaMet: true
                        netSecondsClosedAfterSla: 0
                        closingDateForSLA: '2024-06-13T08:10:00Z'
                        dueDateForSLA: '2024-06-13T08:30:00Z'
                        reOpened: false
                        csat: null
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
  '/ticket/{ticketId}/worklog/exists':
    get:
      tags:
        - Quality
      summary: 'Check whether worklogs exist for a ticket'
      description: "Lightweight existence probe used by the UI to decide whether to show the worklog tab. Cheaper\nthan `GET /ticket/{ticketId}/worklog` because it skips loading the worklog entries.\n"
      operationId: ticketWorklogExists
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'Ticket id.'
          schema:
            type: integer
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  exists:
                    type: boolean
        '404':
          description: 'Ticket not found'
  '/ticket/{ticketId}/quality/check':
    post:
      tags:
        - 'Quality Management'
      summary: 'Live Quality Coach check on a draft reply'
      description: "Score an unsent draft reply against every applicable scorecard whose\n`liveCoach.enabled` is true. Nothing is persisted. Used by the Quality Coach\npanel to render live scores per scorecard.\n\nOne ticket may match multiple live-enabled scorecards (e.g. Compliance, Empathy,\nTone) — every match is scored and returned in `scorecards`, ordered by\n`scorecardId` ascending so the FE can rely on positional rendering.\n\nScorecards without any AI-evaluable criteria are silently skipped. When no\napplicable scorecard has Live Coach enabled for this ticket, `scorecards` is empty.\n\nEither every applicable scorecard is scored successfully, or — if cortex fails on\nany of them — the whole request fails with 502; no partial results are returned.\n"
      operationId: ticketQualityCheck
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'Ticket ID'
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - draftText
              properties:
                draftText:
                  type: string
                  description: 'Draft reply text (HTML allowed) to score'
                scorecardId:
                  type: integer
                  description: "Optional override — score against a specific scorecard revision only.\nWhen set, applicability and `liveCoach.enabled` are ignored; the result\nalways contains exactly one entry. When omitted, every applicable\nlive-enabled scorecard is scored.\n"
            example:
              draftText: '<div>Hallo Susanne, vielen Dank für Ihre Nachricht...</div>'
      responses:
        '200':
          description: 'Live coach evaluation results'
          content:
            application/json:
              schema:
                type: object
                properties:
                  scorecards:
                    type: array
                    description: "One entry per applicable live-enabled scorecard with at least one\nAI-evaluable criterion. Empty when nothing applies. Ordered by\n`scorecardId` ascending.\n"
                    items:
                      type: object
                      properties:
                        scorecardId:
                          type: integer
                        name:
                          type: string
                          description: 'Scorecard name as configured in Quality Management.'
                        percentage:
                          type: number
                          description: "Overall score as percentage of max points across AI-evaluable\ncriteria. A category whose `makeOrBreakForCategory` criterion\nscored 0 contributes 0 to the numerator (its maxPoints stay in\nthe denominator) — this is the only way make-or-break is\nsurfaced in the response. `passed` (percentage vs threshold)\nis the FE's signal of a make-or-break failure.\n"
                        threshold:
                          type: integer
                          description: "Scorecard's live coach threshold (0-100)."
                        passed:
                          type: boolean
                          description: 'True when `percentage >= threshold`.'
                        aiSummary:
                          type: string
                          nullable: true
                        categories:
                          type: array
                          description: "Per-category aggregates. Only includes categories that have at\nleast one AI-evaluable criterion. Each category's\n`score`/`maxPoints` are summed over AI-evaluable criteria only;\nnon-AI criteria are not returned.\n"
                          items:
                            type: object
                            properties:
                              categoryId:
                                type: integer
                              label:
                                type: string
                              score:
                                type: integer
                                description: "Raw sum of AI criteria scores in this category — not affected\nby `makeOrBreakForCategory`. Mirrors the stored quality\nassessment shape, where per-criterion scores are kept raw\nand make-or-break only affects the overall percentage.\n"
                              maxPoints:
                                type: integer
                                description: 'Sum of AI criteria maxPoints in this category.'
                              criteria:
                                type: array
                                description: 'AI-evaluated criteria in this category.'
                                items:
                                  type: object
                                  properties:
                                    criterionId:
                                      type: integer
                                    label:
                                      type: string
                                    score:
                                      type: integer
                                      nullable: true
                                    maxPoints:
                                      type: integer
                                    reason:
                                      type: string
                                      nullable: true
                                    autoGenerateByAi:
                                      type: boolean
                                      description: "Mirrors the scorecard configuration. `false` means\nthe criterion is manually evaluated; in the live\ncheck it appears with `score: null` and `reason:\nnull` so the FE can render it as disabled.\n"
                                    makeOrBreakForCategory:
                                      type: boolean
                                      description: "Mirrors the scorecard configuration. When `true` and\nthe criterion scored 0, the category contributes 0 to\nthe overall `percentage` (its `maxPoints` stay in the\ndenominator). FE uses this flag to highlight which\ncriterion blocked the category.\n"
                                    makeOrBreakForAssessment:
                                      type: boolean
                                      description: "Mirrors the scorecard configuration. Currently not\nenforced by `quality/check` — exposed for the FE to\nlabel the criterion consistently with the stored\nassessment view.\n"
              examples:
                multiple:
                  summary: 'Two applicable live-enabled scorecards'
                  value:
                    scorecards:
                      -
                        scorecardId: 1
                        name: Compliance
                        percentage: 92.0
                        threshold: 90
                        passed: true
                        aiSummary: 'All required disclaimers present.'
                        categories:
                          -
                            categoryId: 1
                            label: Disclaimers
                            score: 18
                            maxPoints: 20
                            criteria:
                              -
                                criterionId: 101
                                label: 'Privacy notice'
                                score: 9
                                maxPoints: 10
                                reason: 'Mentioned with link.'
                                makeOrBreakForCategory: false
                                makeOrBreakForAssessment: false
                              -
                                criterionId: 102
                                label: 'Legal opt-out'
                                score: 9
                                maxPoints: 10
                                reason: 'Included verbatim.'
                                makeOrBreakForCategory: true
                                makeOrBreakForAssessment: false
                      -
                        scorecardId: 7
                        name: Empathy
                        percentage: 68.0
                        threshold: 75
                        passed: false
                        aiSummary: 'Polite but lacks acknowledgement of frustration.'
                        categories:
                          -
                            categoryId: 5
                            label: 'Customer Orientation'
                            score: 13
                            maxPoints: 20
                            criteria:
                              -
                                criterionId: 501
                                label: Acknowledgement
                                score: 5
                                maxPoints: 10
                                reason: 'Generic apology only.'
                                makeOrBreakForCategory: false
                                makeOrBreakForAssessment: false
                              -
                                criterionId: 502
                                label: Tone
                                score: 8
                                maxPoints: 10
                                reason: 'Polite but distant.'
                                makeOrBreakForCategory: false
                                makeOrBreakForAssessment: false
                none:
                  summary: 'No applicable live-enabled scorecard'
                  value:
                    scorecards: []
        '400':
          description: 'Missing or invalid `draftText`, or requested `scorecardId` is not active'
        '404':
          description: 'Requested explicit `scorecardId` does not exist'
        '502':
          description: "Cortex evaluation endpoint unreachable or returned a validation error for one of\nthe scorecards. The error message includes the scorecard name that failed.\n"
  '/ticket/{ticketId}/quality/exists':
    get:
      tags:
        - 'Quality Management'
      summary: 'Check if Live Quality Coach is configured for a ticket'
      description: "Fast pre-flight check used by the FE to decide whether to expose the Live Quality\nCoach Check action in the answer panel. Returns `exists: true` when at least one\napplicable scorecard has `liveCoach.enabled=true` AND has at least one AI-evaluable\ncriterion (i.e. running `quality/check` would actually return a result).\n\nDoes not call cortex.\n"
      operationId: ticketQualityExists
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'Ticket ID'
          schema:
            type: integer
      responses:
        '200':
          description: 'Live coach availability for this ticket'
          content:
            application/json:
              schema:
                type: object
                properties:
                  exists:
                    type: boolean
              examples:
                available:
                  summary: 'At least one usable live-enabled scorecard'
                  value:
                    exists: true
                none:
                  summary: 'No applicable live-enabled scorecard'
                  value:
                    exists: false
        '404':
          description: 'Ticket not found'
  '/ticket/{ticketId}/aiInsight':
    get:
      tags:
        - 'Ticket AI Insights'
      summary: 'Get AI insights for a ticket'
      description: "Active questions of the ticket's subchannel, each paired with its stored insight (or `null`)."
      operationId: getTicketAiInsights
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Question + insight pairs'
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        question:
                          type: object
                          properties:
                            id:
                              type: integer
                            subchannelId:
                              type: integer
                            name:
                              type: string
                            schema:
                              type: object
                              additionalProperties: true
                            createdAt:
                              type: string
                              nullable: true
                            modifiedAt:
                              type: string
                              nullable: true
                        insight:
                          type: object
                          nullable: true
                          additionalProperties: true
        '404':
          description: 'Ticket not found'
  '/ticket/{ticketId}/aiInsight/refresh':
    post:
      tags:
        - 'Ticket AI Insights'
      summary: 'Manually re-run AI insights for a ticket'
      operationId: refreshTicketAiInsights
      parameters:
        -
          name: ticketId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Refresh scheduled'
          content:
            application/json:
              schema:
                type: object
                properties:
                  eventId:
                    type: integer
                  message:
                    type: string
        '400':
          description: 'No active questions or system channel'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
  /export/aiInsights:
    get:
      tags:
        - Export
      summary: 'Export AI insights (one row per ticket, one column per question)'
      description: "Wide-format export. For each active question the export carries two columns: alias\n`insight_<id>` for the value and `confidence_<id>` for the LLM's 0–1 confidence. The\nquestion's human-readable name is used as the XLSX/CSV header. The `reasoning` column\nis intentionally excluded from bulk export — it contains free-form AI text that quotes\nconversation content; it remains visible per ticket via the ticket-AI-insights endpoint.\nLarge ranges are handed off to the `exportBigList` async pipeline.\n"
      operationId: exportAiInsights
      parameters:
        -
          name: format
          in: query
          schema:
            type: string
            enum:
              - xlsx
              - csv
              - json
            default: xlsx
        -
          name: from
          in: query
          schema:
            type: string
        -
          name: to
          in: query
          schema:
            type: string
        -
          name: subchannelId
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: 'Export file (sync) or async scheduling message'
        '403':
          description: 'Unauthorized (requires exportData)'
  /health:
    get:
      tags:
        - Health
      summary: 'Get health status'
      operationId: getHealth
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    description: 'The status of the service'
                    example: healthy
                  db:
                    type: string
                    description: 'The status of the database'
                    example: 'host.docker.internal via TCP/IP'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /health/cron:
    get:
      tags:
        - Health
      summary: 'Get health status of cron jobs'
      operationId: getHealthCron
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    description: 'The status of the service'
                    example: healthy
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /health/containers:
    get:
      tags:
        - Health
      summary: 'Container-level health probe'
      description: "Detailed health check that reports the status of each container Mind depends on (Auth, Cortex,\nio-proxy, ACD, code-executor). Used by uptime monitors that need a more granular signal than\n`/health`.\n"
      operationId: healthContainers
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  containers:
                    type: object
                    description: 'Per-container status keyed by service name.'
        '500':
          description: 'At least one dependency is unhealthy'
  '/survey/{reference}-{ticketId}':
    post:
      tags:
        - General
      summary: 'Submit a survey response'
      operationId: submitSurvey
      parameters:
        -
          name: reference
          in: path
          required: true
          description: 'The reference code of a survey that was provided by an API endpoint, e.g. a reply'
          schema:
            type: string
          example: ticket
        -
          name: ticketId
          in: path
          required: true
          description: 'The ID of the referenced ticket'
          schema:
            type: integer
            format: int32
          example: 30
      requestBody:
        description: 'The survey response'
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                stars:
                  type: integer
                  example: 5
                  description: 'Stars given by the user'
                text:
                  type: string
                  example: 'I really loved your app'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/survey/{id}':
    get:
      tags:
        - General
      summary: 'Render a customer survey form'
      description: "Renders the customer-facing survey form. The numeric `id` resolves to the survey row that was\ngenerated when the originating ticket was closed; the public link in the closing email points\nhere.\n"
      operationId: showCustomerSurvey
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Customer survey id.'
          schema:
            type: integer
          example: 42
      responses:
        '200':
          description: 'Survey HTML'
          content:
            text/html:
              schema:
                type: string
        '404':
          description: 'Survey not found or expired'
    post:
      tags:
        - General
      summary: 'Submit a customer survey'
      description: 'Submit the response payload generated by the rendered survey form.'
      operationId: submitCustomerSurvey
      parameters:
        -
          name: id
          in: path
          required: true
          description: 'Customer survey id.'
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                stars:
                  type: integer
                  example: 5
                text:
                  type: string
                  example: 'Great service'
      responses:
        '200':
          description: 'Submitted (or already submitted — idempotent)'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  alreadySubmitted:
                    type: boolean
                    description: 'Present and true when the survey was already submitted before this request'
                    example: true
                  template:
                    type: string
                    description: 'HTML success message to display to the customer'
        '404':
          description: 'Survey not found or expired'
  /reminder:
    post:
      tags:
        - General
      summary: 'Create a reminder'
      operationId: createReminder
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketId:
                  type: integer
                  description: 'The id of the ticket against which to compare the intent examples'
                  example: 1
                type:
                  type: string
                  enum:
                    - reOpenTicket
                    - freeText
                date:
                  type: string
                  format: DateTime
                  example: '2024-08-29 14:38:12'
      responses:
        '201':
          description: 'Reminder created'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Reminder'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /agents/queue:
    get:
      tags:
        - General
      summary: 'Get agent queue status'
      description: 'Get information about agent availability and expected waiting times for customer service'
      operationId: getAgentsQueue
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  agentQueue:
                    type: number
                    description: 'Number of waiting conversations per available suitable agent. If this is for example 2.5, we have 2.5 chats that first need to be handled per agent until there is capacity for a new request. If -1, then there are no agents available'
                  liveConversationPossible:
                    type: object
                    description: 'Indicates if a realtime conversation is possible per channel'
                    properties:
                      chat:
                        type: boolean
                        description: 'Whether chat requests can be handled in realtime by a human agent'
                  expectedWaitingTime:
                    type: object
                    description: 'Estimated waiting times per channel in seconds. If -1, there are no humans available, and any incoming chat will be handled offline.'
                    properties:
                      chat:
                        type: number
                        description: 'Estimated waiting time for chat channel before it is handled by a human. If -1, responses are done via email.'
                  success:
                    type: boolean
              examples:
                noAgentsAvailable:
                  value:
                    agentQueue: -1
                    liveConversationPossible:
                      chat: false
                    expectedWaitingTime:
                      chat: -1
                    success: true
                  summary: 'No agents available'
                agentsWithQueue:
                  value:
                    agentQueue: 2.5
                    liveConversationPossible:
                      chat: true
                    expectedWaitingTime:
                      chat: 145
                    success: true
                  summary: 'Agents available with waiting queue'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/cron/{interval}':
    get:
      tags:
        - General
      summary: 'Run cron job'
      operationId: runCronJob
      parameters:
        -
          name: interval
          in: path
          required: true
          description: 'The interval of the cron job to run'
          schema:
            type: string
            enum:
              - minute
              - hour
              - day
              - week
          example: minute
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /docs/open-api:
    get:
      tags:
        - General
      summary: 'Get merged OpenAPI spec'
      description: 'Returns the fully merged OpenAPI YAML document with all `$ref` references resolved.'
      operationId: getOpenApiSpec
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/x-yaml:
              schema:
                type: string
            text/plain:
              schema:
                type: string
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/docs/{path}':
    get:
      tags:
        - General
      summary: 'Get documentation'
      description: 'Serves the Mind documentation index or a specific document at the given path.'
      operationId: getDocumentation
      parameters:
        -
          name: path
          in: path
          required: true
          description: 'Documentation subpath. Use any non-empty value to render the index.'
          schema:
            type: string
          example: overview
      responses:
        '200':
          description: 'Successful operation'
          content:
            text/html:
              schema:
                type: string
        '403':
          description: Unauthorized
        '404':
          description: 'Documentation not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /version:
    get:
      tags:
        - General
      summary: 'Get version'
      operationId: getVersion
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: string
                    description: 'The version of the service'
                    example: 1.0.0
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/validation/{type}':
    post:
      tags:
        - General
      summary: 'Validate a value of the given type'
      operationId: validate
      description: 'Validates a value against the rules of the specified validation type. Supported types are `email` and `letterAddress`.'
      parameters:
        -
          name: type
          in: path
          required: true
          description: 'The validation type to apply.'
          schema:
            type: string
            enum:
              - email
              - letterAddress
          example: email
      requestBody:
        description: 'The value to validate'
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - value
              properties:
                value:
                  type: string
                  description: "The value to validate. For `email`, an email address. For `letterAddress`, a postal address\n(name + street + zip/city), separated by newlines or commas.\n"
                  example: user@example.com
      responses:
        '200':
          description: 'Validation result'
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
                    description: 'Whether the value is valid'
                    example: true
                  reason:
                    type: string
                    description: 'The reason why the value is invalid (only present if valid is false)'
                    example: 'Invalid email address format'
        '400':
          description: 'Bad request — missing required field or unsupported type'
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
                    example: false
                  reason:
                    type: string
                    example: 'Invalid validation type. Supported types: email, letterAddress'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /deferrer/pc:
    get:
      tags:
        - Deferrer
      summary: 'Get deferrer for PC'
      operationId: getDeferrerForPC
      responses:
        '200':
          description: 'Successful operation'
          content:
            text/html:
              schema:
                type: string
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/deferrer/contract/{contractId}':
    get:
      tags:
        - Deferrer
      summary: 'Get deferrer for contract'
      operationId: getDeferrerForContract
      parameters:
        -
          name: contractId
          in: path
          required: true
          description: 'The id of the contract'
          schema:
            type: string
          example: '123'
      responses:
        '200':
          description: 'Successful operation'
          content:
            text/html:
              schema:
                type: string
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /storage:
    post:
      tags:
        - Storage
      summary: 'Upload a file'
      description: 'Upload a file to storage. The body is sent as `multipart/form-data`.'
      operationId: uploadStorageFile
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: 'The file to upload.'
                path:
                  type: string
                  description: 'Optional target path.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  url:
                    type: string
                    description: 'Public URL to the stored file (if applicable).'
                  path:
                    type: string
                    description: 'Storage path the file was saved under.'
        '400':
          description: 'Missing file'
        '403':
          description: Unauthorized
  /storage/test:
    get:
      tags:
        - Storage
      summary: 'Test storage'
      operationId: testStorage
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/storage/tmp/{path}':
    get:
      tags:
        - Storage
      summary: 'Get a file from the tmp namespace'
      description: "Returns a file from the short-lived `tmp/` namespace in storage. The tmp namespace is used\nfor files that are produced server-side and immediately offered for download (e.g. generated\nexports). Files there expire after a short TTL.\n"
      operationId: getTmpFileFromStorage
      parameters:
        -
          name: path
          in: path
          required: true
          description: 'Path inside the tmp namespace.'
          schema:
            type: string
      responses:
        '200':
          description: 'Binary file'
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '404':
          description: 'File not found or expired'
  '/storage/{path}':
    get:
      tags:
        - Storage
      summary: 'Get file from storage'
      operationId: getFileFromStorage
      parameters:
        -
          name: path
          in: path
          required: true
          description: 'The path of the file to retrieve'
          schema:
            type: string
          example: test.txt
      responses:
        '200':
          description: 'Binary file'
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '403':
          description: Unauthorized
        '404':
          description: 'File not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /files/manager/list:
    get:
      tags:
        - 'File Manager'
      summary: 'List files and folders in managed-files storage'
      operationId: listManagedFiles
      parameters:
        -
          name: path
          in: query
          required: false
          description: 'Relative path within managed-files'
          schema:
            type: string
          example: Audiodateien/Warteschleife
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  path:
                    type: string
                    example: Audiodateien/Warteschleife
                  folders:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        path:
                          type: string
                  files:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        path:
                          type: string
                        url:
                          type: string
                        size:
                          type: integer
                        lastModified:
                          type: string
                          format: date-time
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /files/manager/usages:
    get:
      tags:
        - 'File Manager'
      summary: 'Get file usage information'
      operationId: getManagedFileUsages
      parameters:
        -
          name: path
          in: query
          required: true
          description: 'Relative path of the file'
          schema:
            type: string
          example: Audiodateien/musik.mp3
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  path:
                    type: string
                  isInUse:
                    type: boolean
                  usages:
                    type: array
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                          example: subchannel
                        subchannelId:
                          type: integer
                          example: 11
                        location:
                          type: string
                          example: 'Phonebot: Support'
                        setting:
                          type: string
                          example: waitingAgentSound
                        settingLabel:
                          type: string
                          example: Warteschleifen-Audiodatei
                        settingUrl:
                          type: string
                          example: '/settings/category/phonebot_11#waitingAgentSound'
                          description: 'Direct link to the setting'
        '400':
          description: 'Bad request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /files/manager/file:
    post:
      tags:
        - 'File Manager'
      summary: 'Upload a file to managed-files storage'
      operationId: createManagedFile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - path
              properties:
                path:
                  type: string
                  description: 'Target directory path'
                  example: Audiodateien
                name:
                  type: string
                  description: 'File name'
                  example: music.mp3
                url:
                  type: string
                  description: 'URL to download the file from'
                  example: 'https://example.com/file.mp3'
                base64:
                  type: string
                  description: 'Base64-encoded file content (with or without data URI prefix)'
                  example: 'data:image/png;base64,iVBORw0KGgo...'
      responses:
        '200':
          description: 'File uploaded successfully'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  file:
                    type: object
                    properties:
                      name:
                        type: string
                      path:
                        type: string
                      url:
                        type: string
                      size:
                        type: integer
                      contentType:
                        type: string
        '400':
          description: 'Bad request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: 'File already exists'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - 'File Manager'
      summary: 'Rename or move a file'
      operationId: updateManagedFile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - oldPath
                - newPath
              properties:
                oldPath:
                  type: string
                  example: Audiodateien/musik.mp3
                newPath:
                  type: string
                  example: Audiodateien/neue-musik.mp3
      responses:
        '200':
          description: 'File renamed successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: 'File not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: 'Target file already exists'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - 'File Manager'
      summary: 'Delete a file'
      operationId: deleteManagedFile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - path
              properties:
                path:
                  type: string
                  example: Audiodateien/musik.mp3
      responses:
        '200':
          description: 'File deleted successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: 'File is in use and cannot be deleted'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /files/manager/folder:
    post:
      tags:
        - 'File Manager'
      summary: 'Create a folder'
      operationId: createManagedFolder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - path
              properties:
                path:
                  type: string
                  example: 'Audiodateien/Neue Ordner'
      responses:
        '200':
          description: 'Folder created successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: 'Folder already exists'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - 'File Manager'
      summary: 'Rename or move a folder'
      operationId: updateManagedFolder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - oldPath
                - newPath
              properties:
                oldPath:
                  type: string
                  example: Audiodateien/Alt
                newPath:
                  type: string
                  example: Audiodateien/Neu
      responses:
        '200':
          description: 'Folder renamed successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: 'Folder not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - 'File Manager'
      summary: 'Delete a folder and its contents'
      operationId: deleteManagedFolder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - path
              properties:
                path:
                  type: string
                  example: Audiodateien/Alte
      responses:
        '200':
          description: 'Folder deleted successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: 'Folder not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: 'Folder contains files that are in use'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /internal/cache/clear:
    get:
      tags:
        - Cache
      summary: 'Clear cache'
      operationId: clearCache
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      removed:
                        type: object
                        properties:
                          memcached:
                            type: integer
                            description: 'The number of removed memcached entries'
                            example: 123
                          database:
                            type: integer
                            description: 'The number of removed database entries'
                            example: 123
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/internal/cache/invalidateUser/{userId}':
    get:
      tags:
        - Cache
      summary: 'Invalidate user cache'
      operationId: invalidateUserCache
      parameters:
        -
          name: userId
          in: path
          required: true
          description: 'The id of the user'
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'User not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/internal/cache/get/{key}':
    get:
      tags:
        - Cache
      summary: 'Get cache entries'
      description: "Get cache entries.\n- If no key is provided (or key='all'), all cache items are returned.\n- If a key with wildcard characters (* or %) is provided, all keys matching the pattern are returned.\n- If a specific key is provided, only that cache item is returned.\n- Can filter results with q parameter to find entries where value contains the search string.\n"
      operationId: getCache
      parameters:
        -
          name: key
          in: path
          required: true
          description: 'The cache key to retrieve. Can contain wildcards (* or %) to match multiple keys.'
          schema:
            type: string
          example: 'user-*'
        -
          name: q
          in: query
          required: false
          description: 'Filter results to only include entries where the value contains this search string'
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                description: 'Map of cache keys to their values'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/internal/cortex/ticket/{ticketId}/callback':
    post:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Receive processed ticket response from Cortex'
      description: "Internal endpoint called by the Cortex microservice to deliver AI processing results for a ticket.\nRequires the `x-internal-communication-token` header — regular user tokens are rejected.\n"
      operationId: callbackFromCortexForTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
          example: 123
      requestBody:
        description: 'The response from Cortex'
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /internal/helpers/convertPdfToImage:
    post:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Convert a PDF to an image'
      description: "Internal helper used by various Mind flows (e.g. document previews, intent input). Accepts a PDF file\nreference (storage path or base64 payload) and returns the rendered page(s) as image(s).\n"
      operationId: convertPdfToImage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'PDF source and rendering options.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '400':
          description: 'Invalid PDF input'
        '403':
          description: Unauthorized
  /internal/reset:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Reset the local environment'
      description: 'Wipes data tables and reseeds dev fixtures. Available only in non-production environments.'
      operationId: reset
      responses:
        '200':
          description: 'Reset complete'
        '403':
          description: 'Unauthorized / disabled in this environment'
  /internal/initMessageSamples:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Initialise message samples (dev)'
      description: 'Loads canned message-sample fixtures into the local DB. Dev-only.'
      operationId: initMessageSamples
      responses:
        '200':
          description: 'Successful operation'
        '403':
          description: 'Unauthorized / disabled in this environment'
  /internal/loadSeedTickets:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Load seed tickets (dev)'
      description: 'Loads a curated set of seed tickets into the local DB for development and demos.'
      operationId: loadSeedTickets
      responses:
        '200':
          description: 'Successful operation'
        '403':
          description: 'Unauthorized / disabled in this environment'
  /internal/getSupersetUsers:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'List Superset users'
      description: "Returns the user list provisioned in the Superset analytics tenant for this client. Used by\nthe Settings UI when wiring Superset SSO.\n"
      operationId: getSupersetUsers
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
  /internal/explainTimeTracking:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Explain time-tracking decisions'
      description: "Diagnostic endpoint that returns the time-tracking computation breakdown for the current user\nor a given user/window. Used to debug worklog disputes.\n"
      operationId: explainTimeTracking
      parameters:
        -
          name: userId
          in: query
          required: false
          description: 'User to explain. Defaults to the caller.'
          schema:
            type: integer
        -
          name: date
          in: query
          required: false
          description: 'Date to explain (ISO 8601). Defaults to today.'
          schema:
            type: string
            format: date
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
  /internal/invalidateCache:
    post:
      tags:
        - Cache
      summary: 'Invalidate cache entries by key pattern'
      description: "Drops cache entries matching the supplied keys/patterns. Use sparingly — `clearCache` is the\nblunter alternative.\n"
      operationId: invalidateInternalCache
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                keys:
                  type: array
                  items:
                    type: string
                  description: 'Keys or wildcard patterns to drop.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  removed:
                    type: integer
                    description: 'Number of cache entries dropped.'
  /internal/workerStatus:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Get worker status'
      description: 'Returns liveness + progress information for all Mind workers (`worker_*.log`).'
      operationId: getWorkerStatus
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
  /internal/query:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Run an ad-hoc read-only query'
      description: "Diagnostic SQL endpoint — accepts a SQL string in `q` and returns the rows. Locked down to\nEnneo staff and never enabled on customer-facing deployments.\n"
      operationId: runInternalQuery
      parameters:
        -
          name: q
          in: query
          required: true
          description: 'SQL string to run (read-only).'
          schema:
            type: string
          example: 'SELECT id, name FROM team LIMIT 10'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  rows:
                    type: array
                    items:
                      type: object
  '/internal/telemetry/trace/{traceId}':
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Get a telemetry trace'
      description: "Proxies the admin portal telemetry endpoint\n(`admin.enneo.ai/api/telemetry/clients/{CLIENT_ID}/traces/{traceId}`) for the current\nenvironment's client. Restricted to enneo and partner users (HTTP 403 for all other profile types).\n"
      operationId: getTelemetryTrace
      parameters:
        -
          name: traceId
          in: path
          required: true
          schema:
            type: string
            pattern: '^[a-f0-9]{32}$'
            example: ae7b734065c82c46c4e8e5c2a51a0657
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                allOf:
                  -
                    $ref: '#/components/schemas/Success'
                  -
                    type: object
                    properties:
                      trace:
                        type: object
                        description: 'Trace data returned by the admin portal.'
        '403':
          description: 'Forbidden — caller is not an enneo or partner user'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          description: 'Upstream failure — admin portal returned an error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /internal/syncPhoneNumbers:
    get:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Sync phone numbers from the telephony provider (read)'
      description: "Pulls the current list of phone numbers configured at the telephony provider and reconciles\nthem with `subchannel` rows in Mind. Mounted on both GET (preview) and POST (apply).\n"
      operationId: syncPhoneNumbersGet
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
    post:
      tags:
        - 'Internal (Enneo only)'
      summary: 'Sync phone numbers from the telephony provider (apply)'
      description: 'Same as `GET /internal/syncPhoneNumbers`, but persists the diff.'
      operationId: syncPhoneNumbersPost
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
  /executor/preview:
    post:
      tags:
        - 'Executor Sandbox'
      summary: 'Preview executor'
      operationId: previewExecutor
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: integer
                  description: 'The id of the executor'
                  example: 123
                code:
                  type: string
                  description: 'The name of the executor'
                  example: "<?php\n\n echo '{\"success\": true}';\n"
                type:
                  type: string
                  description: 'The type of the executor'
                  example: sourceCode
                language:
                  type: string
                  description: 'The language of the executor'
                  example: php82
                packages:
                  type: string
                  description: 'The packages of the executor'
                  example: ''
                parameters:
                  type: object
                  properties:
                    key1:
                      type: string
                      example: 'val1ö"\n*!'
                    key2:
                      type: string
                      example: "val'ue"
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecutionResponse'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/executor/execute/{name}':
    post:
      tags:
        - 'Executor Sandbox'
      summary: 'Execute executor'
      operationId: executeExecutor
      parameters:
        -
          name: name
          in: path
          required: true
          description: 'The name of the executor'
          schema:
            type: string
          example: Example
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                parameters:
                  type: object
                  description: 'The parameters of the executor'
                  example:
                    method: GET
                    api: dev/api/health
                    params: false
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /executor/localExecutionCommand:
    post:
      tags:
        - 'Executor Sandbox'
      summary: 'Get local execution command'
      operationId: getLocalExecutionCommand
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: integer
                  description: 'The id of the executor'
                  example: 123
                code:
                  type: string
                  description: 'The name of the executor'
                  example: "<?php\n\n echo '{\"success\": true}';\n"
                type:
                  type: string
                  description: 'The type of the executor'
                  example: sourceCode
                language:
                  type: string
                  example: php82
                packages:
                  type: string
                  example: ''
                parameters:
                  type: object
                  properties:
                    key1:
                      type: string
                      example: 'val1ö"\n*!'
                    key2:
                      type: string
                      example: "val'ue"
      responses:
        '200':
          description: 'Successful execution'
          content:
            application/json:
              schema:
                type: object
                properties:
                  unix:
                    type: string
                    example: "curl -s http://host.docker.internal:8000/api/codeExecutor/sdk/php82.php -o sdk.php && echo '{}' | ENNEO_API_URL=http://host.docker.internal:8000 ENNEO_SESSION_TOKEN=***TOKEN_EXAMPLE*** ENNEO_USER_AUTH_HEADER=Authorization: Bearer ***TOKEN_EXAMPLE*** SDK=sdk.php php -dxdebug.mode=debug -dxdebug.client_port=9003 -dxdebug.client_host=127.0.0.1 -dxdebug.start_with_request=yes mycode.php"
                  windows:
                    type: string
                    example: 'Invoke-WebRequest -Uri http://host.docker.internal:8000/api/codeExecutor/sdk/php82.php -OutFile sdk.php ; $env:ENNEO_API_URL = "http://host.docker.internal:8000"; $env:ENNEO_SESSION_TOKEN=***TOKEN_EXAMPLE*** $env:ENNEO_USER_AUTH_HEADER = "Authorization: Bearer ***TOKEN_EXAMPLE***"; $env:SDK = "sdk.php"; ; php -dxdebug.mode=debug -dxdebug.client_port=9003 -dxdebug.client_host=127.0.0.1 -dxdebug.start_with_request=yes mycode.php'
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /telephony/callReceived:
    post:
      tags:
        - Telephony
      summary: 'Call received'
      description: "Called when a call is received (inbound) or initiated (outbound).\n\nFor inbound calls (direction=in, default): creates a ticket, attempts customer identification from phone number, and returns the ticket ID. Routing happens later via getRouting.\n\nFor outbound calls (direction=out): creates a ticket, creates a queue entry (no routing needed), and links customer/contract data if available. Requires 'to'. No human initiator is required — the call can be fully autonomous/callflow-driven. If a human initiator is provided ('agentId'/'userId'), they are set to 'interacting' and the ticket is assigned to them. For bot/callflow-driven calls with no human on the line, omit 'agentId'/'userId' — presence is not touched and the queue is created as Unknown (out of routing).\n"
      operationId: callReceived
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                routing:
                  type: string
                  description: "Determines how this call should be routed to an agent. Possible values are 'none', 'external', and 'native'.\n- none: Call is not routed, creates a new ticket in status closed, does not run AI agents.\n- external: Call should be routed, but routing engine is external. Enneo processes the ticket via its AI, and either connects to a specified agent (property 'userId' must be provided) or expects a later connection through a subsequent /agentConnected call.\n- native: Call should be routed using Enneo as the call routing engine. Returns a ticketId that can be polled to query agent availability.\n"
                  enum:
                    - none
                    - external
                    - native
                  example: none
                contractId:
                  type: string
                  description: 'Optional contract id, if provided will be used for customer identification'
                  nullable: true
                  example: '123456'
                customerId:
                  type: string
                  description: 'Optional customer id, if provided will be used for customer identification'
                  nullable: true
                  example: '789012'
                phone:
                  type: string
                  description: "Phone number of the caller. Used for customer identification on inbound calls. For outbound calls, use 'to' instead."
                  nullable: true
                  example: '+491234567890'
                to:
                  type: string
                  description: 'Customer phone number being called. Required for outbound calls (direction=out). Used for customer identification.'
                  nullable: true
                  example: '+491234567890'
                agentId:
                  type: integer
                  description: "ID of the agent initiating an outbound call where the human is on the line. Optional for outbound calls — if provided, the agent will be set to 'interacting' and assigned to the queue entry. Omit for bot/callflow-driven calls where no human is on the call."
                  nullable: true
                  example: 1
                aiAgentId:
                  type: integer
                  description: 'ID of the AI agent conducting the outbound call. When present with direction=out, the call is handled by a voicebot — no human agent is required, and any supplied human agent (userId/agentId) is recorded on the ticket but not set to interacting. Sufficient alone as the sole initiator for bot-only outbound calls. Ignored for inbound calls (direction=in).'
                  nullable: true
                  example: 456
                transcript:
                  type: array
                  description: 'Array of previous bot transcript entries'
                  items:
                    $ref: '#/components/schemas/Transcript'
                userId:
                  type: integer
                  description: "Optional id of user that should be associated with the call. If that user has a browser window of enneo open, user will be redirected to the newly created ticket. Same effect as a subsequent /telephony/agentConnected API call. Requires property 'routing' to be set to 'external' or 'none'."
                  nullable: true
                  example: 42
                channelId:
                  type: string
                  description: 'Optional unique identifier of a third party system of this call. If provided, then it can be used as reference for any future updates to this call flow'
                  nullable: true
                  example: call_123abc456
                callerName:
                  type: string
                  description: 'Optional name of the caller. Used for the fromName field of the ticket.'
                  nullable: true
                  example: 'John Smith'
                triggeredBy:
                  type: integer
                  description: "ID of the user who triggered an autonomous/callflow-driven outbound call. Recorded in the ticketCreated event for attribution only — not put on the call (no presence/'interacting' change), not assigned to the ticket, not placed in the queue. Use 'agentId'/'userId' instead if the human is actually on the line."
                  nullable: true
                  example: 1
                direction:
                  type: string
                  description: "Direction of the call. Defaults to 'in' if not provided. Outbound calls (direction=out) require 'to'. A human initiator ('agentId'/'userId') is optional — omit for fully autonomous/callflow-driven calls."
                  nullable: true
                  enum:
                    - in
                    - out
                  example: in
                subchannelId:
                  type: integer
                  description: 'Optional Enneo subchannel ID to associate with the ticket. Usually the ID of the call line as defined in the Enneo settings'
                  nullable: true
                  example: 5
                externalTicketId:
                  type: string
                  description: 'Optional external ticket ID to associate with the ticket'
                  nullable: true
                  example: ext-123
                status:
                  type: string
                  description: "Status to set for the ticket. Defaults to 'closed' for routing 'none', 'open' for routing 'external' or 'native'."
                  nullable: true
                  enum:
                    - open
                    - closed
                    - pending
                  example: open
                triggerAiProcessing:
                  type: boolean
                  description: "Whether to trigger AI processing. Defaults to false for routing 'none', true for routing 'external' or 'native'."
                  nullable: true
                  example: true
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  ticketId:
                    type: integer
                    description: 'ID of the newly created enneo ticket'
                    example: 2142
                  contractId:
                    type: string
                    description: 'Contract ID associated with the call, if any'
                    nullable: true
                    example: null
                  customerId:
                    type: string
                    description: 'Customer ID associated with the call, if any'
                    nullable: true
                    example: null
                  ioResponse:
                    type: string
                    description: "Response from IO system when routing is external, otherwise 'None'"
                    example: 'None, as routing is not external'
                  queueId:
                    type: integer
                    description: "Unique ID of the queue the call was assigned to (only returned if routing is 'native')"
                    example: 123
        '400':
          description: 'Invalid input'
        '500':
          description: 'Internal error'
  /telephony/agentConnected:
    post:
      tags:
        - Telephony
      summary: 'Connect agent to call'
      description: "Called whenever the telephony system has connected a agent (user) with a call that was previously sent to enneo via the /telephony/callReceived endpoint. Upon receiept, the specified agent's browser window is then redirected to the previously created ticket id (specified via ticket id or channel id)"
      operationId: connectAgentToCall
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketId:
                  type: integer
                  description: 'Optional ticket id of the enneo call ticket. Either ticketId or channelId must be provided'
                  nullable: true
                  example: 123
                channelId:
                  type: string
                  description: 'Optional unique identifier of a third party system of this call. Must have been previously created with the /telephony/callReceived endpoint. Either ticketId or channelId must be provided'
                  nullable: true
                  example: call_123abc456
                userId:
                  type: integer
                  description: 'Id of agent that accepted the call. If that agent has a browser window of enneo open, he will be prompted with this call'
                  example: 42
              required:
                - userId
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '400':
          description: 'Invalid input'
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
  /telephony/callCompleted:
    post:
      tags:
        - Telephony
      summary: 'Complete call and store transcript'
      description: 'Called when a call is completed to store the final transcript and call details'
      operationId: completeCall
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ticketId:
                  type: integer
                  description: 'Optional ticket id of the enneo call ticket. If provided, the transcript will be attached to the given ticket id'
                  nullable: true
                  example: 123
                channelId:
                  type: string
                  description: 'Unique identifier of a third party system of this call. If a channelId was provided during a previous /agentConnected api call, the transcript will be appended to the corresponding ticket'
                  example: call_123abc456
                transcript:
                  type: array
                  description: 'Complete transcript of the call'
                  items:
                    $ref: '#/components/schemas/Transcript'
                duration:
                  type: integer
                  description: 'DEPRECATED: Use totalDurationSeconds instead'
                  nullable: true
                  example: 300
                totalDurationSeconds:
                  type: integer
                  description: 'Total call duration from start to end in seconds'
                  nullable: true
                  example: 300
                botDurationSeconds:
                  type: integer
                  description: 'Duration spent with the voice bot (automated conversation) in seconds'
                  nullable: true
                  example: 45
                queueDurationSeconds:
                  type: integer
                  description: 'Duration spent waiting in queue for an agent in seconds'
                  nullable: true
                  example: 30
                humanDurationSeconds:
                  type: integer
                  description: 'Duration spent talking with a human agent in seconds'
                  nullable: true
                  example: 225
              required:
                - channelId
                - transcript
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '400':
          description: 'Invalid input'
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /telephony/routingAvailability:
    get:
      tags:
        - Telephony
      summary: 'Get routing availability for all users'
      description: 'Returns the routing availability for all users. Used by a dispatcher to check which agents are currently available to pick up a ticket.'
      operationId: routingAvailability
      parameters:
        -
          name: userId
          in: query
          required: false
          description: 'Optional filter to return only a specific user'
          schema:
            type: integer
          example: 4
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  users:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: 'The user ID'
                          example: 42
                        status:
                          type: string
                          description: "The user's current status"
                          nullable: true
                        chatRoutingStatus:
                          $ref: '#/components/schemas/RoutingStatus'
                        callRoutingStatus:
                          $ref: '#/components/schemas/RoutingStatus'
                        skills:
                          type: object
                          properties:
                            tagIds:
                              type: array
                              items:
                                type: integer
                              description: 'IDs of tags representing user skills'
                              example:
                                - 1
                                - 2
                                - 3
                            channels:
                              type: array
                              items:
                                type: string
                              description: 'Communication channels the user is skilled in'
                              example:
                                - email
                                - phone
                                - chat
                        currentTickets:
                          type: array
                          items:
                            type: integer
                          description: 'IDs of tickets currently being worked on by the user (opened in UI)'
                          example:
                            - 123
                            - 456
                        queueTickets:
                          type: array
                          items:
                            type: integer
                          description: 'IDs of tickets assigned to the user in the routing queue'
                          example:
                            - 789
                        lastActivity:
                          type: string
                          format: date-time
                          description: "Timestamp of the user's last activity"
                          example: '2024-03-20 14:30:00'
        '403':
          description: 'Unauthorized - user does not have permission to read user status'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /telephony/getRouting:
    get:
      tags:
        - Telephony
      summary: 'Get routing status'
      description: 'Returns the routing status of a specific ticket in the routing queue.'
      operationId: getRouting
      parameters:
        -
          name: ticketId
          in: query
          required: true
          description: 'ID of the ticket to get routing status for'
          schema:
            type: integer
          example: 12345
        -
          name: cleanState
          in: query
          required: false
          description: 'When true, resets routing state for all user-type agents before evaluating the ticket'
          schema:
            type: boolean
          example: false
        -
          name: forceReEvaluation
          in: query
          required: false
          description: 'If set, force re-evaluation of the routing status'
          schema:
            type: boolean
          example: false
        -
          name: testQueueStatusResponse
          in: query
          required: false
          description: 'Testing helper that forces the endpoint to return the provided queue status without executing routing logic. When set to `declined`, a random decline reason is included.'
          schema:
            type: string
            enum:
              - preProcessing
              - waitingForAgent
              - assigned
              - currentlyProcessed
              - completed
              - declined
              - unknown
          example: declined
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    description: 'ID of the queue entry'
                    example: 789
                  ticketId:
                    type: integer
                    description: 'ID of the associated ticket'
                    example: 12345
                  status:
                    type: string
                    description: 'Current routing status'
                    enum:
                      - preProcessing
                      - waitingForAgent
                      - assigned
                      - currentlyProcessed
                      - completed
                      - declined
                      - unknown
                    example: waitingForAgent
                  declineReasonCode:
                    type: string
                    description: 'Code indicating why the call was declined, if applicable'
                    nullable: true
                    example: null
                  declineReasonMessage:
                    type: string
                    description: 'Human-readable message explaining why the call was declined'
                    nullable: true
                    example: null
                  declineReasonDebugInfos:
                    type: string
                    description: "Debug string (newline-separated) listing all online agents and why each was excluded or eligible for routing.\nPopulated when status is declined (e.g. no agents with matching skills). Also included in ticketRouted event\ndata.routingExplanation for decline cases.\n"
                    nullable: true
                    example: null
                  primaryTagId:
                    type: integer
                    description: 'ID of the primary tag associated with this queue entry'
                    nullable: true
                    example: 46
                  channel:
                    type: string
                    description: 'Communication channel for this ticket'
                    example: phone
                  subchannelId:
                    type: integer
                    description: 'ID of the subchannel, if applicable'
                    nullable: true
                    example: null
                  assignedUserId:
                    type: integer
                    description: 'ID of the assigned user, if any'
                    nullable: true
                    example: null
                  assignedUserRawData:
                    type: object
                    description: 'Raw data of the assigned user, may include external IDs'
                    nullable: true
                    example: null
                  createdAt:
                    type: string
                    format: date-time
                    description: 'Timestamp when the queue entry was created'
                    example: '2025-04-08 19:38:03'
                  routedAt:
                    type: string
                    format: date-time
                    description: 'Timestamp when the queue entry was routed to an agent'
                    nullable: true
                    example: null
                  completedAt:
                    type: string
                    format: date-time
                    description: 'Timestamp when processing of the queue entry was completed'
                    nullable: true
                    example: null
                  updatedAt:
                    type: string
                    format: date-time
                    description: 'Timestamp when the queue entry was last updated'
                    example: '2025-04-08 19:38:11'
                  positionDetails:
                    type: object
                    description: 'Details about the queue position and waiting time'
                    nullable: true
                    properties:
                      position:
                        type: integer
                        description: 'Position in the queue (0-based)'
                        example: 0
                      agentsOnlineWithMatchingSkills:
                        type: array
                        description: 'IDs of online agents with matching skills'
                        items:
                          type: integer
                        example:
                          - 20
                      estimatedWaitingTimeInSeconds:
                        type: integer
                        description: 'Estimated waiting time in seconds'
                        example: 60
        '400':
          description: 'Invalid input - missing required parameters'
        '403':
          description: Unauthorized
        '404':
          description: 'Queue entry not found'
        '500':
          description: 'Internal error'
  /telephony/testOutboundCall:
    get:
      tags:
        - Telephony
      summary: 'Test outbound call'
      description: 'Makes a test outbound call using configured settings. Used for testing the telephony integration.'
      operationId: testOutboundCall
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  phoneNumber:
                    type: string
                    description: 'Phone number called'
                    example: '+491234567890'
                  subchannelId:
                    type: integer
                    description: 'ID of the subchannel used for the call'
                    example: 5
                  ticketId:
                    type: integer
                    description: 'ID of the newly created ticket for the call'
                    example: 12345
                  ticketIdUrl:
                    type: string
                    description: 'URL to the newly created ticket'
                    example: /ticket/12345
                  response:
                    type: object
                    description: 'Response from the cortex service'
        '400':
          description: 'Invalid input or missing settings'
        '401':
          description: Unauthorized
        '500':
          description: 'Internal error'
    post:
      tags:
        - Telephony
      summary: 'Test outbound call (POST)'
      description: 'Same as `GET /telephony/testOutboundCall`, but accepts a JSON body to override the configured target phone number or subchannel.'
      operationId: testOutboundCallPost
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                phoneNumber:
                  type: string
                  description: 'Override the configured target phone number.'
                  example: '+491234567890'
                subchannelId:
                  type: integer
                  description: 'Override the configured subchannel.'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '400':
          description: 'Invalid input or missing settings'
        '401':
          description: Unauthorized
        '500':
          description: 'Internal error'
  /telephony/acd/config:
    get:
      tags:
        - Telephony
      summary: 'Get ACD configuration'
      description: "Returns configuration for the ACD microservice including:\n- ACD-enabled phone channels with their workflows\n- SIP trunk configurations\n"
      operationId: getAcdConfig
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  channels:
                    type: array
                    description: 'List of ACD-enabled phone channels'
                    items:
                      type: object
                      properties:
                        phoneNumbersIn:
                          type: array
                          description: 'Inbound phone numbers for this channel'
                          items:
                            type: string
                          example:
                            - '+1234567890'
                            - '+0987654321'
                        ringDuration:
                          type: string
                          description: 'Ring duration for this channel'
                          example: 20s
                        workflow:
                          type: array
                          description: 'ACD workflow stages for this channel'
                          items:
                            type: object
                  sipTrunks:
                    type: array
                    description: 'SIP trunk configurations'
                    items:
                      type: object
                      properties:
                        username:
                          type: string
                          description: 'SIP username'
                          example: user123
                        password:
                          type: string
                          description: 'SIP password'
                          example: pass123
                        serverAddress:
                          type: string
                          description: 'SIP server address with port'
                          example: 'sip.example.com:5060'
                        reregisterInterval:
                          type: string
                          description: 'Re-registration interval'
                          example: 1h
        '403':
          description: Unauthorized
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  '/experimental/cortex/ticket/{ticketId}/trigger':
    post:
      tags:
        - 'Debugging for AI Microservice Cortex'
      summary: 'Submit request to cortex for ticket'
      description: "Manually trigger Cortex processing for a ticket.\n\n**Deprecated** — this endpoint will be removed in the 2026-06-14 release.\n"
      deprecated: true
      operationId: triggerCortexForTicket
      parameters:
        -
          name: ticketId
          in: path
          required: true
          description: 'The id of the ticket'
          schema:
            type: integer
          example: 123
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '403':
          description: Unauthorized
        '404':
          description: 'Ticket not found'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /app:
    get:
      tags:
        - Apps
      summary: 'List apps'
      description: 'Return all installed/created apps the caller can read.'
      operationId: listApps
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  apps:
                    type: array
                    items:
                      $ref: '#/components/schemas/App'
        '403':
          description: 'Unauthorized — missing `readApps` permission.'
        '500':
          description: 'Internal error.'
    post:
      tags:
        - Apps
      summary: 'Create app'
      description: 'Create a new app. Requires `appCreator` permission.'
      operationId: createApp
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/App'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  app:
                    $ref: '#/components/schemas/App'
        '400':
          description: 'Missing `name` or invalid payload.'
        '403':
          description: 'Unauthorized — missing `appCreator` permission.'
  /app/import:
    post:
      tags:
        - Apps
      summary: 'Import app'
      description: 'Import an app from an exported bundle. Requires `appCreator` permission.'
      operationId: importApp
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'App bundle exported via the corresponding export route.'
      responses:
        '201':
          description: Imported
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  app:
                    $ref: '#/components/schemas/App'
        '400':
          description: 'Invalid bundle.'
        '403':
          description: Unauthorized.
  /app/store:
    get:
      tags:
        - Apps
      summary: 'List app store entries'
      description: 'Returns the curated list of installable apps available in the app store.'
      operationId: listAppStoreApps
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  apps:
                    type: array
                    items:
                      type: object
        '500':
          description: 'Internal error.'
  '/app/store/{storeId}':
    get:
      tags:
        - Apps
      summary: 'Get app store entry'
      operationId: getAppStoreEntry
      parameters:
        -
          name: storeId
          in: path
          required: true
          schema:
            type: string
          example: helpful-assistant
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '404':
          description: 'Store entry not found.'
  '/app/store/{storeId}/install':
    post:
      tags:
        - Apps
      summary: 'Install an app from the store'
      operationId: installAppFromStore
      parameters:
        -
          name: storeId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Installed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  app:
                    $ref: '#/components/schemas/App'
        '403':
          description: Unauthorized.
  '/app/store/{storeId}/uninstall':
    post:
      tags:
        - Apps
      summary: 'Uninstall a previously store-installed app'
      operationId: uninstallAppFromStore
      parameters:
        -
          name: storeId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Uninstalled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '403':
          description: Unauthorized.
  '/app/{appId}':
    get:
      tags:
        - Apps
      summary: 'Get an app by id or slug'
      operationId: getApp
      parameters:
        -
          name: appId
          in: path
          required: true
          description: 'App id or slug.'
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  app:
                    $ref: '#/components/schemas/App'
        '404':
          description: 'App not found.'
    patch:
      tags:
        - Apps
      summary: 'Update an app'
      operationId: updateApp
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/App'
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  app:
                    $ref: '#/components/schemas/App'
        '403':
          description: Unauthorized.
    delete:
      tags:
        - Apps
      summary: 'Delete an app'
      operationId: deleteApp
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '403':
          description: Unauthorized.
  '/app/{appId}/revisions':
    get:
      tags:
        - Apps
      summary: 'List app revisions'
      operationId: listAppRevisions
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  revisions:
                    type: array
                    items:
                      type: object
  '/app/{appId}/rollback/{revision}':
    post:
      tags:
        - Apps
      summary: 'Roll back an app to a previous revision'
      operationId: rollbackApp
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
        -
          name: revision
          in: path
          required: true
          description: 'Revision number to roll back to.'
          schema:
            type: integer
      responses:
        '200':
          description: 'Rolled back'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  app:
                    $ref: '#/components/schemas/App'
        '404':
          description: 'Revision not found.'
  '/app/{appId}/preview':
    post:
      tags:
        - Apps
      summary: 'Preview an app without persisting changes'
      description: "Render the supplied app code in the sandbox and return the resulting HTML. Used by the editor's preview pane."
      operationId: previewApp
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/App'
      responses:
        '200':
          description: 'Rendered HTML'
          content:
            text/html:
              schema:
                type: string
  '/app/{appId}/run':
    get:
      tags:
        - Apps
      summary: 'Run an app'
      description: 'Executes the app in the code-executor sandbox and returns the rendered HTML. Same handler is mounted on POST.'
      operationId: runApp
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Rendered HTML'
          content:
            text/html:
              schema:
                type: string
        '404':
          description: 'App not found.'
    post:
      tags:
        - Apps
      summary: 'Run an app (with POST body)'
      description: 'Same as `GET /app/{appId}/run`, but accepts a JSON body forwarded to the app as input.'
      operationId: runAppWithBody
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: 'Rendered HTML'
          content:
            text/html:
              schema:
                type: string
  '/app/{appId}/data':
    get:
      tags:
        - Apps
      summary: 'List persisted keys for an app'
      operationId: listAppData
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  keys:
                    type: array
                    items:
                      type: string
  '/app/{appId}/data/{key}':
    get:
      tags:
        - Apps
      summary: 'Get a persisted value'
      operationId: getAppData
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
        -
          name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
        '404':
          description: 'Key not found.'
    put:
      tags:
        - Apps
      summary: 'Save a persisted value'
      operationId: saveAppData
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
        -
          name: key
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: 'Value to persist under the given key.'
      responses:
        '200':
          description: Saved
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
    delete:
      tags:
        - Apps
      summary: 'Delete a persisted value'
      operationId: deleteAppData
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
        -
          name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
  '/app/{appId}/data/{key}/meta':
    get:
      tags:
        - Apps
      summary: 'Get metadata for a persisted value'
      description: 'Returns size, timestamps and ownership information for the given key.'
      operationId: getAppDataMeta
      parameters:
        -
          name: appId
          in: path
          required: true
          schema:
            type: string
        -
          name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Successful operation'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  meta:
                    type: object
        '404':
          description: 'Key not found.'
components:
  parameters:
    limitParam:
      name: limit
      in: query
      required: false
      description: 'The number of items to return'
      schema:
        type: integer
        default: 100
        minimum: 1
        maximum: 1000
      example: 100
    offsetParam:
      name: offset
      in: query
      required: false
      description: 'The number of items to skip'
      schema:
        type: integer
        default: 0
        minimum: 0
      example: 0
  schemas:
    Channel:
      type: string
      description: 'Channel of ticket'
      enum:
        - email
        - portal
        - phone
        - letter
        - system
        - chat
        - walkIn
      example: email
    Status:
      type: string
      description: 'Status of ticket'
      enum:
        - open
        - pending
        - closed
      example: open
    Priority:
      type: string
      description: 'Priority of ticket'
      enum:
        - low
        - medium
        - high
        - urgent
      example: low
    Template:
      type: object
      properties:
        id:
          type: integer
          description: 'The id of the template'
          example: 1
        tagId:
          type: integer
          description: 'The id of the tag that the template belongs to'
          example: 1
        emailTemplateId:
          type: integer
          description: 'The id of the email template that is used for wrapping'
          example: 1
        description:
          type: string
          description: 'The description of the intent template'
          example: 'Zählerstand erfolgreich hinterlegt'
        message:
          type: string
          description: 'The template itself'
          example: '<p>Wir haben den Zählerstand von {{intent.data.reading}} kWh für den {{formatDateDE intent.data.date}} erfasst und im System hinterlegt.</p>'
        subject:
          type: string
          description: 'The subject of the email for outgoing emails'
          example: 'Wir brauchen Deine Unterstützung'
        exampleTicketIds:
          type: array
          description: 'The ids of the tickets that are used as examples for the intent template'
          items:
            type: integer
            example: 1
        mergedTemplate:
          type: string
          description: 'The merged template'
          example: 'Wir haben den Zählerstand von 123 kWh für den 01.01.2022 erfasst und im System hinterlegt.'
        template:
          type: string
          description: 'The template itself'
          example: '<p>Wir haben den Zählerstand von 123 kWh für den 01.01.2022 erfasst und im System hinterlegt.</p>'
        success:
          type: boolean
          description: 'If true, the template is successful'
          example: true
    WikiEntry:
      type: object
      description: 'Enneo wiki entry from built-in knowledge mgmt. system'
      properties:
        id:
          type: integer
          example: 21221
          description: 'ID of article'
        title:
          type: string
          example: 'Textbaustein: "Zählerstand nicht lesbar"'
          description: 'Title of article'
        type:
          type: string
          description: 'Type of article. Relevant also for small image shown in UI'
          enum:
            - article
            - emailSnippet
          example: emailSnippet
        lastChange:
          type: string
          format: DateTime
          example: '2022-08-12 12:21:21'
        lastEditor:
          type: string
          example: Anja
    AuthProfile:
      type: object
      properties:
        id:
          type: integer
          description: 'User ID'
          example: 1
        firstName:
          type: string
          description: 'First name'
          example: Max
        lastName:
          type: string
          description: 'Last name'
          example: Mustermann
        email:
          type: string
          description: 'Email address (read only)'
          example: demo@enneo.dev
        phone:
          type: string
          description: 'Phone number'
          example: '+49 123 456789'
        image:
          type: string
          format: base64
          description: Image
          writeOnly: true
          example: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=
        password:
          type: string
          description: Password
          writeOnly: true
          example: 123456
        lastSeen:
          type: string
          format: DateTime
          description: 'The last time the user was seen (read only)'
          example: '2022-08-12 12:21:21'
        type:
          type: string
          description: 'The type of the user'
          example: enneo
        isSsoOnly:
          type: integer
          description: 'If 1, the user can only login via SSO. If 0, the user can login with password or SSO'
          enum:
            - 0
            - 1
          example: 0
        lang:
          type: string
          description: 'The language of the user'
          example: de
        nameAlias:
          type: string
          description: 'Alternative display name or alias for the user'
          example: Johnny
        externalId:
          type: string
          description: 'External identifier such as employee number or ID from external system'
          example: EMP-12345
    MindProfile:
      type: object
      properties:
        id:
          type: integer
          description: 'User ID'
          example: 1
        settings:
          oneOf:
            -
              type: object
              properties:
                status:
                  type: string
                  description: 'The status of the user'
                  enum:
                    - available
                    - busy
                  example: available
                supersetRole:
                  type: string
                  description: 'The superset role of the user. List of available options is retrieved from /api/mind/profile/{id:\d+}/supersetRoles'
                ticketsFilters:
                  type: object
                  properties:
                    groups:
                      type: array
                      items:
                        type: number
                        example: 1
                    intents:
                      type: array
                      items:
                        type: string
                        example: process_meter_reading
                    tagIds:
                      type: array
                      items:
                        type: number
                        example: 1
                    personalQueue:
                      type: boolean
                      description: 'Use skills to filter tickets'
                      example: true
                    onlyIntents:
                      type: boolean
                      description: 'Show only tickets with intents'
                      example: true
                roleId:
                  type: integer
                  description: 'The role ID to assign to the user'
                  example: 1
                teamIds:
                  type: array
                  description: 'The IDs of teams the user should be assigned to'
                  items:
                    type: integer
                    example: 1
                skills:
                  type: object
                  description: 'The skills to assign to the user (use this field in request body for create/update operations)'
                  properties:
                    tagIds:
                      type: array
                      items:
                        type: integer
                      example:
                        - 1
                        - 2
                    channels:
                      type: array
                      items:
                        type: string
                      example:
                        - email
                        - chat
                actualRoleId:
                  type: integer
                  description: 'The id of the role that is used for the user (including inherited from teams)'
                  readOnly: true
                  example: 1
                actualTeamIds:
                  type: array
                  description: 'The ids of the teams that the user is directly assigned to (without parent teams)'
                  readOnly: true
                  items:
                    type: integer
                    example: 1
                actualSkills:
                  type: object
                  description: 'The skills of the user (including inherited from teams) - read-only field shown in response'
                  readOnly: true
                  properties:
                    intents:
                      type: array
                      items:
                        type: string
                        example: process_meter_reading
                    tagIds:
                      type: array
                      items:
                        type: number
                        example: 1
                    channels:
                      type: array
                      items:
                        type: string
                        example: email
                limitTicketBacklogAccess:
                  type: boolean
                  description: 'If true, the user can only see tickets with specific tags'
                  example: false
                ticketBacklogRequiredTagIds:
                  type: array
                  description: 'The ids of the tags that are required for the user to see tickets'
                  items:
                    type: integer
                    example: 1
                tagsOnRoute:
                  type: array
                  description: 'Tags to auto-assign to a ticket when it is routed to the user'
                  items:
                    type: integer
                    example: 1
                tagsOnEdit:
                  type: array
                  description: 'Tags to auto-assign to a ticket when the user edits the ticket'
                  items:
                    type: integer
                    example: 1
                actualLimitTicketBacklogAccess:
                  type: boolean
                  description: 'If true, the user can only see tickets with specific tags (inherited from teams)'
                  example: false
                actualTicketBacklogRequiredTagIds:
                  type: array
                  description: 'The ids of the tags that are required for the user to see tickets (inherited from teams)'
                  items:
                    type: integer
                    example: 1
                actualTagsOnRoute:
                  type: array
                  description: 'Tags auto-assigned on route, including inherited from teams'
                  items:
                    type: integer
                    example: 1
                actualTagsOnEdit:
                  type: array
                  description: 'Tags auto-assigned on edit, including inherited from teams'
                  items:
                    type: integer
                    example: 1
                isPersonalFilters:
                  type: boolean
                  description: 'If true, the filters are personal and only apply to the user'
                  example: false
                inheritTeamSettings:
                  type: boolean
                  description: 'If true, the filters are inherited from the team'
                  example: false
                nameReports:
                  type: string
                  description: 'The name of the reports'
                  example: 'Tapfere Feige'
                callRoutingStatus:
                  $ref: '#/components/schemas/RoutingStatus'
                  description: 'The routing status for phone calls'
                chatRoutingStatus:
                  $ref: '#/components/schemas/RoutingStatus'
                  description: 'The routing status for chat conversations'
            -
              type: object
              nullable: true
    Intent:
      type: object
      description: 'An intent of a customer that he wants to be resolved by contacting us'
      properties:
        id:
          type: integer
          description: 'Internal ID of intent'
          example: 1211221
        code:
          type: string
          nullable: true
          description: 'Intent model code that resolves a specific issue. Currently supported is process_meter_reading, process_bank_data and process_installment_change'
          example: process_meter_reading
        name:
          type: string
          description: 'User-readable name of intent as defined by client'
          example: 'Process a meter reading'
        contractId:
          type: string
          example: '746839'
        status:
          type: string
          description: "Lifecycle status of the intent. `preview` — created as a non-persisted preview. `ready` — active, waiting for agent action. `executed` — action taken, but dialogue may continue (interaction with remaining options). `archived` — terminal: action taken and dialogue is closed (text reply sent, or interaction with no remaining options). Preserved as audit history. `invalidated` — underlying data changed, will be re-processed on next load. `deleted` — soft-deleted.\n"
          enum:
            - preview
            - ready
            - executed
            - archived
            - invalidated
            - deleted
          example: ready
        confidence:
          type: number
          format: float
          description: 'Confidence how certain the AI is that this is correct. 0.95 means for example 95% certain. Defaults to 100% when verified by human.'
          example: 0.95
        confidenceColor:
          type: string
          example: warning
          description: "A color indication of an intent showing the user how much user interaction is needed. 'success' indicates the AI is ready to execute, all other codes require user interaction first"
          enum:
            - success
            - neutral
            - warning
            - danger
        verified:
          type: boolean
          description: 'True if intent has been verified by human'
          example: false
        context:
          type: object
          description: 'Intent-specific additional data to be shown to the user so he has relevant information about the intent. Data type varies by intent code'
        messagePreview:
          type: string
          nullable: true
          description: 'A preview of the response a customer would receive when executing this intent. Not always available, e.g. when the respond depends on user-input'
          example: 'We have successfully processed your meter reading'
        recipient:
          type: string
          example: john@smith.com
        tags:
          type: string
          description: 'Tags to show to the user. prePopulated means that all properties are already pre-filled by the AI and the user can just insert the template to the body. adaptionNeeded is the opposite, and means the user still needs to modify the text. writesToErp means that upon execution of the intent a write is done to the ERP system, like a insertion of a meter reading or the creation of a bill.'
          enum:
            - prePopulated
            - adaptionNeeded
            - writesToErp
        data:
          type: object
          description: 'Intent-specific data object showing all the data that was extracted from the ticket in a structured form. Data type varies by intent code'
        options:
          description: 'Different options that the user is given. Every option is a new button in the UI'
          type: array
          items:
            $ref: '#/components/schemas/IntentOption'
          example:
            -
              type: enter_into_system
              name: Eintragen
              icon: check
              recommended: true
              order: 1
            -
              type: ignore
              name: Ignorieren
              icon: cancel
              recommended: false
              order: 2
            -
              type: forward_to_vnb
              name: 'An VNB verweisen'
              icon: questionMark
              recommended: false
              order: 3
        infos:
          type: array
          items:
            $ref: '#/components/schemas/IntentInfo'
        extraInfo:
          type: string
          example: null
          nullable: true
          description: 'Extra-Information shown in tooltip. Info-Icon only shown if not null'
        outcome:
          $ref: '#/components/schemas/IntentOutcome'
    IntentOption:
      type: object
      properties:
        type:
          type: string
        name:
          type: string
        icon:
          type: string
        recommended:
          type: boolean
        order:
          description: 'Buttons ordered from lowest (shown first) to highest (shown last)'
          type: integer
    IntentInfo:
      type: object
      description: 'Info areas shown to the user'
      properties:
        code:
          type: string
          nullable: true
          description: 'Machine-readable code identifying the info type, e.g. "plausible", "USER_WARNING", "TRACE_INFO", "ERROR_MESSAGE"'
          example: USER_WARNING
        type:
          type: string
          example: warning
          enum:
            - success
            - neutral
            - warning
            - danger
        message:
          type: string
          example: 'Reading is plausible'
        extraInfo:
          type: string
          example: 'Expected reading was 421 kWh. Plausbible because difference to 317 kWh is below threshold of 200 kWh'
          description: 'Extra-Information shown in tooltip. Info-Icon only shown if not null'
          nullable: true
    IntentOutcome:
      type: object
      nullable: true
      description: 'Shows the outcome (if the intent was already executed)'
      properties:
        aiAgentId:
          type: integer
          format: int32
          description: 'ID of the AI agent that was executed'
          example: 1
        success:
          type: boolean
          example: true
        messageLocalized:
          description: 'Human-readable message, usually from the backend system. Already localized.'
          type: string
          example: 'Powercloud accepted meter reading'
        internalData:
          type: object
          description: 'Technical output for debugging purposes'
          additionalProperties: true
          example:
            requestEndpoint: saveReadingByContractId
            requestParams: readingValue=21;date=2022-12-31
        executedAt:
          type: string
          description: 'Date and time when intent was executed'
          format: DateTime
          example: '2022-12-13 22:18:06'
        userId:
          type: integer
          description: 'User ID of user that triggered the intent. 0 for system user'
          example: 1
        sent:
          description: 'Was the email / chat message / etc. described in this object  successfully sent?'
          type: boolean
        ticketClosed:
          description: 'Was the ticket closed?'
          type: boolean
          example: true
        recipient:
          type: string
          example: john@smith.com
        message:
          type: string
          nullable: true
          example: 'We successfully processed your meter reading of 21 kWh dated Dec 31, 2022'
        template:
          type: string
          example: '<p>Dear John,</p><p>%MESSAGE%</p><i>Mike from your service team</i>'
          nullable: true
        sources:
          type: array
          description: 'Any sources that were used to create the result'
          items:
            $ref: '#/components/schemas/Source'
        txId:
          type: string
          example: c916167c94
          description: 'Unique transaction id. Corresponds to a log entry in enneo'
    AiAgent:
      type: object
      properties:
        id:
          type: integer
          format: int32
          minimum: 0
          description: 'Unique identifier of the AI agent (unsigned integer)'
          example: 1
        tagId:
          type: integer
          description: 'Category of this ai agent'
          example: 41
        channels:
          type: array
          description: 'Channels this ai agent is available on'
          items:
            type: string
          example:
            - all
        name:
          type: string
          description: 'The name of the AI agent visible to the user. It is also used by the LLM to validate if the intent is correctly identified'
          example: 'Change bank data (Code)'
        description:
          type: string
          description: 'The description of the AI agent as defined by the user. It is also used by the LLM to validate if the intent is correctly identified'
          example: 'Customer wants to change his bank data'
        appearance:
          type: object
        exampleTicketIds:
          type: array
          items:
            type: integer
          example:
            - 8
            - 26
        settings:
          type: object
    Executor:
      type: object
      description: 'An executor that runs user-specified code. Currently, we have three supported types: sourceCode,  apiCall and visualEditor (work in progress). Next is an executor for the visual editor. The type of executor is defined by the "type" property'
      oneOf:
        -
          $ref: '#/components/schemas/SourceCodeExecutor'
        -
          $ref: '#/components/schemas/ApiCallExecutor'
        -
          $ref: '#/components/schemas/VisualEditorExecutor'
    SourceCodeExecutor:
      type: object
      properties:
        type:
          type: string
          description: 'Specifies that this is a sourceCode executor'
          example: sourceCode
        id:
          type: integer
          description: 'ID that has to be unique within the AI agent'
          example: 0
        code:
          type: string
          example: "<?php\necho 'hi';"
        language:
          type: string
          description: 'Programming language of this source code'
          enum:
            - php82
            - python311
            - node18
          example: php82
        packages:
          type: string
          description: 'Spacebar separated list of packages. Used as parameter for composer require , npm install or pip install'
          example: 'spatie/pdf-to-image ezyang/htmlpurifier'
        parameters:
          type: array
          description: 'Reference to the input parameter IDs that are passed on to this executor'
          items:
            type: integer
          example:
            - 0
            - 1
            - 2
            - 3
            - 1698862629088
            - 1698866968168
    ApiCallExecutor:
      type: object
      properties:
        type:
          type: string
          description: 'Specifies that this is an apiCall executor'
          example: apiCall
        id:
          type: integer
          description: 'ID that has to be unique within the AI agent'
          example: 0
        url:
          type: string
          example: /api/mind/experimental/intentTests/checkBankAccount
        method:
          type: string
          description: 'The HTTP method used for the request'
          enum:
            - GET
            - POST
        headers:
          type: object
          description: 'Key-value object of headers'
          additionalProperties:
            type: string
          example:
            Authorization: 'Bearer secret-bearer-key'
        parameters:
          type: array
          items:
            type: integer
          description: 'Reference to the input parameter IDs that are passed on to this executor as query parameters (GET call) or json-encoded in the body (POST call)'
          example:
            - 0
            - 1
            - 2
            - 3
            - 1698862629088
            - 1698866968168
        body:
          type: array
          items:
            type: integer
          deprecated: true
          description: 'Reference to the input parameter IDs that are passed on to json-encoded in the body (POST call)'
          example:
            - 0
            - 1
            - 2
            - 3
            - 1698862629088
            - 1698866968168
        params:
          type: array
          items:
            type: integer
          deprecated: true
          description: 'Reference to the input parameter IDs that are passed on to this executor as query parameters (GET call) or json-encoded in the body (POST call)'
          example:
            - 0
            - 1
            - 2
            - 3
            - 1698862629088
            - 1698866968168
    VisualEditorExecutor:
      type: object
      properties:
        type:
          type: string
          description: 'Specifies that this is an visualEditor executor'
          example: visualEditor
        id:
          type: integer
          description: 'ID that has to be unique within the AI agent'
          example: 0
        editingUrl:
          type: string
          format: uri
          example: 'https://retool.enneo.ai/workflows/9d0d6fd5-7166-4624-b0ec-c6c2e5da8c20'
        executionUrl:
          type: string
          format: uri
          example: 'https://retool.enneo.ai/retool/v1/workflows/9d0d6fd5-7166-4624-b0ec-c6c2e5da8c20/startTrigger?workflowApiKey=retool_wk_af295c16c83a4e55b41a611f2850671c'
        parameters:
          type: array
          items:
            type: integer
          description: 'Reference to the input parameter IDs that are passed on to this executor json-encoded in the body'
          example:
            - 0
            - 1
            - 2
            - 3
            - 1698862629088
            - 1698866968168
    Parameter:
      type: object
      properties:
        id:
          type: integer
          description: 'Input parameter ID. Needs to be unique in this AI Agent. References by executors and output handling cases'
          example: 0
        key:
          type: string
          example: contractId
        name:
          type: string
          example: 'Contract Number'
        type:
          type: string
          example: int
        source:
          type: string
          description: "Where the AI should take this data from.\n'contract'/'customer'/'ticket' means from the associated metadata from this ticket\n'message' means that the AI uses an LLM query using the description to extract it from the message\n'manual' means a hardocded value\n"
          enum:
            - ticket
            - contract
            - customer
            - message
            - manual
          example: contract
        required:
          description: "If set to true, then the executor won't be triggered unless this parameter is available"
          type: boolean
        visibility:
          type: string
          description: 'The visibility status of the parameter, only determined if it should be shown to the frontend'
          enum:
            - visible
            - hidden
            - readonly
          default: visible
          example: visible
        description:
          type: string
          description: 'Description of this parameter. When'
          example: 'Contract ID'
        sourceKey:
          type: string
          example: id
          description: 'When source is metadata, i.e. contract/customer/ticket, this field identifies which json-value should be used'
        value:
          description: 'When specified, the AI does not auto-detect this value, but takes it. Deprecated, as the "data" parameter is used for this'
          deprecated: true
          type: string
        config:
          type: object
          description: "Optional frontend rendering hints for 'str' parameters. Persisted as-is and forwarded to cortex,\nwhich translates it into the interaction form field (e.g. a textarea).\n"
          properties:
            multiline:
              type: boolean
              description: 'Render a multiline textarea instead of a single-line input'
              default: false
            autosubmit:
              type: boolean
              description: 'When false, the agent is not re-executed on input; the operator submits explicitly'
              default: true
            lines:
              type: integer
              description: 'Visible row height of the textarea (used when multiline is true)'
              example: 3
    ResponseCase:
      type: object
      properties:
        description:
          type: string
          example: 'IBAN valid, submit to system'
        condition:
          type: object
          properties:
            operands:
              type: array
              items:
                $ref: '#/components/schemas/Operand'
            operator:
              type: string
              example: And
        output:
          type: object
          properties:
            type:
              type: string
              example: llm
            parameters:
              type: array
              description: 'Parameters passed on to the LLM that is uses to generate the answer. Can be e.g. the result of the excecutor. If not specified, all parameters are passed to the LLM'
              items:
                type: integer
            templateId:
              type: string
        executor:
          type: array
          description: 'If populated, these executors are called if this response case matches. If one executor returns a http status code different than 2xx, the text output is not run, but an error is returned to the user. A typical use case is a writing request to the ERP that was previously manually approved by the agent.'
          items:
            $ref: '#/components/schemas/Executor'
    Operand:
      type: object
      properties:
        id:
          type: integer
        value:
          type: string
        operand:
          type: integer
        operator:
          type: string
          example: Equal
    IntentCandidate:
      type: object
      description: 'A potential intent that is offered or recommended to the user'
      properties:
        tagId:
          type: integer
          description: 'Tag id that belongs to the intent'
          example: 1
        code:
          type: string
          description: 'Intent model code that resolves a specific issue. Currently supported is process_meter_reading, process_bank_data and process_installment_change'
        name:
          type: string
          description: 'Name of intent as configured by client'
        confidence:
          type: number
          format: float
          description: 'Confidence how certain the AI is that this is correct. 0.7 means for example 70% certain. Defaults to 100% when verified by human.'
        channels:
          type: array
          items:
            $ref: '#/components/schemas/Channel'
        customerNeeded:
          type: boolean
          description: 'Defines if the user is needed for executing intent'
      example:
        code: process_meter_reading
        name: 'Process meter reading'
        categoryName: 'Meter reading'
        confidence: 0.91
        tags:
          - prePopulated
          - writesToErp
        message: null
        order: 1
    IntentCandidateList:
      type: array
      items:
        $ref: '#/components/schemas/IntentCandidate'
    AiAgentList:
      type: array
      items:
        $ref: '#/components/schemas/AiAgent'
    ReplyRecipients:
      type: object
      nullable: true
      description: "Recommended recipients for replying to this ticket. Calculated based on channel, direction, latest conversation, and ERP data.\nReturns null for system tickets (cannot be replied to)."
      properties:
        to:
          type: array
          description: 'Primary recipients (email addresses or postal addresses depending on channel)'
          items:
            type: string
          example:
            - customer@example.com
        cc:
          type: array
          description: 'CC recipients (email addresses only)'
          items:
            type: string
          example:
            - cc@example.com
        bcc:
          type: array
          description: 'BCC recipients (email addresses only)'
          items:
            type: string
          example: []
      example:
        to:
          - customer@example.com
        cc:
          - manager@example.com
        bcc: []
    Attachment:
      type: object
      properties:
        id:
          type: integer
          description: 'id of attachment'
          example: 103013960646
        url:
          type: string
          format: uri
          example: 'https://client.enneo.ai/attachments/reading_w23po_107647890574i49994_(1).pdf'
        name:
          type: string
          example: reading_w23po_107647890574i49994_(1).pdf
        size:
          type: integer
          description: 'size of attachment in bytes.'
          example: 721592
        width:
          type: integer
          description: 'width of image attachment in pixels. Can be ommitted for non-image attachments.'
          example: 3120
        height:
          type: integer
          description: 'height of image attachment in pixels. Can be ommitted for non-image attachments.'
          example: 4160
        inline:
          type: boolean
          description: 'true if attachment is an inline image'
          example: false
        fileEnding:
          type: string
          description: 'file ending of attachment'
          example: pdf
        contentType:
          type: string
          description: 'content type of attachment'
          example: application/pdf
        originalUrl:
          type: string
          format: uri
          description: 'url of original attachment, most likely contains invalid temporary access token'
          example: 'https://storage.example.com/attachments/reading_w23po_107647890574i49994_(1).pdf'
        extractedData:
          type: object
          description: 'data extracted from the attachment, e.g. a PDF text or a meter reading'
        extractionStatus:
          type: string
          description: 'Status of the current extraction. Valid options: "notStarted", "inProgress", "success", "error"'
          example: notStarted
        extractionData:
          type: object
          description: 'Data extracted from the attachment by meterReadingMicroservice'
          example:
            confidence: 1
            meterValue: 1234
      example:
        id: '103013960646,'
        url: 'https://storage.googleapis.com/enneo-attachments-public/a1/103013960646-4b1aa775/IMG_20230301_192712.jpg'
        name: IMG_20230301_192712.jpg
        size: 2292952
        width: 3120
        height: 4160
        inline: false
        fileEnding: jpg
        contentType: image/jpeg
        originalUrl: 'https://storage.example.com/attachments/IMG_20230301_192712.jpg'
        extractedData: null
        extractionStatus: success
        extractionData:
          confidence: 0.815
          meterValue: 87870.5
    Conversation:
      type: object
      description: 'A conversation is an interaction to a ticket. Typically a reply or internal note to an email, or a chat message for a chat'
      properties:
        id:
          type: integer
          example: 123
          description: 'ID of conversation'
        ticketId:
          type: integer
          example: 123
        type:
          type: string
          description: 'Type of message'
          example: html
        direction:
          type: string
          example: out
        private:
          type: boolean
          description: 'If true, then this conversation is a private/internal note of an agent'
          example: false
        isDraft:
          type: boolean
          description: 'If true, then this conversation is a draft that requires supervisor approval before sending'
          example: false
        fromEmail:
          deprecated: true
          type: string
          example: service@enneo.ai
        fromEmailName:
          deprecated: true
          type: string
          example: 'Enneo Admin'
        agentId:
          deprecated: true
          type: integer
          example: 1
        sender:
          type: object
          description: 'Sender of the message'
          properties:
            id:
              type: integer
              example: 1
            name:
              type: string
              example: 'Enneo Admin'
            email:
              type: string
              example: example@enneo.ai
            subchannelId:
              type: integer
              nullable: true
              example: 1
              description: 'The subchannel ID associated with the sender'
        toEmail:
          description: 'Only for email channel'
          type: array
          items:
            type: string
            example: to@gmail.com
        to:
          description: 'Array of recipients. For emails, contains email addresses (e.g. service@enneo.ai). For letters, contains postal addresses (e.g. "John Smith, Smithway 1, 12345 Berlin, Germany")'
          type: array
          items:
            type: string
            example: service@enneo.ai
        ccEmails:
          description: 'Only for email channel'
          type: array
          items:
            type: string
            example: cc@gmail.com
        bccEmails:
          description: 'BCC email addresses. Only for email channel'
          type: array
          items:
            type: string
            example: bcc@gmail.com
        channelId:
          description: 'ID of chat (if channel is chat) or message ID from IMAP mail server (if channel is email)'
          type: string
          nullable: true
          example: msg-123-abc
        content:
          type: object
          description: 'Content of the message'
          properties:
            message:
              type: string
              description: 'Message content'
              example: 'Thank you for your message, we can inform you that...'
          example:
            message: 'Thank you for your message, we can inform you that...'
        intentIds:
          type: array
          description: 'IDs of intents that were detected in the message'
          items:
            type: string
            example: ai_agent_meter_reading
        cortexRequestId:
          type: integer
          description: 'ID of the request to cortex'
          nullable: true
          example: 123
        externalConversationId:
          type: string
          description: 'ID of conversation in external system'
          nullable: true
          example: abc-123
        isRead:
          type: boolean
          description: 'Whether the conversation has been read'
          example: false
        attachments:
          type: array
          items:
            $ref: '#/components/schemas/Attachment'
        interface:
          type: object
          description: 'Interface object for conversation data import/export'
          example: []
        createdAt:
          type: string
          format: DateTime
          example: 1657056276
        modifiedAt:
          type: string
          format: DateTime
          example: 1672756198
    Contract:
      type: object
      description: 'A contract of a customer. Either electricity or gas. IDs match the backend system (powercloud)'
      properties:
        id:
          type: integer
          example: 746839
        orderId:
          type: integer
          example: 123
        tenant:
          type: string
          description: 'If a customer has multiple tenants/brands, this is the tenant name. Currently only used for SAP'
          nullable: true
          example: null
        customerId:
          type: string
          example: '123'
        signupDate:
          type: string
          format: date
          example: 1661126400
        status:
          type: string
          example: active
        statusCode:
          type: integer
          example: 5000
        statusCodeTitle:
          type: string
          example: active
        energy:
          type: string
          example: electricity
        energyTypeId:
          type: integer
          example: 1
        energySubTypeId:
          type: integer
          example: 1
        email:
          type: string
          example: johndoe@example.com
        firstname:
          type: string
          example: John
        lastname:
          type: string
          example: Doe
        deliveryAddress:
          type: string
          description: 'Address where the electricity/gas is being delivered to'
          example: 'Hugo-Junkers-Str. 5, 82031 Grünwald'
        iban:
          type: string
          example: DE89370400440532013000
          nullable: true
        monthlyDeposit:
          type: number
          format: integer
          example: 53
          nullable: true
        startDate:
          type: string
          format: date
          example: 1661990400
          nullable: true
        endDate:
          type: string
          example: null
          nullable: true
        consumption:
          type: integer
          example: 3477
          nullable: true
        counterNumber:
          type: string
          example: 1ESY1160669167
          nullable: true
        productId:
          type: integer
          example: 1
          nullable: true
        productName:
          type: string
          example: 'Flex 24'
          nullable: true
        productCode:
          type: string
          example: flx_24
          nullable: true
        campaignId:
          type: integer
          example: 1
          nullable: true
        salesChannelId:
          type: integer
          example: 1
          nullable: true
        unpaidDebt:
          type: number
          format: float
          example: 109.21
          nullable: true
        vatRate:
          type: number
          format: float
          example: 0.09
          description: 'VAT Rate, usually 0.19 (=19%) for electricity and 0.07 (=7%) for gas'
          nullable: true
        basePrice:
          type: number
          format: float
          example: 0.09
          nullable: true
        workingPrice:
          type: number
          format: float
          example: 0.09
          nullable: true
        billingAddress:
          type: string
          description: 'Address where written communication should be sent to. NULL if same as delivery address'
          example: 'Rechnungssstraße 24, 10587 Berlin'
          nullable: true
        business:
          type: boolean
          example: false
          nullable: true
        phone:
          type: string
          example: 491721041689
          nullable: true
        gridOperatorName:
          type: string
          example: 'E.ON Bayern AG'
          nullable: true
        gridOperatorCode:
          type: string
          example: 1000000000000
          nullable: true
        registers:
          type: array
          items:
            type: object
          nullable: true
        erpUrls:
          type: array
          items:
            type: object
            properties:
              url:
                type: string
                example: 'https://erp-system.com/contract/123'
              logo:
                type: string
                description: 'Logo the FE should show when processing an URL of this type'
                enum:
                  - powercloud
                  - lynqtech
                  - sap
                example: powercloud
          nullable: true
        rawData:
          nullable: true
          type: object
          description: 'Raw source data from the originating ERP system in the ERP-proprietary format is included here. Only provided on request.'
          additionalProperties: true
          example: null
        agentPreview:
          type: array
          description: 'Preview information shown to agents in the UI'
          items:
            type: object
            properties:
              label:
                type: string
                description: 'Label text shown to the agent'
                example: Tarif
              value:
                type: string
                description: 'Value text shown to the agent'
                example: 'Flex 24'
              tooltip:
                type: string
                nullable: true
                description: 'Optional tooltip shown on hover'
                example: "AP: 28.5 ct/kWh\nGP: 149.88 €/Jahr"
              url:
                type: string
                nullable: true
                description: 'Optional URL that opens when clicking the preview item'
                example: 'https://erp.example.com/contract/123'
        tabPreview:
          type: object
          nullable: true
          description: 'Preview information shown in contract tabs'
          properties:
            color:
              type: string
              enum:
                - blue
                - green
                - red
                - grey
                - yellow
                - purple
                - orange
                - teal
              description: 'Color indicator for contract status'
              example: green
            icon:
              type: string
              enum:
                - electricity
                - gas
                - other
              description: 'Icon shown in the tab'
              example: electricity
    Ticket:
      type: object
      description: "A ticket, e.g. an email conversation, call or chat conversation.\n\n⚠️ Response format varies by endpoint:\n- POST /ticket/search: Returns compact format (excludes: body, bodyPlain, bodyClean, attachments, template, workitem)\n- GET /ticket/{id}: Returns full ticket data with all fields"
      properties:
        id:
          type: integer
          example: 376189
        direction:
          type: string
          description: 'Direction of the ticket'
          enum:
            - in
            - out
            - internal
          example: in
        from:
          oneOf:
            -
              type: string
              format: email
              description: 'Sender of email. Used when channel is not call. Null is channel=system'
              nullable: true
              example: tom@gmail.com
            -
              type: integer
              example: 491721041689
              description: "Requester's telephone number address, used when channel is call"
            -
              type: integer
              format: ipv4
              example: 12.23.45.67
              description: "Requester's IP address, used when channel is chat AND the user did not provide an email to the chat bot yet"
        fromName:
          type: string
          description: "Provided when customer's request included a Name, e.g. 'Tom Mustermann <tom@mm.com>' for an email or during the chat bot initialization. Null when not set"
          nullable: true
          example: 'Tom Mustermann'
        to:
          description: 'Recipent of request. Can be an array of email addresses, but also a phone number or chat group name depending on channel'
          oneOf:
            -
              type: array
              items:
                type: string
                format: email
                example: service@energy.com
                description: 'E-Mail address, used when channel is email,portal,chat'
            -
              type: integer
              example: 49303000001
              description: "Client's customer service center telephone number address, used when channel is call"
        ccEmails:
          type: array
          deprecated: true
          description: '⚠️ DEPRECATED: Use replyRecipients.cc instead. Any emails in CC. Used when channel=email, otherwise empty array'
          items:
            type: string
            format: email
            example: secondlevel@enneo.ai
        bccEmails:
          type: array
          deprecated: true
          description: '⚠️ DEPRECATED: Use replyRecipients.bcc instead. Any emails in BCC. Used when channel=email, otherwise empty array'
          items:
            type: string
            format: email
            example: archive@enneo.ai
        replyCcEmails:
          type: array
          deprecated: true
          description: '⚠️ DEPRECATED: Use replyRecipients.cc instead. Any emails in CC for a reply. Used when channel=email, otherwise empty array'
          items:
            type: string
            format: email
        replyRecipients:
          $ref: '#/components/schemas/ReplyRecipients'
        subchannelId:
          type: integer
          description: 'ID of the subchannel'
          nullable: true
        externalTicketId:
          type: string
          description: "Unique identifier from an external ticketing system.\n\nCan be provided when creating a ticket via POST /ticket to prevent duplicates. If a ticket with this externalTicketId already exists, it will be updated instead of creating a new one."
          nullable: true
          example: EXT-12345
        sentiment:
          type: string
          description: 'AI-detected sentiment'
          nullable: true
          example: positive
        language:
          type: string
          description: 'Detected language name'
          nullable: true
          example: English
        languageCode:
          type: string
          description: 'Detected language code'
          nullable: true
          example: en
        autoExecuteAt:
          type: string
          format: date-time
          description: 'When the ticket should be auto-executed'
          nullable: true
        aiSupportLevel:
          type: string
          enum:
            - unprocessed
            - human
            - bot
            - automated
          description: 'Level of AI support for this ticket'
        spamStatus:
          type: string
          enum:
            - auto_spam
            - auto_clean
            - auto_programmatic
            - manual_spam
            - manual_clean
          description: "Spam classification status of the ticket. Primary marker for routing and backlog spam filters.\n\n- `auto_spam` / `auto_clean` / `auto_programmatic`: Set automatically (headers, Cortex). `auto_programmatic` = header rule marked programmatic.\n- `manual_spam` / `manual_clean`: Set manually via API. Manual statuses are not overwritten by automatic detection.\n\nSpam statuses (`auto_spam`, `auto_programmatic`, `manual_spam`) exclude tickets from routing (`ticketScope=skills`).\nBacklog filter: `t.spamStatus` with `in` / `not in` and spam status values (`not in` includes NULL as not-spam).\n\nTickets marked as spam are skipped during automatic AI processing. A configured `spamDetectionTagIds` tag also marks spam at runtime (`Ticket::isSpam()`), but SQL filters use this column only."
          nullable: true
          example: auto_clean
        isCustomerActive:
          type: boolean
          description: 'Whether the customer is currently active'
        interface:
          type: string
          enum:
            - internal
          description: 'Name of the ticketing system interface'
          example: internal
        priority:
          $ref: '#/components/schemas/Priority'
        channel:
          $ref: '#/components/schemas/Channel'
        channelId:
          type: string
          description: 'Unique ID of chat conversation. Used when channel=chat'
          example: 32291c7e-1cce-4d4c-8269-15e6a6501466
          nullable: true
        status:
          $ref: '#/components/schemas/Status'
        summary:
          type: string
          description: 'Summary of request'
          example: 'I want to relocate and send you a meter reading'
        subject:
          type: string
          description: 'Subject of email'
          example: 'I want to relocate and send you a meter reading'
        bodyPlain:
          type: string
          description: "Body of request in plain text form (no HTML).\n\n⚠️ Only included in GET /ticket/{id}, NOT in POST /ticket/search responses.\nMay be null if ticket has no body content."
          nullable: true
          example: "Sehr geehrter Kundenservice,\n\nkönnten Sie bitte den beiliegenden Zählerstand meines Zählers in Ihr System eintragen und mir eine Zwischenrechnung zukommen lassen? Außerdem würde ich gerne zu meiner neuen Adresse Hugo-Junkers-Str. 5 in 82031 Grünwald wechseln. Ich ziehe nächsten Donnerstag ein. Können Sie meinen alten Vertrag zum 1. November für mich kündigen?\n\nIch danke Ihnen, Tom"
        body:
          type: string
          description: "Body of request in originial form (incl. HTML).\n\n⚠️ Only included in GET /ticket/{id}, NOT in POST /ticket/search responses.\nMay be null if ticket has no body content."
          nullable: true
          example: '<p>Sehr geehrter Kundenservice,</p><p>k&#246;nnten Sie bitte den beiliegenden Z&#228;hlerstand meines Z&#228;hlers in Ihr System eintragen und mir eine Zwischenrechnung zukommen lassen? Au&#223;erdem w&#252;rde ich gerne zu meiner neuen Adresse Hugo-Junkers-Str. 5 in 82031 Gr&#252;nwald wechseln. Ich ziehe n&#228;chsten Donnerstag ein. K&#246;nnen Sie meinen alten Vertrag zum 1. November f&#252;r mich k&#252;ndigen?</p><p>Ich danke Ihnen, Tom</p>'
        bodyClean:
          type: string
          description: "Version of body without any footers, signatures or other non-relevant content. Used as input for AI models.\n\n⚠️ Only included in GET /ticket/{id}, NOT in POST /ticket/search responses.\nMay be null if ticket has no body content."
          nullable: true
          example: 'Sehr geehrter Kundenservice, könnten Sie bitte den beiliegenden Zählerstand meines Zählers in Ihr System eintragen und mir eine Zwischenrechnung zukommen lassen?'
        isEscalated:
          type: boolean
          example: false
        createdAt:
          type: string
          format: DateTime
          example: '2022-12-13 22:18:06'
        modifiedAt:
          type: string
          format: DateTime
          nullable: true
          description: 'Last change of this ticket'
          example: null
        firstResponseDueBy:
          type: string
          format: DateTime
          nullable: true
          description: 'Latest date by when this ticket should be responded to, but not necessarily solved'
          example: '2022-12-14 22:18:06'
        dueBy:
          type: string
          format: DateTime
          nullable: true
          description: 'Latest date by when this ticket should be resolved'
          example: '2022-12-15 22:18:06'
        closedAt:
          type: string
          format: DateTime
          nullable: true
          description: 'Date when this ticket was closed/resolved'
          example: null
        customer:
          description: "Customer information associated with this ticket.\n\n⚠️ Format varies by endpoint:\n- POST /ticket/search: Compact format with limited properties\n- GET /ticket/{id}: Full Customer object with all properties"
          allOf:
            -
              $ref: '#/components/schemas/Customer'
          additionalProperties: true
        customerId:
          type: string
          example: '123'
          description: 'Associated primary customer id of that ticket. Null if not detected by AI or not set by user'
          nullable: true
        contractId:
          type: string
          example: '746839'
          description: 'Associated primary contract id of that ticket. Null if not detected by AI or not set by user'
          nullable: true
        partnerId:
          type: integer
          example: 123
          description: 'Associated partner id of that ticket. Null if not detected by AI or not set by user'
          nullable: true
        customerLegitimation:
          type: integer
          description: "Customer legitimation score:\n- 0 = No customer identified or no contract associated\n- 10 = Default confidence for email/portal/chat channels when no other criteria match\n- 12 = Email not found in ERP system\n- 13 = Matching contract/customer ID and lastname found in message\n- 15 = Matching contract and customer ID found in message\n- 20 = High confidence cases:\n  * System channel or outgoing/internal messages\n  * Sender email matches contract/customer email\n  * For letters: address matches customer/contract address\n- 30 = Manually confirmed by agent or AI during chat\n- 40+ = Custom level that will be set by agent/chatbot for more strict customer verification\n"
          example: 30
        customerLegitimationMessage:
          type: string
          description: 'Human-readable message explaining the customer legitimation status. Only provided when customerLegitimation is between 10 and 19.'
          nullable: true
          example: 'Customer could not be confirmed'
        responderId:
          type: integer
          description: 'User ID of agent that authored the latest response to this ticket.'
          nullable: true
          example: null
        agentId:
          type: integer
          description: 'Agent ID associated to this ticket (if any).'
          nullable: true
          example: null
        agent:
          $ref: '#/components/schemas/AuthProfile'
          description: 'Agent profile associated to this ticket. Null if no agent is assigned.'
        assignedAgentIds:
          type: array
          description: 'Array of agent IDs that are assigned to this ticket'
          items:
            type: integer
            description: 'Agent ID'
            example: 123
        assignedAgents:
          type: array
          description: 'Array of agents that are assigned to this ticket'
          items:
            $ref: '#/components/schemas/AuthProfile'
        workedOnByIds:
          type: array
          description: 'Array of agent IDs that worked on this ticket in last hour'
          items:
            type: integer
            description: 'Agent ID'
            example: 123
        workedOnBy:
          type: array
          description: 'Array of agent IDs that worked on this ticket in last hour'
          items:
            $ref: '#/components/schemas/AuthProfile'
        attachments:
          type: array
          description: "Attachments associated with this ticket (filtered: non-inline attachments or PDFs only).\n\n⚠️ Only included in GET /ticket/{id}, NOT in POST /ticket/search responses."
          items:
            $ref: '#/components/schemas/Attachment'
        additionalData:
          type: object
          description: 'Client-specific data objects can be included here'
          additionalProperties: true
          nullable: true
          example: null
        template:
          type: string
          description: "Generic email template with placeholders like \"Hello {{customer.firstname}}, %MESSAGE%, Best regards {{agent.firstName}}\".\n\n⚠️ Only included in GET /ticket/{id}, NOT in POST /ticket/search responses.\nMay be null if no template is configured for this subchannel."
          nullable: true
          example: '<p>Hallo {{customer.firstname}},</p><p>%MESSAGE%</p><p>Viele Grüße,<br>{{agent.firstName}}</p>'
        tags:
          type: array
          description: 'Tags loaded from intents'
          items:
            $ref: '#/components/schemas/Tag'
        tagIds:
          type: array
          description: 'List of tag IDs. When updating, replaces all existing tags. Cannot be used together with addTagIds/removeTagIds'
          items:
            type: integer
          example:
            - 62
            - 61
        addTagIds:
          type: array
          description: 'List of tag IDs to add to existing tags (for bulk update). Cannot be used together with tagIds'
          writeOnly: true
          items:
            type: integer
          example:
            - 62
        removeTagIds:
          type: array
          description: 'List of tag IDs to remove from existing tags (for bulk update). Cannot be used together with tagIds'
          writeOnly: true
          items:
            type: integer
          example:
            - 61
        refresh:
          type: boolean
          deprecated: true
          description: 'Deprecated. Use `refreshMode=full` instead. Special flag for bulk update — if true, skips saving the ticket to database and webhooks execution, and schedules a full AI re-run.'
          writeOnly: true
          example: true
        refreshTags:
          type: boolean
          deprecated: true
          description: 'Deprecated. Use `refreshMode=tags` instead. Special flag for bulk update — if true, only AI tag detection will be performed (faster than full processing). Skips saving to database and webhooks execution.'
          writeOnly: true
          example: true
        refreshMode:
          type: string
          enum:
            - full
            - summaryAndText
            - customer
            - tags
            - tagsRuleBased
            - agents
          description: "Special flag for bulk update. Schedules a selective AI re-run and skips saving the ticket to database and webhooks execution.\n- `full` — re-run all AI checks (equivalent to the legacy `refresh` flag).\n- `summaryAndText` — re-run summary, body extraction, sentiment.\n- `customer` — re-detect customer; if a new customer is detected, AI agents re-run too.\n- `tags` — clear manual tags (auto-assigned tags are preserved) and re-detect via AI (equivalent to the legacy `refreshTags` flag).\n- `tagsRuleBased` — like `tags` but rule-based only (channel/subchannel/condition tags); no LLM or embedding calls.\n- `agents` — re-run AI agents only.\n"
          writeOnly: true
          example: tags
        agentSkillsMatch:
          type: boolean
          description: "If false, the agent doesn't have the skills to handle the ticket"
          example: true
        intents:
          type: array
          description: "AI-detected intents for this ticket.\n\n⚠️ Format varies by endpoint:\n- POST /ticket/search: Only {id, aiAgentId, name, confidenceColor, infos} properties are included\n- GET /ticket/{id}: Full Intent objects with all properties"
          items:
            $ref: '#/components/schemas/Intent'
        rawData:
          type: object
          description: "Raw source data from the originating system in its proprietary format.\n\nOnly included if explicitly requested via includeRawData=true query parameter."
          nullable: true
          additionalProperties: true
          example: null
        workitem:
          type: object
          description: "Additional metadata for system-generated tickets (channel=system). Used internally for automated processes.\n\n⚠️ Only included in GET /ticket/{id} AND only when channel=system. Returns null for all other tickets."
          nullable: true
          additionalProperties: true
    Customer:
      type: object
      properties:
        id:
          type: integer
          example: 83771
        emailMatches:
          type: boolean
          example: true
          description: "If false, the customer sent the email from an account that doesn't match our records. Always returns 'null' when customer is retrieved by customerId and not by ticketId"
          nullable: true
        business:
          type: boolean
          example: false
        company:
          type: string
          description: 'Name of the company. Null when customer is not a business but private'
          nullable: true
          example: null
        firstname:
          type: string
          example: Tom
        lastname:
          type: string
          example: Mustermann
        address:
          type: string
          example: 'Hugo-Junkers-Str. 5'
        phone:
          type: string
          example: 49123456789
        email:
          type: string
          example: tom@mustermann.de
          format: email
        tags:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
                example: 2
              color:
                type: string
                enum:
                  - green
                  - yellow
                  - red
                  - blue
                  - grey
                example: green
              name:
                type: string
                example: VIP
        contracts:
          type: array
          description: 'Contracts that this customer has'
          items:
            $ref: '#/components/schemas/Contract'
        additionalData:
          type: object
          description: 'Client-specific data objects can be included here'
          additionalProperties: true
      nullable: true
    Filter:
      type: object
      properties:
        key:
          type: string
          enum:
            - t.id
            - t.channel
            - t.subchannelId
            - t.channelId
            - t.direction
            - t.status
            - t.priority
            - t.agentId
            - t.customerId
            - t.contractId
            - t.partnerId
            - t.isCustomerActive
            - t.aiSupportLevel
            - i.aiAgentId
            - t.createdAt
            - t.modifiedAt
            - t.firstResponseDueBy
            - t.dueBy
            - t.lastMessageAt
            - t.lastCustomerMessageAt
            - tt.tagId
            - t.sentiment
            - t.language
            - t.languageCode
            - t.modelRunAt
            - t.externalTicketId
            - t.from
            - t.spamStatus
            - w.id
            - q
          example: channel
        comparator:
          type: string
          description: 'Possible values: =, !=, >, >=, <, <=, in, not in, between, not between'
          example: in
        values:
          type: array
          description: "Used for \"in\" and \"not in\" comparators.\n\nInclude the sentinel string `null` to match SQL NULL rows together with other values:\n- `in` + `[\"null\", \"a\"]` → `(col IS NULL OR col IN ('a'))` (whitelist)\n- `not in` + `[\"null\", \"a\"]` → `(col IS NULL OR col NOT IN ('a'))` (blacklist complement)\n- `[\"null\"]` only → `col IS NULL` (`in`) or `col IS NOT NULL` (`not in`)\n\nExample: backlog not-spam filter uses `in` with `[\"null\", \"auto_clean\", \"manual_clean\"]`; spam filter uses `in` with spam statuses only."
          items:
            type: string
            example: email
        value:
          type: string
          description: 'Used for "=", "!=", ">", ">=", "<", "<=" comparators'
          example: 1609459200
        from:
          type: string
          description: 'Used for "between" and "not between" comparators'
          example: 1609459200
        to:
          type: string
          description: 'Used for "between" and "not between" comparators'
          example: 1612051200
    Reminder:
      type: object
      description: 'Reminder object'
      properties:
        id:
          type: integer
          description: 'Reminder id'
          example: 123
        ticketId:
          type: integer
          description: 'The id of the ticket against which to compare the intent examples'
          example: 321
        type:
          type: string
          description: 'Reminder type'
          enum:
            - reOpenTicket
            - freeText
        date:
          type: string
          format: DateTime
          example: '2024-08-29 14:38:12'
        status:
          type: string
          description: 'Reminder status'
          enum:
            - open
            - closed
            - deleted
    Settings:
      type: object
      description: 'Settings object'
      properties:
        name:
          type: string
          example: ticketingSystem
        category:
          type: string
          example: email
        group:
          type: string
          example: 'Generic E-Mail Settings'
          nullable: true
        label:
          type: string
          example: 'Ticketing system in use'
        description:
          type: string
          example: 'Enneo supports multiple ticketing systems. Please select one.'
        type:
          type: string
          example: select
          description: 'Format in which the FE should render this setting. Follows the enneo standard dynamic intent form options specified in Notion'
          enum:
            - text
            - date
            - radio
            - number
            - integer
            - checkbox
            - textarea
            - select
            - json
            - executor
            - executors
            - multiselect
        config:
          type: object
          description: 'Additional configuration properties that are dependant on type or rarely used'
          nullable: true
          default: null
          properties:
            options:
              description: "If type is 'select', then this lists the dropdown options available to the user"
              type: array
              items:
                type: object
                properties:
                  value:
                    type: string
                    description: 'The internal value representing the option.'
                    example: internal
                  label:
                    type: string
                    description: 'The display label for the option.'
                    example: 'Enneo-internal ticketing system'
            readonly:
              type: boolean
              description: 'Information that the FE should render this field as a readonly'
              example: false
              default: false
            password:
              type: boolean
              description: 'Information that this is sensitive information. Value will only be returned if the user has sufficient permissions, and if the query parameter showSecrets=true was provided'
              example: false
              default: false
            additionalProperties:
              type: object
        value:
          anyOf:
            -
              type: string
            -
              type: integer
            -
              type: boolean
            -
              type: object
            -
              type: array
          example: internal
        defaultValue:
          anyOf:
            -
              type: string
            -
              type: integer
            -
              type: boolean
            -
              type: object
            -
              type: array
          example: internal
    Subchannel:
      type: object
      description: 'Subchannel object'
      properties:
        id:
          type: integer
          example: 1
        channel:
          $ref: '#/components/schemas/Channel'
        name:
          type: string
          example: Support
        isDefault:
          type: boolean
          description: 'Whether this subchannel is the default for its channel. Only one subchannel per channel can be default.'
          default: false
          example: false
        interface:
          type: string
          description: "The underlying data source of this subchannel.\n\nCurrently only 'internal' is supported, meaning the subchannel is managed directly by Enneo."
          enum:
            - internal
          example: internal
        enabled:
          type: boolean
          default: true
          example: true
        data:
          type: object
          nullable: true
          additionalProperties: true
          description: 'Channel-specific properties of this subchannel, e.g. IMAP Username and password for emails'
    AiAgentValidationError:
      type: object
      description: 'Response returned when an AI agent definition contains invalid parameter references (HTTP 422)'
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          description: 'Human-readable error summary'
          example: 'The AI agent definition contains invalid parameter references. Please fix the highlighted fields and try again.'
        violations:
          type: array
          description: 'List of referential-integrity violations detected by Cortex'
          items:
            type: object
            properties:
              responseCaseId:
                type: integer
                description: 'ID of the response case that contains the broken reference'
                example: 1707648933463
              location:
                type: string
                description: 'Where in the response case the broken reference was found'
                enum:
                  - conditionOperand
                  - outputParameter
                example: conditionOperand
              missingParameterId:
                type: string
                description: 'The parameter ID that was referenced but not found in settings.parameters'
                example: '1707644198624'
              operandId:
                type: string
                nullable: true
                description: 'The operand ID that contained the broken reference (null for outputParameter violations)'
                example: '1707649003847'
              message:
                type: string
                description: 'Human-readable description of the violation'
                example: "Response case 1707648933463 condition operand references parameter id '1707644198624', which is not defined in the agent parameters."
    Error:
      type: object
      description: 'Data format of Enneo error messages'
      properties:
        error:
          type: string
          example: 'Contract 121 could not be processed'
          description: 'Readable error message that should be shown to the user'
        details:
          type: string
          example: 'Uncatched null point exception in testFunction() in /app/src/file:212'
          description: 'Not easily readable error message that is for the developer'
        txId:
          type: string
          example: c916167c94
          description: 'Internal transaction id. Useful for debugging. Corresponds to the OpenTelemetry trace ID.'
    Success:
      type: object
      description: 'Data format of Enneo success messages'
      properties:
        success:
          type: boolean
          example: true
          description: 'Operation was successful'
    Source:
      type: object
      description: 'A source that was used by an AI model / intent'
      properties:
        type:
          type: string
          description: "The type of source used. ticket = A similar ticket existed and that response was used as a basis\nknowledgeSource = An external knowledge source was used, usually a company Wiki or FAQ\ntemplate = A predefined text template could be applied\nlanguageModel = If the AI model did not use anything end created the answer by itself\n"
          enum:
            - ticket
            - knowledgeSource
            - template
            - languageModel
          example: knowledgeSource
        id:
          type: integer
          description: 'Reference to the entry id, e.g. ticketId (if type=ticket)'
          example: 376189
          nullable: true
        name:
          type: string
          description: 'Name of reference (e.g. FAQ title or ticket title)'
          example: 'Opening hours'
        url:
          type: string
          description: 'URL to associated source. null for sources with internal ids (tickets and templates) or if non-existant (language model source)'
          example: 'https://company.com/faq/376189'
          nullable: true
        text:
          type: string
          description: 'Full text that was used as source'
          example: 'Our service hours are from 8am to 5pm. We are closed on weekends. [...]'
    Tag:
      type: object
      description: 'Tag object'
      properties:
        id:
          type: integer
          description: 'Internal id, e.g. 123'
          nullable: true
          example: 123
        name:
          type: string
          description: 'e.g. "Complaint"'
          example: Complaint
        fullName:
          type: string
          description: 'The full name when not shown in a tree'
          example: 'Second Level: Complaint'
        parent:
          type: integer
          description: 'The id of the parent tag. null if on root level. Can be created to create the tree structure of tags'
          nullable: true
          example: 6
        reference:
          type: string
          description: 'To what this tag can be assigned to'
          enum:
            - ticket
            - contract
            - customer
          example: ticket
        type:
          type: string
          description: 'Type of the tag'
          enum:
            - skill
            - product
            - brand
            - customerProperty
            - contractProperty
            - other
          example: skill
        visibility:
          type: string
          description: 'Visibility of the tag'
          enum:
            - public
            - private
            - disabled
          default: public
          example: public
        color:
          type: string
          description: 'Color of the tag. Default is grey'
          enum:
            - grey
            - green
            - red
            - blue
            - yellow
            - purple
            - orange
            - teal
          example: grey
          default: grey
        properties:
          type: array
          description: "An array of strings that specify special properties. Currently only ['defaultSkill'], if this tag should be assigned if no skill was found"
          items:
            type: string
            enum:
              - defaultSkill
          example: []
        complexity:
          type: string
          description: 'The complexity of the tag'
          example: moderate
        sla:
          type: number
          format: float
          description: 'SLA in business hours. So if we have business hours from 9-17h, an 8 hour SLA means ticket has to be solved within one day. NULL means no SLA is assigned.'
          nullable: true
          example: 8
        priority:
          type: string
          description: 'If set to anything other than do-not-change, then the ticket priority will be set to this value if the tag was assigned (either manually or via AI)'
          enum:
            - do-not-change
            - low
            - medium
            - high
          example: do-not-change
        channels:
          type: array
          description: 'Channels for which this tag can be detected. If empty or null, then this tag can be assigned to all channels.'
          nullable: true
          items:
            type: string
          example:
            - email
            - chat
        subchannels:
          type: array
          description: 'Subchannels (=Mailboxes/Chat channels) for which this tag can be detected. If empty or null, then this tag can be assigned to all subchannels.'
          nullable: true
          items:
            type: number
            format: int
          example:
            - 2
            - 4
        routingRelevance:
          type: boolean
          description: "If false, this tag is ignored in routing skill-matching filters. Tags used only for reporting or classification should set this to false so they don't block ticket routing."
          default: true
          example: true
        detectionDetails:
          type: object
          description: 'Details how the AI agent is detected'
        assignment:
          type: array
          description: "Specifies how this tag is being assigned. The details in the assignment are specified in the settings page of the tag. So for example 'assignBySubchannel', 'assignByCustomLogic' means this tag is auto-assigned for certain subchannels, and using a custom logic defined in an executor."
          deprecated: true
          items:
            type: string
            enum:
              - assignByChannel
              - assignBySubchannel
              - assignByTicketProperty
              - assignByContractProperty
              - assignByCustomerProperty
              - assignByCustomLogic
              - assignByAI
          example:
            - assignBySubchannel
            - assignByCustomLogic
        testCase:
          type: object
          description: 'Test case for the tag'
        modifiedBy:
          type: string
          description: 'The user that last modified the tag'
          example: 'John Doe'
        modifiedAt:
          type: string
          format: DateTime
          description: 'The date and time when the tag was last modified'
          example: '2024-08-29 14:38:12'
    TagTree:
      type: object
      description: 'Tag object'
      properties:
        id:
          type: integer
          description: 'Internal id, e.g. 123'
          nullable: true
          example: 123
        name:
          type: string
          description: 'e.g. "Complaint"'
          example: Complaint
        fullName:
          type: string
          description: 'The full name when not shown in a tree'
          example: 'Second Level: Complaint'
        parent:
          type: integer
          description: 'The id of the parent tag. null if on root level. Can be created to create the tree structure of tags'
          nullable: true
          example: 6
        reference:
          type: string
          description: 'To what this tag can be assigned to'
          enum:
            - ticket
            - contract
            - customer
          example: ticket
        type:
          type: string
          description: 'Type of the tag'
          enum:
            - skill
            - product
            - brand
            - customerProperty
            - contractProperty
            - other
          example: skill
        visibility:
          type: string
          description: 'Visibility of the tag'
          enum:
            - public
            - private
            - disabled
          default: public
          example: public
        color:
          type: string
          description: 'Color of the tag. Default is grey'
          enum:
            - grey
            - green
            - red
            - blue
            - yellow
            - purple
            - orange
            - teal
          example: grey
          default: grey
        properties:
          type: array
          description: "An array of strings that specify special properties. Currently only ['defaultSkill'], if this tag should be assigned if no skill was found"
          items:
            type: string
            enum:
              - defaultSkill
          example: []
        complexity:
          type: string
          description: 'The complexity of the tag'
          example: moderate
        sla:
          type: number
          format: float
          description: 'SLA in business hours. So if we have business hours from 9-17h, an 8 hour SLA means ticket has to be solved within one day. NULL means no SLA is assigned.'
          nullable: true
          example: 8
        priority:
          type: string
          description: 'The priority of the tag'
          enum:
            - do-not-change
            - low
            - medium
            - high
          example: do-not-change
        routingRelevance:
          type: boolean
          description: "If false, this tag is ignored in routing skill-matching filters. Tags used only for reporting or classification should set this to false so they don't block ticket routing."
          default: true
          example: true
        assignment:
          type: array
          description: "Specifies how this tag is being assigned. The details in the assignment are specified in the settings page of the tag. So for example 'assignBySubchannel', 'assignByCustomLogic' means this tag is auto-assigned for certain subchannels, and using a custom logic defined in an executor."
          items:
            type: string
            enum:
              - assignByChannel
              - assignBySubchannel
              - assignByTicketProperty
              - assignByContractProperty
              - assignByCustomerProperty
              - assignByCustomLogic
              - assignByAI
          example:
            - assignBySubchannel
            - assignByCustomLogic
        testCase:
          type: object
          description: 'Test case for the tag'
        modifiedBy:
          type: string
          description: 'The user that last modified the tag'
          example: 'John Doe'
        modifiedAt:
          type: string
          format: DateTime
          description: 'The date and time when the tag was last modified'
          example: '2024-08-29 14:38:12'
        children:
          type: array
          items:
            $ref: '#/components/schemas/Tag'
    TagWithIntentCandidates:
      type: object
      description: 'Tag object with intent candidates inside'
      allOf:
        -
          $ref: '#/components/schemas/Tag'
        -
          type: object
          properties:
            intentCandidates:
              type: array
              items:
                $ref: '#/components/schemas/IntentCandidateList'
    TagWithAiAgents:
      type: object
      description: 'Tag object with intent candidates inside'
      allOf:
        -
          $ref: '#/components/schemas/Tag'
        -
          type: object
          properties:
            aiAgents:
              type: array
              items:
                $ref: '#/components/schemas/AiAgent'
    KnowledgeSourceAccess:
      type: object
      description: "Team-ACL \"access hole\" indicator. Present only when this node has a problem or contains one;\nomitted entirely when the node is clean. A hole = the node declares team(s) that an ancestor\nfolder forbids — because visibility is narrowing-only (reachable only by users who pass every\nrestricted ancestor AND the node's own restriction), the access the editor granted does not\nactually work. To learn what is wrong, open the item and read its real `teams`.\n"
      properties:
        blocked:
          type: boolean
          description: "True when THIS node's own teams are partly or fully cut by an ancestor folder. Present only\nwhen true (omitted otherwise).\n"
          example: true
        blockedItems:
          type: array
          description: "Direct children that are blocked or contain a blocked descendant — a one-level breadcrumb:\nfollow it down to the next level until you reach the node with `blocked: true`. Present only\nwhen non-empty. Detection is user-independent, so a problem hidden from the caller by ACL\nstill surfaces here as `{id, kind}` (only id/kind leak — never the gated content).\n"
          items:
            type: object
            properties:
              id:
                type: integer
                example: 42
              kind:
                type: string
                enum:
                  - folder
                  - article
                example: article
          example:
            -
              id: 42
              kind: article
            -
              id: 57
              kind: folder
    KnowledgeSource:
      type: object
      description: 'Knowledge source object'
      properties:
        id:
          type: integer
          description: 'Internal id, e.g. 123'
          nullable: false
          example: 123
        type:
          type: string
          description: 'Type of knowledge source'
          enum:
            - faq
            - work-instruction
            - document
            - other
            - news
            - website
          default: faq
        status:
          type: string
          description: 'Status of the knowledge source'
          enum:
            - active
            - archived
            - deleted
          default: active
          example: active
        source:
          type: string
          description: 'URL to associated source. null for sources with internal ids (tickets and templates) or if non-existant (language model source)'
          example: 'https://company.com/faq/376189'
          nullable: false
          default: ''
        name:
          type: string
          description: 'Name of knowledge source, any requests with existing name will update the existing knowledge source'
          example: 'Opening hours'
          nullable: false
        tags:
          type: array
          items:
            type: integer
          description: 'Tags that are assigned to this knowledge source'
          example:
            - 70
            - 71
          nullable: true
        teams:
          type: array
          items:
            type: integer
          description: 'Teams that restrict access to this knowledge source. When set, only agents whose active team memberships intersect this list can see the article. Applies to all types. An empty array (or omitting the field) means no restriction — all agents see it. On update, sending `teams` replaces the full set; omitting `teams` leaves the existing set unchanged.'
          example:
            - 1
            - 2
        readByAgents:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
                example: 1
              name:
                type: string
                example: 'John Doe'
              readAt:
                type: string
                format: DateTime
                example: '2024-11-29 14:38:12'
          description: 'Agents that have read the knowledge source'
          example:
            -
              id: 1
              name: 'John Doe'
              readAt: '2024-11-29 14:38:12'
          readOnly: true
        title:
          type: string
          description: 'Title of knowledge source'
          example: 'Opening hours'
          nullable: true
        text:
          type: string
          description: 'Full text that was used as source'
          example: 'Our service hours are from 8am to 5pm. We are closed on weekends. [...]'
          nullable: false
        confidential:
          type: boolean
          description: 'If true, then this knowledge source is only visible to agents'
          example: false
          nullable: false
          default: false
        excluded:
          type: boolean
          description: "For website-type pages only — true if the page URL matches the parent connector's\nsourceConfig.excludePaths. Excluded pages are filtered out of `GET /knowledgeSource`\n(bulk list) and return 404 from `GET /knowledgeSource/{id}` so Cortex sync stays consistent.\nThe configuration view (which calls `/knowledgeSourceStructure?withExcluded=true`)\nsees them with this flag set so it can render the \"Excluded\" tag and the Include action.\n"
          default: false
        access:
          $ref: '#/components/schemas/KnowledgeSourceAccess'
    knowledgeSourceStructure:
      type: array
      items:
        type: object
        properties:
          id:
            type: integer
            description: 'The unique identifier of the knowledge source structur'
            nullable: false
            example: 1
          name:
            type: string
            description: 'The name of the knowledge source structure'
            example: Fragen
            nullable: false
          type:
            type: string
            description: 'The type of the knowledge source structure'
            example: faq
            nullable: true
          description:
            type: string
            description: 'The description of the knowledge source structure'
            example: 'Allgemeine Fragen'
            nullable: true
          parent:
            type: integer
            description: 'The parent id of the knowledge source structure'
            example: 0
          articleId:
            type: integer
            description: "For website-type nodes only — the id of one representative knowledge_sources page under this\nstructure node, used by the FE to build the article navigation link.\n"
          source:
            type: string
            description: 'For website-type nodes only — the source URL of the representative page.'
          excluded:
            type: boolean
            description: "For website-type nodes only, present only when `?withExcluded=true` was passed.\nTrue if the node's source URL matches the parent connector's `sourceConfig.excludePaths`.\n"
          teams:
            type: array
            items:
              type: integer
            description: "Team IDs that may access this structure node. Empty array means the node is\nunrestricted (visible to everyone). When non-empty, only users whose\n`actualTeamIds` intersect with this list can see the node and its descendants.\n"
            example:
              - 1
              - 5
          access:
            $ref: '#/components/schemas/KnowledgeSourceAccess'
          children:
            type: array
            items:
              type: object
              properties:
                id:
                  type: integer
                  description: 'The unique identifier of the knowledge source structure'
                  nullable: false
                  example: 2
                name:
                  type: string
                  description: 'The name of the knowledge source structure'
                  example: 'Allgemeine Fragen'
                  nullable: false
                parent:
                  type: integer
                  description: 'The parent id of the knowledge source structure'
                  example: 1
                teams:
                  type: array
                  items:
                    type: integer
                  description: 'Team IDs that may access this structure node. Empty array means unrestricted.'
                  example: []
                access:
                  $ref: '#/components/schemas/KnowledgeSourceAccess'
                children:
                  type: array
                  items:
                    type: object
    WebsiteConnector:
      type: object
      description: 'Website connector object. Manages Firecrawl-based website crawling and page ingestion.'
      properties:
        id:
          type: integer
          description: 'Connector ID'
          example: 42
        name:
          type: string
          description: 'Display name'
          example: 'Example Website'
        type:
          type: string
          description: 'Always "website"'
          example: website
        description:
          type: string
          description: 'Description of the connector'
          nullable: true
        parent:
          type: integer
          description: 'Parent folder ID'
          example: 0
        pageCount:
          type: integer
          description: 'Number of active pages under this connector'
          example: 15
        data:
          type: object
          description: 'Connector configuration and state (webhookSecret is never exposed)'
          properties:
            sourceConfig:
              type: object
              properties:
                url:
                  type: string
                  example: 'https://example.com'
                maxPages:
                  type: integer
                  example: 100
                includePaths:
                  type: array
                  items:
                    type: string
                excludePaths:
                  type: array
                  items:
                    type: string
            scheduleConfig:
              type: object
              properties:
                enabled:
                  type: boolean
                  example: true
                frequency:
                  type: string
                  enum:
                    - daily
                    - weekly
                  example: weekly
                status:
                  type: string
                  enum:
                    - idle
                    - running
                    - completed
                    - error
                  example: idle
                nextRunAt:
                  type: string
                  format: date-time
                  nullable: true
                lastStartedAt:
                  type: string
                  format: date-time
                  nullable: true
                lastCompletedAt:
                  type: string
                  format: date-time
                  nullable: true
                firecrawlJobId:
                  type: string
                  nullable: true
            crawlStats:
              type: object
              properties:
                new:
                  type: integer
                  example: 5
                changed:
                  type: integer
                  example: 2
                removed:
                  type: integer
                  example: 0
                same:
                  type: integer
                  example: 10
                error:
                  type: integer
                  example: 0
            lifecycleStatus:
              type: string
              enum:
                - active
                - deleted
              example: active
    teamTree:
      type: object
      properties:
        id:
          type: integer
          description: 'The unique identifier of the team'
          nullable: false
          example: 1
        name:
          type: string
          description: 'The name of the team'
          example: Support
          nullable: false
        parent:
          type: integer
          description: 'The parent id of the team'
          example: 0
        description:
          type: string
          description: 'The description of the team'
          example: 'Allgemeine Fragen'
          nullable: true
        settings:
          type: object
          properties:
            roleId:
              type: integer
              description: 'The role'
              example: 1
            skills:
              type: object
              properties:
                tagIds:
                  type: array
                  items:
                    type: integer
                  example:
                    - 1
                    - 2
                channels:
                  type: array
                  items:
                    type: string
                  example:
                    - email
                    - chat
            supersetRole:
              type: string
              example: analyticsReadAll
            inheritParentalSettings:
              type: boolean
              example: true
        children:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
                description: 'The unique identifier of the team'
                nullable: false
                example: 2
              name:
                type: string
                description: 'The name of the team'
                example: 'Support for financial issues'
                nullable: false
              parent:
                type: integer
                description: 'The parent id of the team'
                example: 1
              description:
                type: string
                description: 'The description of the team'
                example: 'Finanzielle Fragen'
                nullable: true
              settings:
                type: object
                properties:
                  roleId:
                    type: integer
                    description: 'The role'
                    example: 1
                  skills:
                    type: object
                    properties:
                      tagIds:
                        type: array
                        items:
                          type: integer
                        example:
                          - 1
                          - 2
                      channels:
                        type: array
                        items:
                          type: string
                        example:
                          - email
                          - chat
                  supersetRole:
                    type: string
                    example: analyticsReadAll
                  inheritParentalSettings:
                    type: boolean
                    example: true
              children:
                type: array
                items:
                  type: object
    teamList:
      type: object
      properties:
        id:
          type: integer
          description: 'The unique identifier of the team'
          nullable: false
          example: 1
        name:
          type: string
          description: 'The name of the team'
          example: Support
          nullable: false
        parent:
          type: integer
          description: 'The parent id of the team'
          example: 0
        description:
          type: string
          description: 'The description of the team'
          example: 'Allgemeine Fragen'
          nullable: true
        membersCount:
          type: integer
          description: 'Number of users assigned to this team'
        settings:
          type: object
          properties:
            roleId:
              type: integer
              description: 'The role ID of the team'
              example: 1
            skills:
              type: object
              properties:
                tagIds:
                  type: array
                  items:
                    type: integer
                  example:
                    - 1
                    - 2
                channels:
                  type: array
                  items:
                    type: string
                  example:
                    - email
                    - chat
            supersetRole:
              type: string
              example: analyticsEditor
            inheritParentalSettings:
              type: boolean
              example: true
      example:
        -
          id: 1
          name: Admins
          parent: 0
          description: 'Handles administrative tasks'
          membersCount: 5
          settings:
            roleId: 1
            skills:
              tagIds: []
              channels: []
            supersetRole: analyticsEditor
            inheritParentalSettings: false
        -
          id: 2
          name: 'C-KD NEU'
          parent: 0
          description: null
          membersCount: 1
          settings:
            roleId: 2
            skills:
              tagIds:
                - 2
              channels:
                - email
                - phone
                - system
                - chat
            supersetRole: none
            inheritParentalSettings: false
    team:
      type: object
      properties:
        id:
          type: integer
          description: 'The unique identifier of the team'
          nullable: false
          example: 1
        name:
          type: string
          description: 'The name of the team'
          example: Support
          nullable: false
        parent:
          type: integer
          description: 'The parent id of the team'
          example: 0
        description:
          type: string
          description: 'The description of the team'
          example: 'Allgemeine Fragen'
          nullable: true
        settings:
          type: object
          properties:
            roleId:
              type: integer
              description: 'The role'
              example: 1
            skills:
              type: object
              properties:
                tagIds:
                  type: array
                  items:
                    type: integer
                  example:
                    - 1
                    - 2
                channels:
                  type: array
                  items:
                    type: string
                  example:
                    - email
                    - chat
            supersetRole:
              type: string
              example: analyticsReadAll
            inheritParentalSettings:
              type: boolean
              example: true
    partner:
      type: object
      required:
        - business
        - id
      properties:
        id:
          readOnly: true
          type: integer
          description: 'The unique identifier of the partner'
          nullable: false
          example: 1
        email:
          type: string
          description: 'The email of the partner'
          nullable: false
          example: mia.jones@gmail.com
        lastUsage:
          type: string
          readOnly: true
          format: date-time
          description: 'The last usage of the partner'
          nullable: true
          example: '2022-12-13 22:18:06'
        zip:
          type: string
          description: "The postal code of the partner's address"
          nullable: true
          example: '10409'
        city:
          type: string
          description: "The city of the partner's address"
          nullable: true
          example: Berlin
        phone:
          type: string
          description: "The partner's phone number"
          nullable: true
          example: '+4915324567843'
        tagIds:
          type: array
          items:
            type: integer
          description: 'A list of tag IDs associated with the partner'
          example: []
        address:
          type: string
          description: "The street and house number of the partner's address"
          nullable: true
          example: null
        company:
          type: string
          description: 'The name of the company the partner is associated with, if any'
          nullable: true
          example: null
        country:
          type: string
          description: 'The country of the partner'
          nullable: true
          example: Germany
        business:
          type: boolean
          description: 'Whether the partner is a business entity'
          example: false
        lastname:
          type: string
          description: "The partner's last name"
          nullable: true
          example: Jones
        firstname:
          type: string
          description: "The partner's first name"
          nullable: true
          example: Mia
        serviceId:
          type: string
          description: 'The service ID of the partner'
          nullable: true
          example: '5353543'
        link:
          type: string
          description: 'The link of the partner'
          nullable: true
          example: null
        salutation:
          type: string
          description: 'The salutation (e.g., Mr., Mrs.) of the partner'
          nullable: true
          example: null
        contractIds:
          type: array
          items:
            type: integer
          description: 'A list of contract IDs associated with the partner'
          example: []
        emailMatches:
          type: array
          items:
            type: string
          description: 'Possible email matches associated with the partner'
          nullable: true
          example: null
        description:
          type: string
          description: 'The description of the partner'
          nullable: true
          example: null
        additionalData:
          type: string
          description: 'Any additional data related to the partner'
          nullable: true
          example: null
        billingAddress:
          type: string
          description: "The partner's billing address"
          nullable: true
          example: null
        deliveryAddress:
          type: string
          description: "The partner's delivery address"
          nullable: true
          example: null
    ExecutionResponse:
      type: object
      properties:
        duration:
          type: integer
          description: 'Duration of the execution in seconds'
          example: 22
        exitCode:
          type: integer
          description: 'Exit code of the execution'
          example: 0
        output:
          type: object
          properties:
            success:
              type: boolean
              description: 'Indicates if the execution was successful'
              example: true
        stderr:
          type: string
          nullable: true
          description: 'Standard error output, if any'
          example: null
        dependenciesCreated:
          type: boolean
          description: 'Indicates if dependencies were created during execution'
          example: false
        localExecutionCommand:
          type: string
          nullable: true
          description: 'Command used for local execution, if any'
          example: null
    UserDefinedFunction:
      type: object
      properties:
        id:
          type: integer
          example: 1
        type:
          type: string
          enum:
            - tool
            - udf
        data:
          type: object
          properties:
            udfExecutor:
              type: object
              properties:
                id:
                  type: integer
                  example: 1
                code:
                  type: string
                  example: "<?php\n\n// Load enneo SDK. Input is made available through $in\nuse EnneoSDK\\Api;\nuse EnneoSDK\\ApiEnneo;\nrequire(getenv()['SDK']);\n\nfunction exampleApiCall(string $method, string $api, array|object|string|false $params = false, $attempt = 1): stdClass|array\n{\n    // load token\n    $token = ApiEnneo::executeUdf('fetchToken', [])->token;\n\n    // make request\n    $res = Api::call(\n        method: $method,\n        url: sprintf('urlExampleHere', $api),\n        headers: [\n            sprintf('Authorization: Bearer %s', $token),\n            'x-api-key: tokenExampleHere',\n            'Accept: application/json'\n        ],\n        params: $params\n    );\n\n    return $res;\n}\n\necho json_encode(exampleApiCall($in->method, $in->api, $in->params));"
                type:
                  type: string
                  example: sourceCode
                language:
                  type: string
                  example: php82
                packages:
                  type: string
                  example: ''
                parameters:
                  type: array
                  items:
                    type: object
                    description: 'Parameter definitions for the executor'
                  example: []
        name:
          type: string
          example: exampleApiCall
    TestAiAgent:
      type: object
      description: 'Represents test statistics for an AI agent. Shows the number of test cases that reference this agent and the total number of test runs performed for this agent.'
      properties:
        aiAgentId:
          type: string
          description: 'The unique identifier code of the AI agent. If null, it indicates statistics for all agents.'
          example: change_bank_data
          nullable: true
        aiAgentName:
          type: string
          description: 'The human-readable name of the AI agent being tested. If all AI agents are included, this will be "All".'
          example: 'Bankdaten anpassen'
        testCaseCount:
          type: integer
          description: 'The total number of test cases (ticket IDs) used in this test specification.'
          example: 2
          minimum: 0
        emailTestCasesCount:
          type: integer
          description: 'The number of email test cases for this AI agent.'
          example: 1
          minimum: 0
        chatTestCasesCount:
          type: integer
          description: 'The number of chat test cases for this AI agent.'
          example: 1
          minimum: 0
        voiceTestCasesCount:
          type: integer
          description: 'The number of voice test cases for this AI agent.'
          example: 0
          minimum: 0
        totalRuns:
          type: integer
          description: 'The total number of times this test specification has been executed.'
          example: 1
          minimum: 0
      required:
        - aiAgentName
        - testCaseCount
        - emailTestCasesCount
        - chatTestCasesCount
        - voiceTestCasesCount
        - totalRuns
      example:
        aiAgentId: change_bank_data
        aiAgentName: 'Bankdaten anpassen'
        testCaseCount: 2
        emailTestCasesCount: 1
        chatTestCasesCount: 1
        voiceTestCasesCount: 0
        totalRuns: 1
    AiTestRun:
      type: object
      description: 'Represents a test run for AI quality checks. A test run is a collection of tickets that are tested against an expected output.'
      properties:
        id:
          type: integer
          description: 'The ID of the test run'
        aiAgentId:
          type: string
          nullable: true
          description: 'Id code of the AI agent for this test run (for filtering test cases by agent). If null, all test cases will be tested using their reference agents.'
        aiAgentName:
          type: string
          nullable: true
          description: 'Corresponding name of the AI agent, pulled via FK from the ai_agent table'
        state:
          type: string
          enum:
            - processing
            - completed
          description: 'Current state of the test run'
        config:
          type: object
          nullable: true
          description: 'If specified, any additional test run parameters'
        createdBy:
          type: integer
          nullable: true
          description: 'ID of the user who created the test run'
        createdAt:
          type: string
          format: date-time
          description: 'Date when the test run was created'
        endDate:
          type: string
          format: date-time
          nullable: true
          description: 'Date when the test run was completed'
        ticketsScheduledForTesting:
          type: integer
          nullable: true
          description: 'Number of tickets scheduled for testing'
        ticketsTested:
          type: integer
          nullable: true
          description: 'Number of tickets tested'
        successfulTests:
          type: integer
          nullable: true
          description: 'Number of successful tests'
        description:
          type: string
          nullable: true
          description: 'Optional description of the test run'
    AiTestCase:
      type: object
      description: "Represents a test case for AI quality checks. A ticket that is assigned to AI agents with an expected result.\nDuring a specific test run, every test case is individually tested and the result is saved as a \"test ticket\".\nSo if we have 10 test cases and 5 test runs, we will have 10*5=50 test tickets.\nNote: aiAgentIds are for reference only. Each test case generates ONE test ticket per test run, regardless of how many agents are in aiAgentIds."
      properties:
        id:
          type: integer
          nullable: true
          description: 'The ID of the test case. Null if not yet saved.'
        ticketId:
          type: integer
          nullable: true
          description: 'The ID of the ticket that is being tested (null for chat/voice tests)'
        aiAgentIds:
          type: array
          items:
            type: integer
          description: 'Array of AI agent IDs for reference only (indicates which agents this test case is meant for). Does not affect the number of test tickets created.'
          example:
            - 1
            - 2
            - 3
        channel:
          type: string
          nullable: true
          description: 'The channel of the test case (email, chat, or phone)'
        summary:
          type: string
          nullable: true
          description: 'The summary of the ticket. Pulled from ticket.summary'
        description:
          type: string
          nullable: true
          description: 'A short description of the test case. Null if not yet set.'
        expectedResult:
          type: object
          nullable: true
          description: 'The expected result of the test case'
        modifiedBy:
          type: integer
          nullable: true
          description: 'The user ID of the user who last modified the test case'
    AiTestTicket:
      type: object
      description: 'Represents an individual test ticket that is tested regarding AI quality as part of a test run. Each test case generates one test ticket per test run.'
      properties:
        id:
          type: integer
          nullable: true
          description: 'The ID of the test ticket. Null if not yet saved.'
        testRunId:
          type: integer
          description: 'The ID of the test run'
        testCaseId:
          type: integer
          description: 'The ID of the test case'
        ticketId:
          type: integer
          nullable: true
          description: 'The ID of the ticket (null for chat/voice tests)'
        aiAgentId:
          type: string
          nullable: true
          description: "The ID code of the AI agent for reference (from test case's aiAgentIds). Can be null if no agents are specified."
        state:
          type: string
          enum:
            - scheduled
            - processing
            - completed
            - error
          description: 'The scheduling state of the ticket'
        config:
          type: object
          nullable: true
          description: 'If specified, any additional test run parameters'
        expectedResult:
          $ref: '#/components/schemas/AiTestTicketResult'
          nullable: true
          description: 'The expected result of the test case'
        actualResult:
          $ref: '#/components/schemas/AiTestTicketResult'
          nullable: true
          description: 'The actual result of the test case'
        rawResult:
          type: object
          nullable: true
          description: 'The raw result of the test case'
        outcomeShort:
          type: string
          enum:
            - pass
            - fail
            - unknown
          nullable: true
          description: 'The short outcome of the test case (pass, fail, or unknown). Null if not yet processed.'
        outcome:
          type: object
          nullable: true
          description: 'The detailed outcome of the test case, with a property per test case. See also the AiTestTicketResult for further reference. Null if not yet processed.'
    AiTestTicketResult:
      type: object
      description: 'The updated expected result for the test ticket. You can either specify all test conditions, or only some. In this case only the provided test conditions will be provided'
      properties:
        contractId:
          type: string
          example: '123456'
        inputParameters:
          type: object
          example:
            change_meter_reading:
              _action: null
              requestedDeposit: 35
        intentDetection:
          type: array
          items:
            type: string
          example:
            - change_meter_reading
            - change_payment_method
        outcomeActions:
          type: object
          example:
            change_meter_reading:
              - 'Update ERP System'
              - 'Inform Customer change is not possible'
        outcomeNotifications:
          type: object
          example:
            change_meter_reading:
              - 'Meter reading is plausible'
        outcomeText:
          type: object
          example:
            change_meter_reading: null
        outcomeType:
          type: object
          example:
            change_meter_reading: interaction
        sentiment:
          type: string
          example: disappointed
        tags:
          type: array
          items:
            type: string
          example:
            - sales
            - marketing
        aiSupportLevel:
          type: string
          example: human
        sources:
          type: object
          description: 'The sources used by Cortex to generate the response'
          example:
            change_meter_reading:
              -
                type: ticket
                id: '123'
                summary: 'Similar case'
              -
                type: faq
                id: '456'
                title: 'How to change meter reading'
        error:
          type: string
          nullable: true
          example: null
    Permission:
      type: object
      properties:
        name:
          type: string
          description: 'The name of the permission'
          example: createAiAgent
        label:
          type: string
          description: 'The label of the permission'
          example: 'Create AI agent'
        value:
          type: boolean
          description: 'Whether the permission is granted'
          example: true
    PermissionGroup:
      type: object
      properties:
        label:
          type: string
          description: 'The label of the permission group'
          example: 'AI agent permisions'
        description:
          type: string
          description: 'The description of the permission group'
        permissions:
          type: array
          items:
            $ref: '#/components/schemas/Permission'
    Role:
      type: object
      properties:
        name:
          type: string
          description: 'The name of the role'
          example: Admin
        description:
          type: string
          description: 'The description of the role'
          example: 'Handles administrative tasks'
        baseRoleId:
          type: integer
          description: 'The ID of the base role'
          example: 1
        permissions:
          type: array
          description: 'Permissions for this role. When reading: returns grouped permissions with labels. When writing: accepts array of permission names'
          items:
            oneOf:
              -
                $ref: '#/components/schemas/PermissionGroup'
              -
                type: string
                example: createAiAgent
    SearchResult:
      type: object
      description: 'A search result item containing information about a matching resource'
      properties:
        type:
          type: string
          description: "The type of search result (e.g., 'setting', 'category', etc.)"
          example: setting
        name:
          type: string
          description: 'The display name or title of the search result'
          example: 'Last Agent Routing'
        description:
          type: string
          description: 'A description of the search result'
          example: 'When enabled, a ticket will be assigned to the agent after handling. Customer responses to this ticket will then be routed to the original agent. The advantage is more effective handling, as there is no need to familiarize oneself with the case and feedback is received directly related to the inquiry. In case of absence, such tickets must be routed manually.'
        url:
          type: string
          description: 'The URL where this result can be found'
          example: '/settings/category/routing#lastAgentRouting'
        relevance:
          type: number
          format: float
          minimum: 0
          maximum: 1
          description: 'The relevance score of this result (0.0 = 0% to 1.0 = 100%). 1.0 means exact match and 0.0 means no match.'
          example: 1
    Transcript:
      type: object
      description: 'A single entry in a conversation transcript'
      required:
        - speaker
        - message
      properties:
        speaker:
          type: string
          description: "Who spoke this message. Determines conversation direction and type:\n- 'customer': Customer/caller message (direction=in, type=text)\n- 'agent': Bot/assistant message (direction=out, type=text)\n- 'agent_request': AI agent tool call request (direction=internal, type=agent_request)\n- 'agent_response': AI agent tool call response (direction=internal, type=agent_response)\n"
          enum:
            - customer
            - agent
            - agent_request
            - agent_response
          example: customer
        message:
          type: string
          description: 'The actual message that was spoken'
          example: 'Hello, my name is AI Assistant. How can I help you today?'
        timestamp:
          type: string
          format: date-time
          description: 'When this message was spoken'
          nullable: true
          example: '2024-01-23T14:32:11Z'
      example:
        speaker: agent
        message: 'Hello, my name is AI Assistant. How can I help you today?'
        timestamp: '2024-01-23T14:32:11Z'
    RoutingStatus:
      type: string
      description: 'The routing status for queues (calls, chats, etc)'
      enum:
        - idle
        - interacting
        - beingConnected
        - notResponding
        - unavailable
        - offline
        - acw
    EventActivity:
      type: object
      properties:
        id:
          type: integer
          description: 'Event ID'
        status:
          type: string
          description: 'Event status'
          enum:
            - open
            - closed
            - error
            - deleted
            - processing
        user:
          type: object
          description: 'User who created the event'
          properties:
            id:
              type: integer
            firstName:
              type: string
            lastName:
              type: string
            email:
              type: string
            phone:
              type: string
        createdAt:
          type: string
          format: date-time
          description: 'Creation timestamp'
        activity:
          type: string
          description: 'Localized description of the event'
        details:
          type: array
          description: 'Additional details for this event'
          items:
            type: object
            properties:
              label:
                type: string
              value:
                type: string
              type:
                type: string
              url:
                type: string
        tabs:
          type: array
          description: 'Tabs to show in the activity log'
          items:
            type: object
            properties:
              key:
                type: string
              label:
                type: string
        eventTraces:
          type: array
          description: 'Additional traces associated with this event'
          items:
            type: object
    QualityScorecard:
      type: object
      description: 'Quality scorecard for assessing agent performance'
      properties:
        id:
          type: integer
          description: 'Scorecard ID'
          example: 1
        baseId:
          type: integer
          description: 'Base ID for versioning (stable across revisions)'
          example: 1
        revision:
          type: integer
          description: 'Revision number'
          example: 1
        name:
          type: string
          description: 'Scorecard name'
          example: 'Junior agent standard review'
        state:
          type: string
          enum:
            - draft
            - active
            - retired
            - deleted
          description: 'Scorecard state'
          example: active
        categoryCount:
          type: integer
          description: 'Number of categories (summary format only)'
          example: 3
        categories:
          type: array
          description: 'Scorecard categories with criteria (full format only)'
          items:
            type: object
            properties:
              categoryId:
                type: integer
              label:
                type: string
              order:
                type: integer
              criteria:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                    label:
                      type: string
                    description:
                      type: string
                    maxPoints:
                      type: integer
                    scoringType:
                      type: string
                      enum:
                        - metNotMet
                        - numericScale
                    autoGenerateByAi:
                      type: boolean
                    assessmentPrompt:
                      type: string
                    makeOrBreakForCategory:
                      type: boolean
                    makeOrBreakForAssessment:
                      type: boolean
        assignment:
          type: object
          description: 'Assignment rules for scorecard applicability'
          properties:
            ticketTags:
              type: array
              items:
                type: integer
            teams:
              type: array
              items:
                type: integer
            channels:
              type: array
              items:
                type: string
            responseContexts:
              type: array
              description: "Work-session contexts to match: 'afterCustomerMessage' (customer contacted first or worklog linked to an inbound message), 'withoutCustomerMessage' (proactive outbound action with no linked customer message). Empty array (or field absent) means no filter. Existing scorecards default to ['afterCustomerMessage'] via data migration.\n"
              items:
                type: string
                enum:
                  - afterCustomerMessage
                  - withoutCustomerMessage
        liveCoach:
          type: object
          description: "Live Quality Coach configuration. When `enabled` is true and this scorecard\nis applicable to the ticket, the real-time draft check\n(`POST /ticket/{id}/quality/check`) scores against it and the server-side\nsend gate blocks replies whose score is below `threshold` (unless the user\nhas `qualityBypassSendGate`).\n"
          properties:
            enabled:
              type: boolean
              description: 'Use this scorecard as Live Quality Coach'
              example: false
            threshold:
              type: integer
              minimum: 0
              maximum: 100
              description: 'Minimum percentage a draft must reach to pass the send gate'
              example: 85
        createdBy:
          type: integer
          description: 'User ID who created the scorecard'
        modifiedBy:
          type: integer
          description: 'User ID who last modified the scorecard'
        createdAt:
          type: string
          format: date-time
        modifiedAt:
          type: string
          format: date-time
        canEdit:
          type: boolean
          description: 'Whether current user can edit this scorecard'
    QualityAssessment:
      type: object
      description: 'Quality assessment of agent performance on a specific work session'
      properties:
        id:
          type: integer
          description: 'Assessment ID'
          example: 1
        scorecardId:
          type: integer
          description: 'ID of the scorecard used'
          example: 1
        scorecardBaseId:
          type: integer
          description: 'Base ID of the scorecard used'
          example: 1
        state:
          type: string
          enum:
            - unprocessed
            - aiInProgress
            - aiReady
            - reviewOngoing
            - reviewedBySupervisor
            - discussedWithAssessee
            - error
            - deleted
          description: 'Assessment workflow state'
          example: aiReady
        assessedUser:
          type: integer
          description: 'ID of the assessed user (agent)'
          example: 42
        assessedByUser:
          type: integer
          nullable: true
          description: 'ID of the supervisor who assessed'
        ticketId:
          type: integer
          description: 'Related ticket ID'
          example: 12345
        worklogId:
          type: integer
          nullable: true
          description: 'Related worklog ID'
        aiSummary:
          type: string
          description: 'AI-generated summary (summary format)'
          example: 'Good job overall, scored 85%'
        totals:
          type: object
          description: 'Score totals'
          properties:
            scoredPoints:
              type: integer
              description: 'Points achieved'
              example: 24
            usedMaxPoints:
              type: integer
              description: 'Maximum points for scored criteria'
              example: 32
            totalMaxPoints:
              type: integer
              description: 'Maximum points for all criteria'
              example: 47
            percentage:
              type: number
              description: 'Percentage score'
              example: 75.0
        data:
          type: object
          description: 'Full assessment data (full format only)'
          properties:
            aiSummary:
              type: string
            general:
              type: object
              description: 'Work session metadata'
              additionalProperties: true
            aiUsage:
              type: object
              description: 'AI automation metrics'
              additionalProperties: true
            time:
              type: object
              description: 'Time tracking information'
              additionalProperties: true
            customerExperience:
              type: object
              description: 'Customer satisfaction metrics'
              additionalProperties: true
            categories:
              type: array
              description: 'Scored categories and criteria'
              items:
                type: object
                additionalProperties: true
            totals:
              type: object
              additionalProperties: true
            supervisorAssessment:
              type: string
              nullable: true
            assessmentDate:
              type: string
              format: date-time
              nullable: true
            discussionDate:
              type: string
              format: date-time
              nullable: true
        createdAt:
          type: string
          format: date-time
        modifiedAt:
          type: string
          format: date-time
        canEdit:
          type: boolean
          description: 'Whether current user can edit this assessment'
    FilesConnector:
      type: object
      description: 'Files connector root object. Manages drag-and-drop document upload into the Knowledge Base.'
      properties:
        id:
          type: integer
          description: 'Connector structure ID'
          example: 10
        name:
          type: string
          description: 'Always "Files"'
          example: Files
        type:
          type: string
          description: 'Always "file"'
          example: file
        folderCount:
          type: integer
          description: 'Number of folders under this connector'
          example: 5
        fileCount:
          type: integer
          description: 'Number of active file articles under this connector'
          example: 23
        data:
          type: object
          properties:
            allowedExtensions:
              type: array
              items:
                type: string
              example:
                - pdf
                - docx
                - txt
            maxFileSizeMb:
              type: integer
              example: 50
        folders:
          type: array
          items:
            $ref: '#/components/schemas/FilesConnectorFolder'
        files:
          type: array
          items:
            $ref: '#/components/schemas/FilesConnectorFile'
    FilesConnectorFolder:
      type: object
      properties:
        id:
          type: integer
          example: 11
        parent:
          type: integer
          description: 'Parent structure ID (connector ID if top-level)'
          example: 10
        name:
          type: string
          example: Sales
    FilesConnectorFile:
      type: object
      properties:
        id:
          type: integer
          example: 42
        parent:
          type: integer
          description: 'Parent structure ID (folder or connector)'
          example: 11
        name:
          type: string
          description: 'Bare filename (display name)'
          example: Pricing-2026.pdf
        title:
          type: string
          description: 'Same as name for file articles'
          example: Pricing-2026.pdf
        source:
          type: string
          description: 'Full path-qualified label, e.g. "Sales/Pricing-2026.pdf"'
          example: Sales/Pricing-2026.pdf
        status:
          type: string
          enum:
            - active
            - archived
            - deleted
          example: active
        originalFileName:
          type: string
          description: 'Preserved original uploaded filename (stable across renames)'
          example: Pricing-2026.pdf
        originalMimeType:
          type: string
          example: application/pdf
        originalSize:
          type: integer
          description: 'Original file size in bytes'
          example: 2154288
        originalUrl:
          type: string
          description: 'Internal URL to the stored binary'
          example: /api/mind/storage/knowledgeSources/42-abc12345/Pricing-2026.pdf
        hasManualMarkdownEdits:
          type: boolean
          description: "True when the generated Markdown in `text` has been manually edited via\n`PATCH /knowledgeSource/{id}`. Reset to `false` on initial upload, replace, and re-index.\nConsumed by the FE to warn users before a re-upload would overwrite their edits.\n"
          default: false
          example: false
        createdAt:
          type: string
          format: date-time
        modifiedAt:
          type: string
          format: date-time
          nullable: true
    FileUploadResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        id:
          type: integer
          description: 'ID of the created knowledge_sources row'
          example: 42
    BatchFileUploadResponse:
      type: object
      properties:
        successful:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
                description: 'ID of the created knowledge_sources row'
                example: 42
              name:
                type: string
                description: 'Original filename'
                example: invoice.pdf
        failed:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
                description: 'Original filename'
                example: corrupt.pdf
              error:
                type: string
                description: 'Error message'
                example: "Unsupported file type for 'corrupt.pptx'"
    App:
      type: object
      properties:
        id:
          type: integer
          nullable: true
          description: 'Internal numeric row id of the active revision. `null` for previews/imports.'
        appId:
          type: string
          description: 'Stable app identifier (nano-id). Use this in routes like `/app/{appId}`.'
          example: k4FqL2Hm
        revision:
          type: integer
          description: 'Monotonic revision counter. Incremented on every update.'
          example: 7
        name:
          type: string
          example: 'Customer health check'
        slug:
          type: string
          description: 'URL-safe slug derived from `name`.'
          example: customer-health-check
        description:
          type: string
          nullable: true
        vendor:
          type: string
          nullable: true
          description: 'Author/team. Stored as plain text.'
        availability:
          type: string
          nullable: true
          description: 'Visibility/availability flag (see `Mind/Models/App.php` for the allowed values).'
        appearance:
          type: object
          nullable: true
          description: 'UI-level config (icon, color, layout hints).'
        data:
          type: object
          description: "The app body. Contains the HTML template, the executor source (Python/Node/PHP) and any\nstatic parameters. The exact shape is owned by the app author.\n"
        isActive:
          type: boolean
          example: true
        deletedAt:
          type: string
          format: date-time
          nullable: true
        createdBy:
          type: integer
          nullable: true
          description: 'Profile id of the creating user.'
        createdAt:
          type: string
          format: date-time
  securitySchemes:
    cookieAuth:
      type: apiKey
      in: cookie
      name: connect.sid
      description: 'Cookie-based authentication'
      x-scopes:
        api: 'Full access to the API'
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'JWT-based authentication'
      x-scopes:
        api: 'Full access to the API'
