openapi: 3.0.3
info:
  title: Mergly API
  description: |
    API RESTful per Mergly - Sistema di gestione proposte di call/meeting.

    ## Autenticazione

    L'API utilizza OAuth2 con grant type "Resource Owner Password Credentials".

    ### Ottenere un token
    ```bash
    curl -X POST /api/auth/token \
      -H "Content-Type: application/json" \
      -d '{"grant_type": "password", "username": "user", "password": "pass"}'
    ```

    ### Usare il token
    ```bash
    curl -X GET /api/events \
      -H "Authorization: Bearer {access_token}"
    ```

    ### Rinnovare il token
    ```bash
    curl -X POST /api/auth/refresh \
      -H "Content-Type: application/json" \
      -d '{"grant_type": "refresh_token", "refresh_token": "{refresh_token}"}'
    ```

  version: 1.0.0
  contact:
    name: Mergly Support
  license:
    name: MIT

servers:
  - url: /api
    description: Server locale

tags:
  - name: Auth
    description: Autenticazione OAuth2
  - name: Events
    description: Gestione Events/Meeting
  - name: Slots
    description: Gestione slot temporali
  - name: Invitees
    description: Gestione invitati
  - name: Invites
    description: API pubbliche per invitati (no auth)
  - name: Shares
    description: Condivisione call
  - name: Users
    description: Gestione utenti (superadmin)
  - name: Rubrica
    description: Rubrica contatti

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Token OAuth2 Bearer

  schemas:
    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          example: "error_code"
        message:
          type: string
          example: "Descrizione errore"
        details:
          type: object

    Success:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        data:
          type: object

    Pagination:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        pages:
          type: integer
        has_next:
          type: boolean
        has_prev:
          type: boolean

    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
        refresh_token:
          type: string
        token_type:
          type: string
          example: "Bearer"
        expires_in:
          type: integer
          example: 3600
        expires_at:
          type: string
          format: date-time
        scope:
          type: string
        user:
          $ref: '#/components/schemas/UserBasic'

    UserBasic:
      type: object
      properties:
        id:
          type: integer
        username:
          type: string
        email:
          type: string
        role:
          type: string
          enum: [superadmin, operatore]

    User:
      type: object
      properties:
        id:
          type: integer
        username:
          type: string
        email:
          type: string
        role:
          type: string
          enum: [superadmin, operatore]
        is_active:
          type: boolean
        ldap_enabled:
          type: boolean
        ldap_username:
          type: string
        last_login_at:
          type: string
          format: date-time
        last_login_method:
          type: string
          enum: [local, ldap]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Call:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        description:
          type: string
        status:
          type: string
          enum: [draft, active, closed, cancelled]
        token:
          type: string
        created_by:
          type: integer
        creator_name:
          type: string
        total_invitees:
          type: integer
        responded_count:
          type: integer
        confirmed_slot:
          type: integer
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    CallDetail:
      allOf:
        - $ref: '#/components/schemas/Call'
        - type: object
          properties:
            slots:
              type: array
              items:
                $ref: '#/components/schemas/Slot'
            invitees:
              type: array
              items:
                $ref: '#/components/schemas/Invitee'

    Slot:
      type: object
      properties:
        id:
          type: integer
        date:
          type: string
          format: date
        start_time:
          type: string
          example: "09:00"
        end_time:
          type: string
          example: "10:00"
        available_count:
          type: integer

    Invitee:
      type: object
      properties:
        id:
          type: integer
        email:
          type: string
          format: email
        token:
          type: string
        invite_sent_at:
          type: string
          format: date-time
          nullable: true
        responded_at:
          type: string
          format: date-time
          nullable: true
        respondent_name:
          type: string
          nullable: true
        has_responded:
          type: boolean

    Share:
      type: object
      properties:
        user_id:
          type: integer
        username:
          type: string
        email:
          type: string
        permission:
          type: string
          enum: [view, edit]
        shared_by:
          type: integer
        created_at:
          type: string
          format: date-time

    Contact:
      type: object
      properties:
        nome:
          type: string
        cognome:
          type: string
        email:
          type: string
          format: email
        label:
          type: string

paths:
  # ==================== AUTH ====================
  /auth/token:
    post:
      tags: [Auth]
      summary: Ottieni access token (login)
      description: |
        Endpoint OAuth2 per ottenere un access token.
        Grant type supportato: `password`
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [grant_type, username, password]
              properties:
                grant_type:
                  type: string
                  enum: [password]
                username:
                  type: string
                password:
                  type: string
                scope:
                  type: string
                  default: "*"
                client_id:
                  type: string
      responses:
        '200':
          description: Token generato
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/TokenResponse'
        '401':
          description: Credenziali non valide
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /auth/refresh:
    post:
      tags: [Auth]
      summary: Rinnova access token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [grant_type, refresh_token]
              properties:
                grant_type:
                  type: string
                  enum: [refresh_token]
                refresh_token:
                  type: string
      responses:
        '200':
          description: Token rinnovato
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/TokenResponse'
        '401':
          description: Refresh token non valido
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /auth/revoke:
    post:
      tags: [Auth]
      summary: Revoca token (logout)
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Token revocato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /auth/me:
    get:
      tags: [Auth]
      summary: Profilo utente autenticato
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Profilo utente
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/User'

  /auth/password:
    put:
      tags: [Auth]
      summary: Cambia password
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [current_password, new_password, confirm_password]
              properties:
                current_password:
                  type: string
                new_password:
                  type: string
                  minLength: 8
                confirm_password:
                  type: string
      responses:
        '200':
          description: Password aggiornata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  # ==================== EVENTS ====================
  /events:
    get:
      tags: [Events]
      summary: Lista call
      security:
        - bearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: search
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
            enum: [draft, active, closed, cancelled]
      responses:
        '200':
          description: Lista call
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Call'
                      pagination:
                        $ref: '#/components/schemas/Pagination'

    post:
      tags: [Events]
      summary: Crea nuova call
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title]
              properties:
                title:
                  type: string
                  minLength: 3
                description:
                  type: string
                status:
                  type: string
                  enum: [draft, active]
                  default: draft
                slots:
                  type: array
                  items:
                    type: object
                    properties:
                      date:
                        type: string
                        format: date
                      start_time:
                        type: string
                      end_time:
                        type: string
                invitees:
                  type: array
                  items:
                    type: string
                    format: email
                send_invites:
                  type: boolean
                  default: false
      responses:
        '201':
          description: Call creata
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/CallDetail'

  /events/{id}:
    get:
      tags: [Events]
      summary: Dettaglio call
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Dettaglio call
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/CallDetail'
        '404':
          description: Call non trovata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags: [Events]
      summary: Aggiorna call
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                description:
                  type: string
                status:
                  type: string
                  enum: [draft, active, closed, cancelled]
      responses:
        '200':
          description: Call aggiornata
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/CallDetail'

    delete:
      tags: [Events]
      summary: Elimina call
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Call eliminata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{id}/status:
    patch:
      tags: [Events]
      summary: Cambia stato call
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [status]
              properties:
                status:
                  type: string
                  enum: [draft, active, closed, cancelled]
      responses:
        '200':
          description: Stato aggiornato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{id}/matrix:
    get:
      tags: [Events]
      summary: Matrice disponibilita
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Matrice disponibilita
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{id}/best-slot:
    get:
      tags: [Events]
      summary: Slot con piu disponibilita
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Slot migliore
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{id}/confirm-slot/{slotId}:
    post:
      tags: [Events]
      summary: Conferma slot definitivo
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: slotId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Slot confermato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{id}/send-invites:
    post:
      tags: [Events]
      summary: Invia email inviti
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Inviti inviati
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{id}/export/csv:
    get:
      tags: [Events]
      summary: Esporta matrice in CSV
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: File CSV
          content:
            text/csv:
              schema:
                type: string

  /events/{id}/export/ics:
    get:
      tags: [Events]
      summary: Esporta evento ICS
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: File ICS
          content:
            text/calendar:
              schema:
                type: string

  # ==================== SLOTS ====================
  /events/{callId}/slots:
    get:
      tags: [Slots]
      summary: Lista slot di una call
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Lista slot
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Slot'

    post:
      tags: [Slots]
      summary: Aggiungi slot
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [date, start_time, end_time]
              properties:
                date:
                  type: string
                  format: date
                start_time:
                  type: string
                  example: "09:00"
                end_time:
                  type: string
                  example: "10:00"
      responses:
        '201':
          description: Slot creato
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Slot'

  /events/{callId}/slots/{id}:
    put:
      tags: [Slots]
      summary: Modifica slot
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                date:
                  type: string
                  format: date
                start_time:
                  type: string
                end_time:
                  type: string
      responses:
        '200':
          description: Slot aggiornato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

    delete:
      tags: [Slots]
      summary: Elimina slot
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Slot eliminato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  # ==================== INVITEES ====================
  /events/{callId}/invitees:
    get:
      tags: [Invitees]
      summary: Lista invitati
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Lista invitati
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Invitee'

    post:
      tags: [Invitees]
      summary: Aggiungi invitati
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                emails:
                  type: array
                  items:
                    type: string
                    format: email
                send_invite:
                  type: boolean
                  default: false
      responses:
        '201':
          description: Invitati aggiunti
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{callId}/invitees/{id}:
    delete:
      tags: [Invitees]
      summary: Rimuovi invitato
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Invitato rimosso
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{callId}/invitees/{id}/resend:
    post:
      tags: [Invitees]
      summary: Reinvia invito email
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Invito reinviato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  # ==================== INVITES (Pubbliche) ====================
  /invites/{token}:
    get:
      tags: [Invites]
      summary: Dati invito (pubblico)
      description: Endpoint pubblico - non richiede autenticazione
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Dati invito
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'
        '404':
          description: Token non valido
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /invites/{token}/submit:
    post:
      tags: [Invites]
      summary: Invia disponibilita (pubblico)
      description: Endpoint pubblico - non richiede autenticazione
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  description: Nome del rispondente
                slots:
                  type: array
                  items:
                    type: integer
                  description: Array di slot_id disponibili
      responses:
        '200':
          description: Disponibilita salvate
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /invites/{token}/verify:
    get:
      tags: [Invites]
      summary: Verifica validita token (pubblico)
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Risultato verifica
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  # ==================== SHARES ====================
  /events/{callId}/shares:
    get:
      tags: [Shares]
      summary: Lista condivisioni
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Lista condivisioni
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Share'

    post:
      tags: [Shares]
      summary: Condividi call
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [user_id]
              properties:
                user_id:
                  type: integer
                permission:
                  type: string
                  enum: [view, edit]
                  default: view
      responses:
        '201':
          description: Call condivisa
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /events/{callId}/shares/{userId}:
    put:
      tags: [Shares]
      summary: Modifica permessi condivisione
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
        - name: userId
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [permission]
              properties:
                permission:
                  type: string
                  enum: [view, edit]
      responses:
        '200':
          description: Permessi aggiornati
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

    delete:
      tags: [Shares]
      summary: Rimuovi condivisione
      security:
        - bearerAuth: []
      parameters:
        - name: callId
          in: path
          required: true
          schema:
            type: integer
        - name: userId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Condivisione rimossa
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  # ==================== USERS ====================
  /users:
    get:
      tags: [Users]
      summary: Lista utenti (superadmin)
      security:
        - bearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
        - name: limit
          in: query
          schema:
            type: integer
        - name: search
          in: query
          schema:
            type: string
        - name: role
          in: query
          schema:
            type: string
            enum: [superadmin, operatore]
      responses:
        '200':
          description: Lista utenti
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/User'
                      pagination:
                        $ref: '#/components/schemas/Pagination'

    post:
      tags: [Users]
      summary: Crea utente (superadmin)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, email]
              properties:
                username:
                  type: string
                  minLength: 3
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 8
                  description: Se omesso, genera password casuale
                role:
                  type: string
                  enum: [superadmin, operatore]
                  default: operatore
                is_active:
                  type: boolean
                  default: true
                ldap_enabled:
                  type: boolean
                  default: false
                ldap_username:
                  type: string
      responses:
        '201':
          description: Utente creato
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - $ref: '#/components/schemas/User'
                          - type: object
                            properties:
                              generated_password:
                                type: string

  /users/{id}:
    get:
      tags: [Users]
      summary: Dettaglio utente (superadmin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Dettaglio utente
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/User'

    put:
      tags: [Users]
      summary: Modifica utente (superadmin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                email:
                  type: string
                  format: email
                role:
                  type: string
                  enum: [superadmin, operatore]
                is_active:
                  type: boolean
                ldap_enabled:
                  type: boolean
                ldap_username:
                  type: string
      responses:
        '200':
          description: Utente aggiornato
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/User'

    delete:
      tags: [Users]
      summary: Elimina utente (superadmin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Utente eliminato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /users/{id}/reset-password:
    post:
      tags: [Users]
      summary: Reset password utente (superadmin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                new_password:
                  type: string
                  minLength: 8
                  description: Se omesso, genera password casuale
      responses:
        '200':
          description: Password resettata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /users/{id}/toggle-active:
    patch:
      tags: [Users]
      summary: Attiva/disattiva utente (superadmin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Stato aggiornato
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /users/operators:
    get:
      tags: [Users]
      summary: Lista operatori attivi
      security:
        - bearerAuth: []
      parameters:
        - name: exclude
          in: query
          schema:
            type: integer
          description: ID utente da escludere
      responses:
        '200':
          description: Lista operatori
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  # ==================== RUBRICA ====================
  /rubrica/search:
    get:
      tags: [Rubrica]
      summary: Ricerca contatti
      security:
        - bearerAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 2
        - name: limit
          in: query
          schema:
            type: integer
            default: 10
            maximum: 50
      responses:
        '200':
          description: Risultati ricerca
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Contact'

  /rubrica/status:
    get:
      tags: [Rubrica]
      summary: Stato rubrica
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Stato rubrica
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Success'

  /rubrica/list:
    get:
      tags: [Rubrica]
      summary: Lista completa contatti
      security:
        - bearerAuth: []
      parameters:
        - name: page
          in: query
          schema:
            type: integer
        - name: limit
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Lista contatti
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Contact'
                      pagination:
                        $ref: '#/components/schemas/Pagination'
