openapi: 3.0.3
info:
  title: Cliengo Public API
  version: "1.0"
  description: |
    API pública unificada de Cliengo. Punto de entrada único para gestionar contactos,
    conversaciones, canales, chatbots, planes, usuarios y plantillas de WhatsApp.

    ---

    ## Obtener tu Token

    1. Ingresá a [dash.cliengo.com/integrations/connect_api](https://dash.cliengo.com/integrations/connect_api)
    2. Generá un nuevo **Token** seleccionando los permisos que necesites
    3. Copiá tu token (formato `sk_test_...` en stage, `sk_live_...` en producción)

    ---

    ## Autenticación

    Todas las requests requieren el header `Authorization: Bearer <token>`.

    ```bash
    curl -H "Authorization: Bearer TU_TOKEN" https://connect.cliengo.com/v1/plans
    ```

    Si recibís un JSON con planes, tu token funciona.

    ---

    ## Rate Limiting

    **1000 requests por minuto** por token. Cada respuesta incluye:

    | Header | Significado |
    |--------|-------------|
    | `X-RateLimit-Limit` | Máximo por ventana |
    | `X-RateLimit-Remaining` | Restantes |
    | `X-RateLimit-Reset` | Unix timestamp del reset |

    Si excedés el límite → HTTP **429** con header `Retry-After`.

    ---

    ## Errores comunes

    | Código | Causa | Solución |
    |--------|-------|----------|
    | 401 | Falta header de auth o token inválido | Verificá tu `Authorization: Bearer` header |
    | 404 | Endpoint no existe | Revisá la URL |
    | 429 | Rate limit excedido | Esperá `Retry-After` segundos |

    ---

    ## Trazabilidad

    Cada respuesta incluye `X-Request-ID` (UUID). Podés enviar tu propio valor
    en el request para correlacionar logs.

  contact:
    name: Cliengo Engineering
    email: hola@cliengo.com
    url: https://cliengo.com
  license:
    name: Proprietary

servers:
  - url: https://connect.cliengo.com
    description: Producción

security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Token de autenticación (formato `sk_live_...`).
        Obtener token en [dash.cliengo.com/integrations/connect_api](https://dash.cliengo.com/integrations/connect_api).

  schemas:
    Contact:
      type: object
      properties:
        id:
          type: string
          example: "60a1b2c3d4e5f6a7b8c9d0e1"
        name:
          type: string
          example: "Juan Pérez"
        email:
          type: string
          format: email
          example: "juan@example.com"
        phone:
          type: string
          example: "+5491155551234"
        internationalPhoneNumber:
          type: string
          example: "5491155551234"
        channelId:
          type: string
          description: ID del canal donde se originó el contacto
        companyId:
          type: string
        conversationId:
          type: string
        status:
          type: string
          enum: [new, active, client, long_term]
        subStatus:
          type: string
        message:
          type: string
          description: Último mensaje del contacto
        assignedTo:
          type: string
          nullable: true
          description: ID del agente asignado
        rating:
          type: integer
        entryMethod:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    Pagination:
      type: object
      properties:
        page:
          type: integer
          example: 1
        limit:
          type: integer
          example: 20
        total:
          type: integer
          example: 42
        totalPages:
          type: integer
          example: 3
        hasNext:
          type: boolean
        hasPrev:
          type: boolean

    ContactSearchResponse:
      type: object
      properties:
        pagination:
          $ref: "#/components/schemas/Pagination"
        data:
          type: array
          items:
            $ref: "#/components/schemas/Contact"

    Conversation:
      type: object
      properties:
        id:
          type: string
          readOnly: true
          example: "626e9cc66ac98128162b2bbc"
        channelId:
          type: string
          example: "5f08e255c9a881002afc06ec"
        companyId:
          type: string
          readOnly: true
          example: "5c7414a4e4b06f6eb79408f5"
        channel:
          $ref: "#/components/schemas/Channel"
        status:
          type: string
          enum: [ACTIVE, CLOSED]
        lastMessage:
          type: string
          readOnly: true
          example: "será un placer asistirte, cuál es tu nombre?"
        lastMessageAt:
          type: string
          format: date-time
          readOnly: true
        visitorName:
          type: string
          example: "Robert"
        visitorEmail:
          type: string
          example: "robert@example.com"
        visitorPhone:
          type: string
          example: "584122233456"
        participants:
          type: array
          readOnly: true
          items:
            $ref: "#/components/schemas/Participant"
        tags:
          type: array
          items:
            type: string
        closed:
          type: boolean
        createdAt:
          type: string
          format: date-time
          readOnly: true

    Channel:
      type: string
      enum: [FACEBOOK, WEB, WHATSAPP, INSTAGRAM]

    Participant:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
          enum: [robot, visitor, user]

    Message:
      type: object
      properties:
        id:
          type: string
          readOnly: true
        user:
          type: string
          readOnly: true
        text:
          type: string
          example: "Hola, necesito ayuda"
        createdAt:
          type: string
          format: date-time
          readOnly: true
        attachments:
          type: array
          items:
            type: object
            properties:
              title:
                type: string
              text:
                type: string
              image_url:
                type: string
                format: uri
        edited:
          type: boolean
          readOnly: true

    ConversationListResponse:
      type: object
      properties:
        pagination:
          $ref: "#/components/schemas/Pagination"
        data:
          type: array
          items:
            $ref: "#/components/schemas/Conversation"

    ChannelSite:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        url:
          type: string
          format: uri
        company:
          type: string
        enabled:
          type: boolean
        integrations:
          type: object

    Plan:
      type: object
      properties:
        id:
          type: string
          example: "CLIENGO_STARTER"
        name:
          type: string
          example: "Leads Cliengo Starter"
        description:
          type: string
        tier:
          type: string
        annualPlan:
          type: boolean
        leadLimit:
          type: integer
        conversationLimit:
          type: integer
        channelLimit:
          type: integer
        userLimit:
          type: integer
        priceUSD:
          type: number
        priceARS:
          type: number

    Account:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        email:
          type: string
        plan:
          type: string
        features:
          type: object
        statuses:
          type: array
          items:
            type: string

    User:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        email:
          type: string
          format: email
        role:
          type: string
        active:
          type: boolean

    Chatbot:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        channelId:
          type: string
        enabled:
          type: boolean

    Trigger:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
        enabled:
          type: boolean
        conditions:
          type: object
        actions:
          type: object

    WhatsAppTemplate:
      type: object
      properties:
        id:
          type: integer
          description: ID interno de la plantilla
          example: 170
        elementName:
          type: string
          description: Nombre identificador de la plantilla
          example: promo_belleza_personalizada
        status:
          type: string
          enum: [APPROVED, PENDING, REJECTED]
          description: Estado de aprobación en Meta
        category:
          type: string
          enum: [MARKETING, UTILITY, AUTHENTICATION]
          description: Categoría de la plantilla
        text:
          type: string
          description: Contenido del mensaje con variables ({name}, {1}, etc.)
        language:
          type: string
          description: Código de idioma (es, en, pt, etc.)
        components:
          type: array
          items:
            type: object

    WhatsAppCampaign:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        status:
          type: string
          enum: [SCHEDULED, SENDING, SENT, STOPPED, CANCELLED]
        templateId:
          type: string
        scheduledAt:
          type: string
          format: date-time
        sentCount:
          type: integer
        failedCount:
          type: integer

    WhatsAppEvent:
      type: object
      properties:
        id:
          type: integer
        destination:
          type: string
          description: Número de teléfono del destinatario
        status:
          type: string
          enum: [sent, delivered, read, failed, invalid, enqueued, pending]
        failureCode:
          type: string
          nullable: true
        failureReason:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time

    WhatsAppCampaignFull:
      allOf:
        - $ref: "#/components/schemas/WhatsAppCampaign"
        - type: object
          properties:
            events:
              type: array
              items:
                $ref: "#/components/schemas/WhatsAppEvent"
            eventCounter:
              type: object
              properties:
                pending:
                  type: integer
                enqueued:
                  type: integer
                failed:
                  type: integer
                sent:
                  type: integer
                delivered:
                  type: integer
                read:
                  type: integer
                answered:
                  type: integer
                invalid:
                  type: integer

    Note:
      type: object
      properties:
        id:
          type: string
        text:
          type: string
        createdAt:
          type: string
          format: date-time

    OperatorTag:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        color:
          type: string

    Phase:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        order:
          type: integer

    FeaturesResponse:
      type: object
      properties:
        features:
          type: object
          description: Mapa de features habilitadas y sus valores
          additionalProperties: true
          example:
            livechat: true
            whatsapp: true
            campaigns: false

    Integration:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
        enabled:
          type: boolean
        config:
          type: object

    PhaseCount:
      type: object
      properties:
        phaseId:
          type: string
        phaseName:
          type: string
        count:
          type: integer

    # ── Webhook Schemas ──────────────────────────
    WebhookEventType:
      type: string
      description: |
        Tipo de evento al que se puede suscribir un webhook. Formato: `categoría/acción`.
      enum:
        - conversation/created
        - conversation/updated
        - conversation/deleted
        - conversation/operator_assigned
        - conversation/message
        - conversation/note
        - conversation/archived
        - conversation/contact
        - conversation/phase_changed
        - conversation/tag_added
        - conversation/tag_removed
        - contact/created
        - contact/updated
        - contact/status_changed
        - contact/operator_assigned
        - contact/tag_added
        - contact/deleted

    WebhookSubscription:
      type: object
      properties:
        id:
          type: string
          example: "60a1b2c3d4e5f6a7b8c9d0e1"
        companyId:
          type: string
        websiteId:
          type: string
          description: "ID del canal o `*` para todos los canales"
          example: "*"
        url:
          type: string
          format: uri
          example: "https://tu-servidor.com/webhook"
        eventTypes:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
          example: ["conversation/created", "contact/created"]
        isActive:
          type: boolean
          example: true
        secret:
          type: string
          description: "Token secreto enviado en el header `X-Webhook-Secret` de cada entrega"
          example: "whsec_aBcDeFgHiJkLmNoPqRsTuVwXyZ123456"
        retryCount:
          type: integer
          minimum: 0
          maximum: 10
          description: "Cantidad máxima de reintentos en caso de fallo (backoff exponencial)"
          example: 5
        customHeaders:
          type: object
          additionalProperties:
            type: string
          description: "Headers personalizados enviados con cada entrega"
          example: {"X-Custom-Key": "valor"}
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    CreateWebhookRequest:
      type: object
      required: [url, eventTypes]
      properties:
        url:
          type: string
          format: uri
          description: URL donde Cliengo enviará los eventos via POST
          example: "https://tu-servidor.com/webhook"
        eventTypes:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/WebhookEventType"
          description: "Eventos a los que suscribirse (al menos uno)"
          example: ["conversation/created", "conversation/message"]
        websiteId:
          type: string
          description: "ID del canal. Usar `*` o omitir para recibir eventos de todos los canales"
          default: "*"
        retryCount:
          type: integer
          minimum: 0
          maximum: 10
          default: 5
          description: "Reintentos en caso de fallo (0 = sin reintentos, máximo 10)"
        customHeaders:
          type: object
          additionalProperties:
            type: string
          description: "Headers extra a incluir en cada entrega"

    UpdateWebhookRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
        eventTypes:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
        isActive:
          type: boolean
        retryCount:
          type: integer
          minimum: 0
          maximum: 10
        customHeaders:
          type: object
          additionalProperties:
            type: string

    WebhookDeliveryLog:
      type: object
      description: Registro de un intento de entrega de un webhook.
      properties:
        id:
          type: string
        webhookId:
          type: string
        url:
          type: string
          format: uri
        event:
          type: string
          description: Tipo de evento entregado
          example: "conversation/created"
        payload:
          type: object
          description: Body JSON enviado al endpoint
        statusCode:
          type: integer
          nullable: true
          description: "HTTP status de la respuesta (null si no hubo respuesta)"
          example: 200
        responseBody:
          type: string
          nullable: true
          description: Body de la respuesta del servidor
        errorMessage:
          type: string
          nullable: true
          description: "Mensaje de error (null si exitoso)"
        success:
          type: boolean
        createdAt:
          type: string
          format: date-time

  parameters:
    LimitParam:
      name: limit
      in: query
      schema:
        type: integer
        default: 20
        maximum: 100
      description: Resultados por página
    OffsetParam:
      name: offset
      in: query
      schema:
        type: integer
        default: 0
      description: Offset de paginación
    PageParam:
      name: page
      in: query
      schema:
        type: integer
        default: 1
      description: Número de página
    webhookId:
      name: webhookId
      in: path
      required: true
      schema:
        type: string
      description: ID del webhook

paths:
  # ── System ──────────────────────────────────────
  /health:
    get:
      summary: Health check
      operationId: healthCheck
      security: []
      tags: [System]
      responses:
        "200":
          description: Gateway operativo
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "ok"

  # ── Contacts ────────────────────────────────────
  /v1/contacts:
    get:
      summary: Buscar contactos
      operationId: searchContacts
      tags: [Contacts]
      description: Búsqueda de contactos con filtros avanzados.
      parameters:
        - name: search
          in: query
          schema:
            type: string
          description: "Búsqueda fuzzy en nombre, email y teléfono"
        - name: email
          in: query
          schema:
            type: string
          description: Filtrar por email (búsqueda parcial)
        - name: name
          in: query
          schema:
            type: string
          description: Filtrar por nombre (búsqueda parcial)
        - name: phone
          in: query
          schema:
            type: string
          description: Filtrar por teléfono (búsqueda parcial)
        - name: status
          in: query
          schema:
            type: string
            enum: [new, active, client, long_term]
          description: Filtrar por estado del contacto
        - name: subStatus
          in: query
          schema:
            type: string
          description: Filtrar por sub-estado
        - name: websiteId
          in: query
          schema:
            type: string
          description: "Filtrar por canal (IDs separados por coma)"
        - name: assignedTo
          in: query
          schema:
            type: string
          description: "Filtrar por agente asignado (IDs separados por coma, o UNASSIGNED)"
        - name: entryMethod
          in: query
          schema:
            type: string
          description: Filtrar por método de entrada (WEB, WHATSAPP, FACEBOOK, etc.)
        - name: conversationTags
          in: query
          schema:
            type: string
          description: "Filtrar por tags de conversación (separados por coma)"
        - name: rating
          in: query
          schema:
            type: string
          description: "Filtrar por calificación (valores separados por coma)"
        - name: dateFrom
          in: query
          schema:
            type: string
            format: date-time
          description: Contactos creados después de esta fecha (ISO 8601)
        - name: dateTo
          in: query
          schema:
            type: string
            format: date-time
          description: Contactos creados antes de esta fecha (ISO 8601)
        - name: lastUpdateDateFrom
          in: query
          schema:
            type: string
            format: date-time
          description: Contactos actualizados después de esta fecha (ISO 8601)
        - name: lastUpdateDateTo
          in: query
          schema:
            type: string
            format: date-time
          description: Contactos actualizados antes de esta fecha (ISO 8601)
        - name: orderBy
          in: query
          schema:
            type: string
            default: _id
          description: "Campo para ordenar (ej: creationDate, name, email)"
        - name: order
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          description: Dirección de ordenamiento
        - name: page
          in: query
          schema:
            type: integer
            default: 1
          description: Número de página
        - $ref: "#/components/parameters/LimitParam"
      responses:
        "200":
          description: Resultados de la búsqueda
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactSearchResponse"

    post:
      summary: Crear contacto
      operationId: createContact
      tags: [Contacts]
      description: Crea un nuevo contacto.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [websiteId, name]
              properties:
                websiteId:
                  type: string
                  description: ID del canal donde se origina el contacto
                name:
                  type: string
                email:
                  type: string
                phone:
                  type: string
                internationalPhoneNumber:
                  type: string
                message:
                  type: string
                status:
                  type: string
                  enum: [new, active, client, long_term]
                entryMethod:
                  type: string
                assignedTo:
                  type: string
                  description: ID del agente a asignar
                note:
                  type: string
                  description: Nota inicial del contacto
                customFields:
                  type: object
                  description: Campos personalizados (key-value)
      responses:
        "200":
          description: Contacto creado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"

  /v1/contacts/bulk-delete:
    post:
      summary: Eliminar contactos en lote
      operationId: bulkDeleteContacts
      tags: [Contacts]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [contacts]
              properties:
                contacts:
                  type: array
                  items:
                    type: string
                  description: Array de IDs de contactos a eliminar
      responses:
        "207":
          description: Resultado por cada contacto (Multi-Status)

  /v1/contacts/{contactId}:
    get:
      summary: Obtener contacto
      operationId: getContact
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Detalle del contacto
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"

    patch:
      summary: Actualizar contacto
      operationId: patchContact
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                phone:
                  type: string
                status:
                  type: string
                assignedTo:
                  type: string
      responses:
        "200":
          description: Contacto actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"

    delete:
      summary: Eliminar contacto
      operationId: deleteContact
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Contacto eliminado

  /v1/contacts/{contactId}/notes:
    get:
      summary: Listar notas del contacto
      operationId: listContactNotes
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Lista de notas

    post:
      summary: Agregar nota al contacto
      operationId: createContactNote
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message:
                  type: string
      responses:
        "200":
          description: Nota creada

  /v1/contacts/{contactId}/notes/{noteId}:
    patch:
      summary: Actualizar nota
      operationId: patchContactNote
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
        - name: noteId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                message:
                  type: string
      responses:
        "200":
          description: Nota actualizada

    delete:
      summary: Eliminar nota
      operationId: deleteContactNote
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
        - name: noteId
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Nota eliminada

  /v1/contacts/{contactId}/tags:
    post:
      summary: Agregar tag al contacto
      operationId: addContactTag
      tags: [Contacts]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tag]
              properties:
                tag:
                  type: string
      responses:
        "200":
          description: Tag agregado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"

  /v1/contacts/{contactId}/history:
    get:
      summary: Historial del contacto
      operationId: getContactHistory
      tags: [Contacts]
      description: Logs y notas del contacto combinados.
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Historial del contacto

  # ── Conversations ───────────────────────────────
  /v1/conversations:
    get:
      summary: Listar conversaciones
      operationId: listConversations
      tags: [Conversations]
      description: Conversaciones de la empresa autenticada, con filtros avanzados y paginación.
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [opened, closed, inactive]
          description: Estado de la conversación
        - name: tags
          in: query
          schema:
            type: string
          description: Filtrar por tags (separados por coma)
        - name: fromDate
          in: query
          schema:
            type: string
            format: date-time
          description: Fecha de inicio (ISO 8601)
        - name: toDate
          in: query
          schema:
            type: string
            format: date-time
          description: Fecha de fin (ISO 8601)
        - name: channel
          in: query
          schema:
            type: string
          description: "Filtrar por canal (separados por coma, valores: WEB, WHATSAPP, FACEBOOK, INSTAGRAM)"
        - name: phase
          in: query
          schema:
            type: string
          description: ID de la fase
        - name: agent
          in: query
          schema:
            type: string
          description: ID del usuario/agente asignado
        - name: search
          in: query
          schema:
            type: string
          description: Búsqueda de texto libre
        - $ref: "#/components/parameters/LimitParam"
        - $ref: "#/components/parameters/OffsetParam"
        - $ref: "#/components/parameters/PageParam"
        - name: sort
          in: query
          schema:
            type: string
          description: "Campo y orden de ordenamiento (ej: createdAt:desc)"
      responses:
        "200":
          description: Lista de conversaciones
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConversationListResponse"

    post:
      summary: Crear conversación
      operationId: createConversation
      tags: [Conversations]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [channelId, companyId, channel, from]
              properties:
                channelId:
                  type: string
                companyId:
                  type: string
                channel:
                  $ref: "#/components/schemas/Channel"
                from:
                  type: string
                  description: Identificador del originante
                url:
                  type: string
      responses:
        "200":
          description: Conversación creada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"

  /v1/conversations/by-phase:
    get:
      summary: Conversaciones agrupadas por fase
      operationId: listConversationsByPhase
      tags: [Conversations]
      description: Agrupa las conversaciones por fase del pipeline. Soporta los mismos filtros que el listado de conversaciones.
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [opened, closed, inactive]
          description: Estado de la conversación
        - name: tags
          in: query
          schema:
            type: string
          description: Filtrar por tags (separados por coma)
        - name: fromDate
          in: query
          schema:
            type: string
            format: date-time
          description: Fecha de inicio (ISO 8601)
        - name: toDate
          in: query
          schema:
            type: string
            format: date-time
          description: Fecha de fin (ISO 8601)
        - name: channel
          in: query
          schema:
            type: string
          description: "Filtrar por canal (separados por coma, valores: WEB, WHATSAPP, FACEBOOK, INSTAGRAM)"
        - name: phase
          in: query
          schema:
            type: string
          description: ID de la fase
        - name: agent
          in: query
          schema:
            type: string
          description: ID del usuario/agente asignado
        - name: search
          in: query
          schema:
            type: string
          description: Búsqueda de texto libre
        - $ref: "#/components/parameters/LimitParam"
        - $ref: "#/components/parameters/OffsetParam"
        - $ref: "#/components/parameters/PageParam"
        - name: sort
          in: query
          schema:
            type: string
          description: "Campo y orden de ordenamiento (ej: createdAt:desc)"
      responses:
        "200":
          description: Conversaciones agrupadas por fase
          content:
            application/json:
              schema:
                type: object
                properties:
                  phases:
                    type: array
                    items:
                      type: object
                      properties:
                        phase:
                          $ref: "#/components/schemas/Phase"
                        conversations:
                          $ref: "#/components/schemas/ConversationListResponse"

  /v1/conversations/export:
    get:
      summary: Exportar conversaciones
      operationId: exportConversations
      tags: [Conversations]
      description: Exporta conversaciones en formato descargable.
      responses:
        "200":
          description: Archivo de exportación

  /v1/conversations/channels/list:
    get:
      summary: Listar canales activos
      operationId: listConversationChannels
      tags: [Conversations]
      description: Devuelve los canales con conversaciones activas.
      responses:
        "200":
          description: Lista de canales
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Channel"

  /v1/conversations/contact/{contactId}:
    get:
      summary: Conversaciones de un contacto
      operationId: getConversationsByContact
      tags: [Conversations]
      parameters:
        - name: contactId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Conversaciones del contacto
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Conversation"

  /v1/conversations/{conversationId}:
    get:
      summary: Obtener conversación
      operationId: getConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Detalle de la conversación
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"
        "404":
          description: No encontrada

    put:
      summary: Actualizar conversación
      operationId: updateConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Conversation"
      responses:
        "200":
          description: Conversación actualizada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"

    patch:
      summary: Actualizar contexto de conversación
      operationId: patchConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Conversación parcialmente actualizada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"

    delete:
      summary: Eliminar conversación
      operationId: deleteConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Conversación eliminada

  /v1/conversations/{conversationId}/messages:
    get:
      summary: Listar mensajes
      operationId: listConversationMessages
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
        - name: includeCommands
          in: query
          schema:
            type: boolean
          description: Incluir mensajes de tipo comando
      responses:
        "200":
          description: Mensajes de la conversación
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Message"

    post:
      summary: Enviar mensaje
      operationId: addConversationMessage
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [from, fromType, type]
              properties:
                body:
                  type: string
                  example: "Hola, necesito ayuda"
                fromType:
                  type: string
                  enum: [user, robot, visitor]
                from:
                  type: string
                  description: ID del remitente
                type:
                  type: string
                  enum: [image, text/plain, command, note]
                message:
                  type: object
                  description: Para mensajes multimedia
                  properties:
                    url:
                      type: string
                    caption:
                      type: string
      responses:
        "200":
          description: Mensaje enviado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Message"

  /v1/conversations/{conversationId}/assign:
    post:
      summary: Asignar a operador
      operationId: assignConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [assignedTo]
              properties:
                assignedTo:
                  type: string
                  description: ID del operador
      responses:
        "200":
          description: Conversación asignada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"

  /v1/conversations/{conversationId}/transfer:
    post:
      summary: Transferir conversación
      operationId: transferConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [transferTo]
              properties:
                transferTo:
                  type: string
                  description: ID del operador destino
      responses:
        "200":
          description: Conversación transferida
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"

  /v1/conversations/{conversationId}/close:
    post:
      summary: Cerrar conversación
      operationId: closeConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [isLead]
              properties:
                isLead:
                  type: boolean
                  description: Si la conversación resultó en un lead
      responses:
        "200":
          description: Conversación cerrada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"

  /v1/conversations/{conversationId}/open:
    post:
      summary: Reabrir conversación
      operationId: openConversation
      tags: [Conversations]
      description: Reabre una conversación cerrada (status → ACTIVE).
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Conversación reabierta
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Conversation"

  /v1/conversations/{conversationId}/mark:
    post:
      summary: Marcar conversación (leída/no leída)
      operationId: markConversation
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                read:
                  type: boolean
      responses:
        "200":
          description: Conversación marcada

  /v1/conversations/{conversationId}/contact:
    put:
      summary: Actualizar datos del contacto de la conversación
      operationId: updateConversationContact
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                phone:
                  type: string
      responses:
        "200":
          description: Contacto actualizado

  # ── Notes (sub-resource of Conversations) ──────────────────────
  /v1/conversations/{conversationId}/notes:
    get:
      summary: Listar notas de una conversación
      operationId: listConversationNotes
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Lista de notas
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Note"

    post:
      summary: Agregar nota a conversación
      operationId: createConversationNote
      tags: [Conversations]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message:
                  type: string
                  description: Contenido de la nota
                tags:
                  type: array
                  items:
                    type: string
                  description: Tags opcionales para la nota
                quote:
                  type: array
                  items:
                    type: string
                  description: IDs de mensajes citados
      responses:
        "201":
          description: Nota creada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Note"

  # ── Tags (Operator Tags) ──────────────────────
  /v1/tags:
    get:
      summary: Listar tags
      operationId: listTags
      tags: [Tags]
      description: Devuelve todos los tags de operador de la empresa.
      responses:
        "200":
          description: Lista de tags
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/OperatorTag"

    post:
      summary: Crear tag
      operationId: createTag
      tags: [Tags]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                color:
                  type: string
      responses:
        "201":
          description: Tag creado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OperatorTag"

  /v1/tags/{tagId}:
    put:
      summary: Actualizar tag
      operationId: updateTag
      tags: [Tags]
      parameters:
        - name: tagId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                color:
                  type: string
      responses:
        "200":
          description: Tag actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OperatorTag"

    delete:
      summary: Eliminar tag
      operationId: deleteTag
      tags: [Tags]
      parameters:
        - name: tagId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Tag eliminado

  # ── Phases ─────────────────────────────────────
  /v1/phases:
    get:
      summary: Listar fases
      operationId: listPhases
      tags: [Phases]
      description: Fases del pipeline de conversaciones.
      responses:
        "200":
          description: Lista de fases
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Phase"

    post:
      summary: Crear fase
      operationId: createPhase
      tags: [Phases]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                order:
                  type: integer
      responses:
        "201":
          description: Fase creada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Phase"

  /v1/phases/counts:
    get:
      summary: Conteo por fase
      operationId: getPhaseCounts
      tags: [Phases]
      description: Cantidad de conversaciones en cada fase. Soporta los mismos filtros que el listado de conversaciones.
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [opened, closed, inactive]
          description: Estado de la conversación
        - name: tags
          in: query
          schema:
            type: string
          description: Filtrar por tags (separados por coma)
        - name: fromDate
          in: query
          schema:
            type: string
            format: date-time
          description: Fecha de inicio (ISO 8601)
        - name: toDate
          in: query
          schema:
            type: string
            format: date-time
          description: Fecha de fin (ISO 8601)
        - name: channel
          in: query
          schema:
            type: string
          description: "Filtrar por canal (separados por coma, valores: WEB, WHATSAPP, FACEBOOK, INSTAGRAM)"
        - name: phase
          in: query
          schema:
            type: string
          description: ID de la fase
        - name: agent
          in: query
          schema:
            type: string
          description: ID del usuario/agente asignado
        - name: search
          in: query
          schema:
            type: string
          description: Búsqueda de texto libre
      responses:
        "200":
          description: Conteos por fase
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PhaseCount"

  /v1/phases/{phaseId}:
    put:
      summary: Actualizar fase
      operationId: updatePhase
      tags: [Phases]
      parameters:
        - name: phaseId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                order:
                  type: integer
      responses:
        "200":
          description: Fase actualizada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Phase"

    delete:
      summary: Eliminar fase
      operationId: deletePhase
      tags: [Phases]
      parameters:
        - name: phaseId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Fase eliminada

  /v1/conversations/{conversationId}/phases/{phaseId}:
    patch:
      summary: Asignar fase a conversación
      operationId: assignPhaseToConversation
      tags: [Phases]
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
        - name: phaseId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Fase asignada

  # ── Accounts ────────────────────────────────────
  /v1/account:
    get:
      summary: Info de la cuenta
      operationId: getAccount
      tags: [Accounts]
      responses:
        "200":
          description: Datos de la cuenta
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Account"

    put:
      summary: Actualizar cuenta
      operationId: updateAccount
      tags: [Accounts]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
      responses:
        "200":
          description: Cuenta actualizada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Account"

  /v1/account/plan:
    get:
      summary: Plan de la cuenta
      operationId: getAccountPlan
      tags: [Accounts]
      responses:
        "200":
          description: Plan actual de la cuenta
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Plan"

  /v1/account/features:
    get:
      summary: Features de la cuenta
      operationId: getAccountFeatures
      tags: [Accounts]
      responses:
        "200":
          description: Features habilitadas
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeaturesResponse"

  /v1/account/integrations:
    get:
      summary: Integraciones de la cuenta
      operationId: getAccountIntegrations
      tags: [Accounts]
      responses:
        "200":
          description: Lista de integraciones configuradas
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Integration"

  # ── Channels ─────────────────────────────────────
  /v1/channels:
    get:
      summary: Listar canales
      operationId: listChannels
      tags: [Channels]
      description: Lista los canales configurados en la cuenta.
      parameters:
        - $ref: "#/components/parameters/LimitParam"
        - $ref: "#/components/parameters/OffsetParam"
      responses:
        "200":
          description: Lista de canales
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/ChannelSite"

    post:
      summary: Crear canal
      operationId: createChannel
      tags: [Channels]
      description: Crea un nuevo canal.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, url]
              properties:
                title:
                  type: string
                url:
                  type: string
                  format: uri
      responses:
        "201":
          description: Canal creado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelSite"

  /v1/channels/{channelId}:
    get:
      summary: Obtener canal
      operationId: getChannel
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Detalle del canal
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelSite"
        "404":
          description: No encontrado

    put:
      summary: Actualizar canal
      operationId: updateChannel
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                url:
                  type: string
                  format: uri
      responses:
        "200":
          description: Canal actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelSite"

    patch:
      summary: Actualizar canal parcialmente
      operationId: patchChannel
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Canal parcialmente actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChannelSite"

    delete:
      summary: Eliminar canal
      operationId: deleteChannel
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Canal eliminado

  /v1/channels/{channelId}/integrations:
    get:
      summary: Integraciones del canal
      operationId: getChannelIntegrations
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Lista de integraciones del canal

  /v1/channels/{channelId}/businessHoursConfig:
    get:
      summary: Horario de atención del canal
      operationId: getChannelBusinessHours
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Configuración de horarios

    put:
      summary: Actualizar horario de atención
      operationId: updateChannelBusinessHours
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Horarios actualizados

    delete:
      summary: Eliminar horario de atención
      operationId: deleteChannelBusinessHours
      tags: [Channels]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Horarios eliminados

  # ── Users ───────────────────────────────────────
  /v1/users:
    get:
      summary: Listar usuarios
      operationId: listUsers
      tags: [Users]
      parameters:
        - $ref: "#/components/parameters/LimitParam"
        - $ref: "#/components/parameters/OffsetParam"
      responses:
        "200":
          description: Lista de usuarios
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"

    post:
      summary: Crear usuario
      operationId: createUser
      tags: [Users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, email]
              properties:
                name:
                  type: string
                email:
                  type: string
                  format: email
                role:
                  type: string
      responses:
        "201":
          description: Usuario creado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

  /v1/users/me:
    get:
      summary: Obtener usuario actual
      operationId: getCurrentUser
      tags: [Users]
      responses:
        "200":
          description: Datos del usuario autenticado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

  /v1/users/roles:
    get:
      summary: Listar roles
      operationId: listUserRoles
      tags: [Users]
      responses:
        "200":
          description: Roles disponibles
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
                example: ["admin", "operator", "viewer"]

  /v1/users/{userId}:
    get:
      summary: Obtener usuario
      operationId: getUser
      tags: [Users]
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Datos del usuario
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

    put:
      summary: Actualizar usuario
      operationId: updateUser
      tags: [Users]
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                role:
                  type: string
      responses:
        "200":
          description: Usuario actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

    patch:
      summary: Actualizar usuario parcialmente
      operationId: patchUser
      tags: [Users]
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Usuario parcialmente actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

    delete:
      summary: Eliminar usuario
      operationId: deleteUser
      tags: [Users]
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Usuario eliminado

  /v1/users/invite:
    post:
      summary: Invitar usuario
      operationId: inviteUser
      tags: [Users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email:
                  type: string
                  format: email
                role:
                  type: string
      responses:
        "200":
          description: Invitación enviada

  # ── Plans ───────────────────────────────────────
  /v1/plans:
    get:
      summary: Listar planes
      operationId: listPlans
      tags: [Plans]
      description: Todos los planes disponibles de Cliengo.
      responses:
        "200":
          description: Lista de planes
          content:
            application/json:
              schema:
                type: object
                properties:
                  paging:
                    type: object
                    properties:
                      total:
                        type: integer
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/Plan"

  /v1/plans/active:
    get:
      summary: Planes activos
      operationId: getActivePlans
      tags: [Plans]
      description: Solo los planes actualmente disponibles para contratar.
      responses:
        "200":
          description: Lista de planes activos
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Plan"

  /v1/plans/{planId}:
    get:
      summary: Obtener plan
      operationId: getPlan
      tags: [Plans]
      parameters:
        - name: planId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Detalle del plan
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Plan"

  # ── Chatbots ────────────────────────────────────
  /v1/chatbots:
    get:
      summary: Listar chatbots
      operationId: listChatbots
      tags: [Chatbots]
      parameters:
        - name: channelId
          in: query
          schema:
            type: string
          description: Filtrar por canal
        - $ref: "#/components/parameters/LimitParam"
        - $ref: "#/components/parameters/OffsetParam"
      responses:
        "200":
          description: Lista de chatbots
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Chatbot"

  /v1/chatbots/{chatbotId}:
    get:
      summary: Obtener chatbot de un canal
      operationId: getChatbot
      tags: [Chatbots]
      parameters:
        - name: chatbotId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Configuración del chatbot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Chatbot"

    put:
      summary: Actualizar chatbot
      operationId: updateChatbot
      tags: [Chatbots]
      parameters:
        - name: chatbotId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Chatbot"
      responses:
        "200":
          description: Chatbot actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Chatbot"

  /v1/chatbots/{chatbotId}/triggers:
    get:
      summary: Listar triggers del chatbot
      operationId: listChatbotTriggers
      tags: [Chatbots]
      parameters:
        - name: chatbotId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Lista de triggers
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Trigger"

    post:
      summary: Crear trigger
      operationId: createChatbotTrigger
      tags: [Chatbots]
      parameters:
        - name: chatbotId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Trigger"
      responses:
        "201":
          description: Trigger creado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Trigger"

  /v1/chatbots/{chatbotId}/triggers/{triggerId}:
    get:
      summary: Obtener trigger
      operationId: getChatbotTrigger
      tags: [Chatbots]
      parameters:
        - name: chatbotId
          in: path
          required: true
          schema:
            type: string
        - name: triggerId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Detalle del trigger
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Trigger"

    put:
      summary: Actualizar trigger
      operationId: updateChatbotTrigger
      tags: [Chatbots]
      parameters:
        - name: chatbotId
          in: path
          required: true
          schema:
            type: string
        - name: triggerId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Trigger"
      responses:
        "200":
          description: Trigger actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Trigger"

    delete:
      summary: Eliminar trigger
      operationId: deleteChatbotTrigger
      tags: [Chatbots]
      parameters:
        - name: chatbotId
          in: path
          required: true
          schema:
            type: string
        - name: triggerId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Trigger eliminado

  # ── WhatsApp ─────────────────────────────────────
  /v1/whatsapp:
    get:
      summary: Listar canales con WhatsApp
      operationId: listWhatsAppChannels
      tags: [WhatsApp]
      description: Canales vinculados al token con configuración WhatsApp.
      responses:
        "200":
          description: Lista de canales

  /v1/whatsapp/{channelId}/templates:
    get:
      summary: Listar plantillas WhatsApp
      operationId: listWhatsAppTemplates
      tags: [WhatsApp]
      description: Devuelve las plantillas HSM aprobadas, pendientes y rechazadas del canal.
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
          description: ID del canal de WhatsApp (websiteId)
      responses:
        "200":
          description: Lista de plantillas
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: string
                    example: SUCCESS
                  statusCode:
                    type: integer
                    example: 200
                  result:
                    type: array
                    items:
                      $ref: "#/components/schemas/WhatsAppTemplate"

    post:
      summary: Crear plantilla WhatsApp
      operationId: createWhatsAppTemplate
      tags: [WhatsApp]
      description: Envía una nueva plantilla para aprobación de Meta.
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, language, category]
              properties:
                name:
                  type: string
                language:
                  type: string
                  example: "es_AR"
                category:
                  type: string
                  enum: [MARKETING, UTILITY, AUTHENTICATION]
                components:
                  type: array
                  items:
                    type: object
      responses:
        "201":
          description: Plantilla creada (pendiente aprobación)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsAppTemplate"

  /v1/whatsapp/{channelId}/templates/{templateId}:
    put:
      summary: Actualizar plantilla WhatsApp
      operationId: updateWhatsAppTemplate
      tags: [WhatsApp]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
        - name: templateId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Plantilla actualizada

  /v1/whatsapp/{channelId}/campaigns:
    post:
      summary: Crear campaña WhatsApp
      operationId: createWhatsAppCampaign
      tags: [WhatsApp]
      description: Crea una campaña de mensajes WhatsApp (inmediata o programada).
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [templateId, contactListId]
              properties:
                templateId:
                  type: string
                contactListId:
                  type: string
                scheduledAt:
                  type: string
                  format: date-time
                  description: Si no se envía, se envía inmediatamente
      responses:
        "201":
          description: Campaña creada
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsAppCampaign"

  /v1/whatsapp/{channelId}/campaigns/report:
    get:
      summary: Reporte de campañas
      operationId: getWhatsAppCampaignReport
      tags: [WhatsApp]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
        - name: fromDate
          in: query
          schema:
            type: string
            format: date
        - name: toDate
          in: query
          schema:
            type: string
            format: date
      responses:
        "200":
          description: Métricas de campañas

  /v1/whatsapp/{channelId}/campaigns/{campaignId}/full:
    get:
      summary: Detalle completo de campaña
      operationId: getWhatsAppCampaignFull
      tags: [WhatsApp]
      description: Devuelve el detalle de una campaña incluyendo el estado individual (eventos) de cada mensaje enviado.
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
        - name: campaignId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Detalle completo de la campaña
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhatsAppCampaignFull"

  /v1/whatsapp/{channelId}/campaigns/{campaignId}/cancel:
    put:
      summary: Cancelar campaña
      operationId: cancelWhatsAppCampaign
      tags: [WhatsApp]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
        - name: campaignId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Campaña cancelada

  /v1/whatsapp/{channelId}/messages:
    post:
      summary: Enviar WhatsApp a contactos
      operationId: sendWhatsAppMessage
      tags: [WhatsApp]
      description: Envía un mensaje WhatsApp a contactos de Cliengo.
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [templateId, contacts]
              properties:
                templateId:
                  type: string
                contacts:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Mensajes enviados

  /v1/whatsapp/{channelId}/notifications:
    post:
      summary: Enviar notificación WhatsApp
      operationId: sendWhatsAppNotification
      tags: [WhatsApp]
      description: Envía una notificación WhatsApp a un número de destino.
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [templateId, destination]
              properties:
                templateId:
                  type: string
                destination:
                  type: string
                  description: Número de teléfono destino
                variables:
                  type: object
      responses:
        "200":
          description: Notificación enviada

  /v1/whatsapp/{channelId}/contact-lists:
    post:
      summary: Crear lista de contactos
      operationId: createWhatsAppContactList
      tags: [WhatsApp]
      description: Crea una lista de contactos desde un archivo spreadsheet para campañas WhatsApp.
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "201":
          description: Lista de contactos creada

  /v1/whatsapp/{channelId}/account/limit:
    get:
      summary: Límite diario de WhatsApp
      operationId: getWhatsAppDailyLimit
      tags: [WhatsApp]
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Límite diario de la línea WhatsApp

  /v1/whatsapp/{channelId}/whatsapp-config:
    get:
      summary: Configuración WhatsApp del canal
      operationId: getWhatsAppConfig
      tags: [WhatsApp]
      description: Balance, uso y detalles de la línea WhatsApp.
      parameters:
        - name: channelId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Configuración WhatsApp

  # ── Teams ───────────────────────────────────────
  /v1/teams:
    get:
      summary: Listar equipos
      operationId: listTeams
      tags: [Teams]
      responses:
        "200":
          description: Lista de equipos

    post:
      summary: Crear equipo
      operationId: createTeam
      tags: [Teams]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                members:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: Equipo creado

  /v1/teams/{teamId}:
    get:
      summary: Obtener equipo
      operationId: getTeam
      tags: [Teams]
      parameters:
        - name: teamId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Detalle del equipo

    patch:
      summary: Actualizar equipo
      operationId: patchTeam
      tags: [Teams]
      parameters:
        - name: teamId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                members:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Equipo actualizado

    delete:
      summary: Eliminar equipo
      operationId: deleteTeam
      tags: [Teams]
      parameters:
        - name: teamId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Equipo eliminado

  # ── Webhooks ──────────────────────────────────
  /v1/webhooks:
    get:
      summary: Listar webhooks
      operationId: listWebhooks
      tags: [Webhooks]
      description: |
        Devuelve todos los webhooks configurados para la empresa, ordenados por fecha de creación (más recientes primero).
        Opcionalmente filtrá por `websiteId` para ver solo los webhooks de un canal específico.
      parameters:
        - name: websiteId
          in: query
          schema:
            type: string
          description: Filtrar webhooks por canal. Si se omite, devuelve todos.
      responses:
        "200":
          description: Lista de webhooks
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/WebhookSubscription"

    post:
      summary: Crear webhook
      operationId: createWebhook
      tags: [Webhooks]
      description: |
        Crea una nueva suscripción de webhook. Cliengo enviará un `POST` a la URL indicada cada vez que ocurra
        uno de los eventos seleccionados. El webhook se crea activo por defecto y se le asigna un `secret`
        que se enviará en el header `X-Webhook-Secret` de cada entrega para verificar autenticidad.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookRequest"
      responses:
        "201":
          description: Webhook creado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscription"
        "400":
          description: Datos inválidos (URL vacía, evento desconocido, etc.)

  /v1/webhooks/{webhookId}:
    get:
      summary: Obtener webhook
      operationId: getWebhook
      tags: [Webhooks]
      description: Devuelve los detalles de un webhook específico.
      parameters:
        - $ref: "#/components/parameters/webhookId"
      responses:
        "200":
          description: Detalle del webhook
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscription"
        "404":
          description: Webhook no encontrado

    put:
      summary: Actualizar webhook
      operationId: updateWebhook
      tags: [Webhooks]
      description: |
        Actualiza la configuración de un webhook existente. Solo se modifican los campos enviados en el body;
        los demás se mantienen sin cambios.
      parameters:
        - $ref: "#/components/parameters/webhookId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWebhookRequest"
      responses:
        "200":
          description: Webhook actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscription"
        "400":
          description: Datos inválidos
        "404":
          description: Webhook no encontrado

    patch:
      summary: Activar o desactivar webhook
      operationId: toggleWebhook
      tags: [Webhooks]
      description: |
        Activa o desactiva un webhook sin eliminarlo. Un webhook desactivado no recibe entregas
        pero mantiene su configuración y logs.
      parameters:
        - $ref: "#/components/parameters/webhookId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [isActive]
              properties:
                isActive:
                  type: boolean
                  description: "`true` para activar, `false` para desactivar"
      responses:
        "200":
          description: Estado actualizado
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscription"
        "404":
          description: Webhook no encontrado

    delete:
      summary: Eliminar webhook
      operationId: deleteWebhook
      tags: [Webhooks]
      description: Elimina un webhook y todo su historial de entregas de forma permanente.
      parameters:
        - $ref: "#/components/parameters/webhookId"
      responses:
        "204":
          description: Webhook eliminado
        "404":
          description: Webhook no encontrado

  /v1/webhooks/{webhookId}/logs:
    get:
      summary: Historial de entregas
      operationId: getWebhookLogs
      tags: [Webhooks]
      description: |
        Devuelve el historial de entregas (delivery log) de un webhook. Cada entrada registra
        el resultado de un intento de entrega: URL, evento, payload enviado, status HTTP de respuesta,
        y si fue exitoso o no. Útil para debugging.
      parameters:
        - $ref: "#/components/parameters/webhookId"
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
          description: Cantidad de registros a saltar (paginación)
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
            maximum: 100
          description: Cantidad máxima de registros a devolver
      responses:
        "200":
          description: Lista de entregas
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/WebhookDeliveryLog"
        "404":
          description: Webhook no encontrado

  /v1/webhooks/ping:
    post:
      summary: Probar conectividad
      operationId: pingWebhook
      tags: [Webhooks]
      description: |
        Envía un POST de prueba a una URL para verificar que es accesible antes de crear un webhook.
        No requiere que el webhook exista. Timeout: 10 segundos.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
                  example: "https://tu-servidor.com/webhook"
      responses:
        "200":
          description: Resultado de la prueba
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  status:
                    type: integer
                    description: HTTP status code de la respuesta (solo si exitoso)
                  error:
                    type: string
                    description: Mensaje de error (solo si falló)
              example:
                success: true
                status: 200

  /v1/webhooks/{webhookId}/test:
    post:
      summary: Enviar evento de prueba
      operationId: testWebhook
      tags: [Webhooks]
      description: |
        Envía un evento de prueba real al webhook. El payload incluye `"test": true` para que
        tu servidor pueda distinguirlo de eventos reales. La entrega se registra en el log.
      parameters:
        - $ref: "#/components/parameters/webhookId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [eventType]
              properties:
                eventType:
                  $ref: "#/components/schemas/WebhookEventType"
      responses:
        "200":
          description: Resultado del envío
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
              example:
                success: true
                message: "Test event delivered (200)"
        "404":
          description: Webhook no encontrado

tags:
  - name: System
    description: Estado del gateway
  - name: Accounts
    description: Información y configuración de la cuenta
  - name: Channels
    description: Canales configurados
  - name: Chatbots
    description: Configuración de chatbots y triggers
  - name: Contacts
    description: Buscar y gestionar contactos (Elasticsearch)
  - name: Conversations
    description: Conversaciones multicanal (Web, WhatsApp, Facebook, Instagram)
  - name: Phases
    description: Fases del pipeline de conversaciones
  - name: Plans
    description: Planes de suscripción
  - name: Tags
    description: Tags de operador para clasificar conversaciones
  - name: Teams
    description: Equipos de operadores
  - name: Users
    description: Usuarios y roles de la empresa
  - name: Webhooks
    description: |
      Suscribite a eventos en tiempo real. Cliengo envía un `POST` a tu URL cada vez que ocurre
      un evento en tus conversaciones o contactos. Los webhooks incluyen reintentos automáticos
      con backoff exponencial, headers de seguridad, y un log de entregas para debugging.

      **[Ver guia completa de Webhooks →](guides.html#webhooks)** — setup, eventos, payload, seguridad y buenas practicas.
  - name: WhatsApp
    description: Plantillas y campañas de WhatsApp
