openapi: 3.1.0
info:
  title: API do Kobana Dunning
  version: 1.0.0
  description: API pública de Regla de Cobranza — el motor de cobranza de Kobana. Gestiona clientes, cobros, reglas de cobranza, acuerdos, disputas y webhooks de forma programática. Autentícate con una clave de API (Bearer) creada en el panel en Configuración → Seguridad; el acceso se limita por los alcances de la clave (`dunning.dashboard.<recurso>.<acción>`).
  license:
    name: Proprietário — Kobana
    url: https://kobana.com.br
servers:
  - url: https://api.dunning.kobana.com.br
    description: Producción
  - url: https://api.dunning.sandbox.kobana.com.br
    description: Sandbox — entorno de pruebas aislado, separado de producción.
components:
  schemas:
    Error:
      type: object
      properties:
        message:
          type: string
          description: Mensaje legible que describe el error.
        code:
          type: string
          description: "Código estable del error (ej.: VALIDATION_ERROR, NOT_FOUND)."
          example: VALIDATION_ERROR
        details:
          description: "Detalles adicionales (ej.: errores de validación por campo)."
      required:
        - message
        - code
      description: "Envelope de error de la API: mensaje legible + código estable."
    PaginationMeta:
      type: object
      properties:
        total:
          type: integer
          description: Total de registros que coinciden con el filtro.
        page:
          type: integer
          description: Página actual.
        limit:
          type: integer
          description: Elementos por página aplicados.
        totalPages:
          type: integer
          description: Total de páginas.
      required:
        - total
        - page
        - limit
        - totalPages
      description: Metadatos de paginación de los listados.
    PersonAddress:
      type: object
      properties:
        street:
          type: string
          description: Calle.
        number:
          type: string
          description: Número.
        complement:
          type: string
          description: Complemento.
        neighborhood:
          type: string
          description: Barrio.
        city:
          type: string
          description: Ciudad.
        state:
          type: string
          minLength: 2
          maxLength: 2
          description: Estado (2 letras).
          example: SP
        zipCode:
          type: string
          description: Código postal (8 dígitos).
          example: "01310100"
      required:
        - street
        - number
        - neighborhood
        - city
        - state
        - zipCode
    PersonPhone:
      type: object
      properties:
        kind:
          type: string
          enum:
            - mobile
            - landline
            - commercial
            - whatsapp
          description: Tipo (`mobile` | `landline` | `commercial` | `whatsapp`).
        countryCode:
          type: string
          description: Código de país.
          example: "55"
        areaCode:
          type: string
          description: Código de área.
          example: "11"
        number:
          type: string
          description: Número.
          example: "999998888"
      required:
        - kind
        - countryCode
        - areaCode
        - number
    PersonEmail:
      type: object
      properties:
        label:
          type: string
          enum:
            - personal
            - commercial
            - financial
          description: Etiqueta (`personal` | `commercial` | `financial`).
        address:
          type: string
          format: email
          description: Dirección de correo.
      required:
        - label
        - address
    Person:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) asociada al cliente.
        classificationId:
          type:
            - string
            - "null"
          format: uuid
          description: Clasificación del cliente (segmentación de la regla).
        documentNumber:
          type:
            - string
            - "null"
          description: CPF/CNPJ (solo dígitos).
          example: "12345678901"
        documentType:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Tipo de documento (`cpf` | `cnpj`).
        kind:
          type:
            - string
            - "null"
          enum:
            - natural
            - juridical
            - null
          description: Naturaleza (`natural` = persona física, `juridical` = jurídica).
        name:
          type: string
          description: Nombre del cliente.
          example: Maria da Silva
        legalName:
          type:
            - string
            - "null"
          description: Razón social.
        nickname:
          type:
            - string
            - "null"
          description: Alias/nombre de fantasía.
        birthday:
          type:
            - string
            - "null"
          description: Fecha de nacimiento (ISO 8601).
        addresses:
          type: array
          items:
            $ref: "#/components/schemas/PersonAddress"
          description: Direcciones.
        phones:
          type: array
          items:
            $ref: "#/components/schemas/PersonPhone"
          description: Teléfonos.
        emails:
          type: array
          items:
            $ref: "#/components/schemas/PersonEmail"
          description: Correos electrónicos.
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        notes:
          type:
            - string
            - "null"
          description: Notas internas.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        createdViaApi:
          type: boolean
          description: true cuando el registro fue creado vía API.
        flaggedThirdPartyAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento en que la persona marcó "no soy yo" en el portal (supresión total).
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - classificationId
        - documentNumber
        - documentType
        - kind
        - name
        - legalName
        - nickname
        - birthday
        - addresses
        - phones
        - emails
        - externalId
        - tags
        - notes
        - customData
        - createdViaApi
        - flaggedThirdPartyAt
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: Cliente (persona natural o jurídica) de la cartera de cobranza.
    PersonCreateRequest:
      type: object
      properties:
        classificationId:
          type:
            - string
            - "null"
          format: uuid
          description: Clasificación del cliente (segmentación de la regla).
        documentNumber:
          type:
            - string
            - "null"
          maxLength: 20
          description: CPF/CNPJ (solo dígitos).
        documentType:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Tipo de documento (`cpf` | `cnpj`).
        kind:
          type:
            - string
            - "null"
          enum:
            - natural
            - juridical
            - null
          description: Naturaleza (`natural` = persona física, `juridical` = jurídica).
        name:
          type: string
          minLength: 1
          maxLength: 120
          description: Nombre del cliente.
        legalName:
          type:
            - string
            - "null"
          description: Razón social.
        nickname:
          type:
            - string
            - "null"
          maxLength: 120
          description: Alias/nombre de fantasía.
        birthday:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de nacimiento (ISO 8601).
        addresses:
          type: array
          items:
            $ref: "#/components/schemas/PersonAddress"
          description: Direcciones.
        phones:
          type: array
          items:
            $ref: "#/components/schemas/PersonPhone"
          description: Teléfonos.
        emails:
          type: array
          items:
            $ref: "#/components/schemas/PersonEmail"
          description: Correos electrónicos.
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        notes:
          type:
            - string
            - "null"
          description: Notas internas.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
      required:
        - name
      description: Datos para crear un cliente.
    PersonUpdateRequest:
      type: object
      properties:
        classificationId:
          type:
            - string
            - "null"
          format: uuid
          description: Clasificación del cliente (segmentación de la regla).
        documentNumber:
          type:
            - string
            - "null"
          maxLength: 20
          description: CPF/CNPJ (solo dígitos).
        documentType:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Tipo de documento (`cpf` | `cnpj`).
        kind:
          type:
            - string
            - "null"
          enum:
            - natural
            - juridical
            - null
          description: Naturaleza (`natural` = persona física, `juridical` = jurídica).
        name:
          type: string
          minLength: 1
          maxLength: 120
          description: Nombre del cliente.
        legalName:
          type:
            - string
            - "null"
          description: Razón social.
        nickname:
          type:
            - string
            - "null"
          maxLength: 120
          description: Alias/nombre de fantasía.
        birthday:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de nacimiento (ISO 8601).
        addresses:
          type: array
          items:
            $ref: "#/components/schemas/PersonAddress"
          description: Direcciones.
        phones:
          type: array
          items:
            $ref: "#/components/schemas/PersonPhone"
          description: Teléfonos.
        emails:
          type: array
          items:
            $ref: "#/components/schemas/PersonEmail"
          description: Correos electrónicos.
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        notes:
          type:
            - string
            - "null"
          description: Notas internas.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
      description: Campos a actualizar (parcial — envía solo lo que cambia).
    PersonListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Person"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ChargePolicy:
      type: object
      properties:
        fine:
          anyOf:
            - type: object
              properties:
                disabled:
                  type: boolean
                  enum:
                    - true
              required:
                - disabled
            - type: object
              properties:
                mode:
                  type: string
                  enum:
                    - percent
                    - fixed
                value:
                  type: number
                  minimum: 0
                graceDays:
                  type: integer
                  minimum: 0
              required:
                - mode
                - value
          description: "Multa por atraso: incide UNA vez tras `graceDays` del vencimiento. `mode` = `percent` (sobre el valor original) o `fixed` (valor absoluto); `value` ≥ 0."
        interest:
          anyOf:
            - type: object
              properties:
                disabled:
                  type: boolean
                  enum:
                    - true
              required:
                - disabled
            - type: object
              properties:
                mode:
                  type: string
                  enum:
                    - percent_month
                    - percent_day
                    - fixed_day
                value:
                  type: number
                  minimum: 0
                graceDays:
                  type: integer
                  minimum: 0
              required:
                - mode
                - value
          description: "Intereses de mora: pro-rata die, se acumulan por día a partir del vencimiento (respetando `graceDays`). `mode` = `percent_month`, `percent_day` o `fixed_day`; `value` ≥ 0."
        discount:
          anyOf:
            - type: object
              properties:
                disabled:
                  type: boolean
                  enum:
                    - true
              required:
                - disabled
            - type: array
              items:
                type: object
                properties:
                  mode:
                    type: string
                    enum:
                      - percent
                      - fixed
                  value:
                    type: number
                    minimum: 0
                  limitDate:
                    type: string
                required:
                  - mode
                  - value
          description: "Descuento por anticipación: lista de tramos `{ mode, value, limitDate? }` por fecha límite (más temprano = más descuento). `mode` = `percent` o `fixed`. `limitDate` (`YYYY-MM-DD`) ausente = válido mientras no esté vencido."
        correction:
          type: object
          properties:
            index:
              type: string
              enum:
                - none
                - ipca
                - igpm
              description: Índice de corrección monetaria (`none` | `ipca` | `igpm`).
          required:
            - index
          description: Corrección monetaria aplicada al valor.
      description: 'Política de cargos por título (motor). Sobrescribe, a nivel del cobro, la política heredada (organización → regla → cobro), con merge campo a campo. Cada bloque acepta `{ "disabled": true }` para desactivar ese cargo. Ausente/nulo = hereda todo del padre.'
    ChargeEncargos:
      type: object
      properties:
        fineAmount:
          type: number
          description: Multa calculada.
          example: 30
        interestAmount:
          type: number
          description: Intereses de mora calculados.
          example: 15
        correctionAmount:
          type: number
          description: Corrección monetaria calculada.
          example: 0
        discountAmount:
          type: number
          description: Descuento calculado.
          example: 0
        currentAmount:
          type: number
          description: Valor actual = original + multa + intereses + corrección − descuento.
          example: 1545
        daysOverdue:
          type: integer
          description: Días de atraso calculados en la zona horaria de la organización (0 mientras no está vencido).
          example: 12
      required:
        - fineAmount
        - interestAmount
        - correctionAmount
        - discountAmount
        - currentAmount
        - daysOverdue
      description: Desglose de cargos derivado por el motor en la zona horaria de la organización (aditivo al cobro). Valores en number (no string decimal).
    Charge:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) del cobro.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio (BU) del cobro.
        personId:
          type: string
          format: uuid
          description: Cliente (deudor) del cobro.
        collectionRuleId:
          type:
            - string
            - "null"
          format: uuid
          description: Regla de cobranza que sigue el título. Si se omite al crear, se usa la regla predeterminada de la organización.
        documentNumber:
          type:
            - string
            - "null"
          description: Número del documento de origen (factura, contrato, etc.).
          example: NF-2026-0042
        description:
          type:
            - string
            - "null"
          description: Descripción libre del cobro.
        originalAmount:
          type: string
          description: Valor original del título.
          example: "1500.00"
        interestAmount:
          type: string
          description: Intereses acumulados (predeterminado 0).
          example: "15.00"
        fineAmount:
          type: string
          description: Multa (predeterminado 0).
          example: "30.00"
        discountAmount:
          type: string
          description: Descuento aplicado (predeterminado 0).
          example: "0.00"
        correctionAmount:
          type: string
          description: Corrección monetaria acumulada (índice IPCA/IGP-M, predeterminado 0). Serializada como string decimal.
          example: "0.00"
        paidAmount:
          type:
            - string
            - "null"
          description: Valor efectivamente pagado.
          example: "1545.00"
        currentAmount:
          type:
            - string
            - "null"
          description: Valor actual (original + intereses + multa − descuento). Recalculado automáticamente al crear y actualizar.
          example: "1545.00"
        issueDate:
          type: string
          format: date-time
          description: Fecha de emisión.
        dueDate:
          type: string
          format: date-time
          description: Fecha de vencimiento.
        paidAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha del pago.
        daysOverdue:
          type: integer
          description: Días de atraso (0 mientras no está vencido). Recalculado en la lectura.
          example: 12
        status:
          type: string
          enum:
            - pending
            - paid
            - overdue
            - cancelled
            - negotiated
            - protested
            - negatived
            - written_off
          description: Estado (`pending` | `paid` | `overdue` | `cancelled` | `negotiated` | `protested` | `negatived` | `written_off`).
        paymentMethod:
          type:
            - string
            - "null"
          enum:
            - boleto
            - pix
            - credit_card
            - transfer
            - null
          description: Medio de pago (`boleto` | `pix` | `credit_card` | `transfer`).
        barcode:
          type:
            - string
            - "null"
          description: Código de barras/línea digitable del boleto.
        pixEmv:
          type:
            - string
            - "null"
          description: Código Pix copia y pega (EMV).
        paymentUrl:
          type:
            - string
            - "null"
          description: URL de la página de pago.
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        currentStep:
          type:
            - integer
            - "null"
          description: Etapa actual de la regla de cobranza (0 = ninguna ejecutada).
        lastNotificationAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de la última notificación enviada.
        negativedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de la inclusión en burós de crédito (SPC/Serasa).
        protestedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento del protesto notarial.
        source:
          type: string
          enum:
            - kobana
            - erp
            - api
            - spreadsheet
            - manual
          description: Origen del título (`kobana` | `erp` | `api` | `spreadsheet` | `manual`).
        lastVerifiedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Última verificación de pago en el origen.
        paymentClaimed:
          type: boolean
          description: true cuando el deudor declaró "ya pagué" en el portal — la regla se pausa y se abre verificación.
        legalHold:
          type: boolean
          description: "Bloqueo jurídico (prescripción, orden judicial): suspende el cobro."
        legalHoldReason:
          type:
            - string
            - "null"
          description: Motivo del bloqueo jurídico.
        encargoSource:
          type: string
          enum:
            - computed
            - boleto
          description: "Fuente de los cargos: `computed` (calculados por el motor a partir de la política) o `boleto` (espejados de un boleto registrado, que es la fuente de la verdad y no se recalcula)."
          example: computed
        chargePolicy:
          allOf:
            - $ref: "#/components/schemas/ChargePolicy"
            - type:
                - object
                - "null"
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        encargos:
          $ref: "#/components/schemas/ChargeEncargos"
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - personId
        - collectionRuleId
        - documentNumber
        - description
        - originalAmount
        - interestAmount
        - fineAmount
        - discountAmount
        - correctionAmount
        - paidAmount
        - currentAmount
        - issueDate
        - dueDate
        - paidAt
        - daysOverdue
        - status
        - paymentMethod
        - barcode
        - pixEmv
        - paymentUrl
        - externalId
        - customData
        - tags
        - currentStep
        - lastNotificationAt
        - negativedAt
        - protestedAt
        - source
        - lastVerifiedAt
        - paymentClaimed
        - legalHold
        - legalHoldReason
        - encargoSource
        - chargePolicy
        - deletedAt
        - metadata
        - createdAt
        - updatedAt
        - encargos
      description: 'Cobro (título) vinculado a un cliente y seguido por la regla de cobranza. Los valores monetarios se serializan como string decimal (ej.: "1500.00").'
    ChargeCreateRequest:
      type: object
      properties:
        personId:
          type: string
          format: uuid
          description: Cliente (deudor) del cobro.
        collectionRuleId:
          type:
            - string
            - "null"
          format: uuid
          description: Regla de cobranza que sigue el título. Si se omite al crear, se usa la regla predeterminada de la organización.
        documentNumber:
          type:
            - string
            - "null"
          maxLength: 50
          description: Número del documento de origen (factura, contrato, etc.).
        description:
          type:
            - string
            - "null"
          maxLength: 500
          description: Descripción libre del cobro.
        originalAmount:
          type: number
          exclusiveMinimum: 0
          description: Valor original del título.
          example: 1500
        interestAmount:
          type: number
          minimum: 0
          description: Intereses acumulados (predeterminado 0).
        fineAmount:
          type: number
          minimum: 0
          description: Multa (predeterminado 0).
        discountAmount:
          type: number
          minimum: 0
          description: Descuento aplicado (predeterminado 0).
        issueDate:
          type: string
          description: Fecha de emisión.
          example: 2026-07-01
        dueDate:
          type: string
          description: Fecha de vencimiento.
          example: 2026-08-01
        status:
          type: string
          enum:
            - pending
            - paid
            - overdue
            - cancelled
            - negotiated
            - protested
            - negatived
            - written_off
          description: Estado (`pending` | `paid` | `overdue` | `cancelled` | `negotiated` | `protested` | `negatived` | `written_off`).
        paymentMethod:
          type:
            - string
            - "null"
          enum:
            - boleto
            - pix
            - credit_card
            - transfer
            - null
          description: Medio de pago (`boleto` | `pix` | `credit_card` | `transfer`).
        barcode:
          type:
            - string
            - "null"
          maxLength: 100
          description: Código de barras/línea digitable del boleto.
        pixEmv:
          type:
            - string
            - "null"
          maxLength: 500
          description: Código Pix copia y pega (EMV).
        paymentUrl:
          type:
            - string
            - "null"
          format: uri
          description: URL de la página de pago.
        externalId:
          type:
            - string
            - "null"
          maxLength: 100
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        chargePolicy:
          allOf:
            - $ref: "#/components/schemas/ChargePolicy"
            - type:
                - object
                - "null"
      required:
        - personId
        - originalAmount
        - issueDate
        - dueDate
      description: Datos para crear un cobro. Los valores monetarios se envían como number; las fechas aceptan `YYYY-MM-DD` o datetime ISO 8601.
    ChargeUpdateRequest:
      type: object
      properties:
        personId:
          type: string
          format: uuid
          description: Cliente (deudor) del cobro.
        collectionRuleId:
          type:
            - string
            - "null"
          format: uuid
          description: Regla de cobranza que sigue el título. Si se omite al crear, se usa la regla predeterminada de la organización.
        documentNumber:
          type:
            - string
            - "null"
          maxLength: 50
          description: Número del documento de origen (factura, contrato, etc.).
        description:
          type:
            - string
            - "null"
          maxLength: 500
          description: Descripción libre del cobro.
        originalAmount:
          type: number
          exclusiveMinimum: 0
          description: Valor original del título.
          example: 1500
        interestAmount:
          type: number
          minimum: 0
          description: Intereses acumulados (predeterminado 0).
        fineAmount:
          type: number
          minimum: 0
          description: Multa (predeterminado 0).
        discountAmount:
          type: number
          minimum: 0
          description: Descuento aplicado (predeterminado 0).
        issueDate:
          type: string
          description: Fecha de emisión.
          example: 2026-07-01
        dueDate:
          type: string
          description: Fecha de vencimiento.
          example: 2026-08-01
        status:
          type: string
          enum:
            - pending
            - paid
            - overdue
            - cancelled
            - negotiated
            - protested
            - negatived
            - written_off
          description: Estado (`pending` | `paid` | `overdue` | `cancelled` | `negotiated` | `protested` | `negatived` | `written_off`).
        paymentMethod:
          type:
            - string
            - "null"
          enum:
            - boleto
            - pix
            - credit_card
            - transfer
            - null
          description: Medio de pago (`boleto` | `pix` | `credit_card` | `transfer`).
        barcode:
          type:
            - string
            - "null"
          maxLength: 100
          description: Código de barras/línea digitable del boleto.
        pixEmv:
          type:
            - string
            - "null"
          maxLength: 500
          description: Código Pix copia y pega (EMV).
        paymentUrl:
          type:
            - string
            - "null"
          format: uri
          description: URL de la página de pago.
        externalId:
          type:
            - string
            - "null"
          maxLength: 100
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        chargePolicy:
          allOf:
            - $ref: "#/components/schemas/ChargePolicy"
            - type:
                - object
                - "null"
        paidAmount:
          type:
            - number
            - "null"
          minimum: 0
          description: Valor efectivamente pagado.
        paidAt:
          type:
            - string
            - "null"
          description: Fecha del pago.
          example: 2026-08-03
      description: Campos a actualizar (parcial — envía solo lo que cambia). `currentAmount` y `daysOverdue` se recalculan automáticamente.
    ChargeListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Charge"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ChargeDeleteResponse:
      type: object
      properties:
        message:
          type: string
          description: Mensaje de confirmación de la eliminación.
      required:
        - message
    ChargeNotifyRequest:
      type: object
      properties:
        channel:
          type: string
          enum:
            - email
            - sms
            - whatsapp
            - voice
            - manual
          description: Canal (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        recipient:
          type: string
          minLength: 1
          description: "Destinatario: correo para `email`, teléfono para `sms`/`whatsapp`/`voice`; libre para `manual`."
          example: maria@exemplo.com.br
        subject:
          type: string
          maxLength: 200
          description: Asunto (usado en correo).
        body:
          type: string
          minLength: 1
          description: Cuerpo del mensaje.
        collectionRuleStepId:
          type: string
          format: uuid
          description: Etapa de la regla de cobranza a asociar (actualiza el `currentStep` del cobro).
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos libres guardados en la notificación.
      required:
        - channel
        - recipient
        - body
      description: Datos de la notificación manual a enviar al deudor.
    ChargeNotifyResponse:
      type: object
      properties:
        message:
          type: string
          description: Mensaje de confirmación del encolado.
        notification:
          type: object
          additionalProperties: {}
          description: Notificación creada (estado `queued`), incluyendo el cliente y el cobro relacionados.
      required:
        - message
        - notification
    AgreementInstallment:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        agreementId:
          type: string
          format: uuid
          description: Acuerdo dueño de la cuota.
        installmentNumber:
          type: integer
          description: Número de la cuota (desde 1).
          example: 1
        dueDate:
          type: string
          format: date-time
          description: Vencimiento de la cuota.
        amount:
          type: string
          description: Valor de la cuota.
          example: "200.00"
        paidAmount:
          type:
            - string
            - "null"
          description: Valor pagado de la cuota.
          example: "200.00"
        paidAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de pago de la cuota.
        status:
          type: string
          enum:
            - pending
            - paid
            - overdue
            - cancelled
          description: Estado (`pending` | `paid` | `overdue` | `cancelled`).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
        customMetadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
      required:
        - id
        - publicId
        - agreementId
        - installmentNumber
        - dueDate
        - amount
        - paidAmount
        - paidAt
        - status
        - deletedAt
        - customMetadata
        - metadata
        - externalId
        - createdAt
        - updatedAt
      description: Cuota de un acuerdo. Creada automáticamente en la aceptación, con vencimientos mensuales a partir de `firstDueDate`.
    Agreement:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) del acuerdo.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio (BU) del acuerdo.
        personId:
          type: string
          format: uuid
          description: Cliente (deudor) del acuerdo.
        negotiationOfferId:
          type:
            - string
            - "null"
          format: uuid
          description: Oferta de negociación que originó el acuerdo.
        originalTotal:
          type: string
          description: Total original de la deuda renegociada.
          example: "1500.00"
        discountAmount:
          type: string
          description: Descuento concedido (predeterminado 0).
          example: "300.00"
        finalTotal:
          type: string
          description: Total final (`originalTotal` − `discountAmount`). Calculado en el servidor.
          example: "1200.00"
        numberOfInstallments:
          type: integer
          description: Número de cuotas (1 a 120).
          example: 6
        installmentValue:
          type: string
          description: Valor de cada cuota (`finalTotal` / `numberOfInstallments`). Calculado en el servidor.
          example: "200.00"
        firstDueDate:
          type: string
          format: date-time
          description: Vencimiento de la primera cuota (las demás vencen mensualmente).
        status:
          type: string
          enum:
            - proposed
            - accepted
            - active
            - completed
            - cancelled
            - defaulted
          description: Estado (`proposed` | `accepted` | `active` | `completed` | `cancelled` | `defaulted`).
        acceptedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de la aceptación.
        acceptedIp:
          type:
            - string
            - "null"
          description: IP registrada en la aceptación (pista de auditoría).
        termsAccepted:
          type:
            - string
            - "null"
          description: Términos aceptados por el deudor (texto/versión).
        cancelledAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de la cancelación.
        cancellationReason:
          type:
            - string
            - "null"
          description: Motivo de la cancelación.
        completedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de la conclusión (todas las cuotas pagadas).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - personId
        - negotiationOfferId
        - originalTotal
        - discountAmount
        - finalTotal
        - numberOfInstallments
        - installmentValue
        - firstDueDate
        - status
        - acceptedAt
        - acceptedIp
        - termsAccepted
        - cancelledAt
        - cancellationReason
        - completedAt
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: 'Acuerdo de renegociación en cuotas firmado con un cliente. Los valores monetarios se serializan como string decimal (ej.: "1200.00"). Ciclo: `proposed` → `accepted` → `active` → `completed`; puede terminar en `cancelled` o `defaulted`.'
    AgreementCreateRequest:
      type: object
      properties:
        personId:
          type: string
          format: uuid
          description: Cliente (deudor) del acuerdo.
        negotiationOfferId:
          type:
            - string
            - "null"
          format: uuid
          description: Oferta de negociación que originó el acuerdo.
        originalTotal:
          type: number
          exclusiveMinimum: 0
          description: Total original de la deuda renegociada.
          example: 1500
        discountAmount:
          type: number
          minimum: 0
          description: Descuento concedido (predeterminado 0).
          example: 300
        numberOfInstallments:
          type: integer
          exclusiveMinimum: 0
          maximum: 120
          description: Número de cuotas (1 a 120).
          example: 6
        firstDueDate:
          type: string
          description: Vencimiento de la primera cuota (las demás vencen mensualmente).
          example: 2026-08-01
        termsAccepted:
          type:
            - string
            - "null"
          description: Términos aceptados por el deudor (texto/versión).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - personId
        - originalTotal
        - numberOfInstallments
        - firstDueDate
      description: Datos para crear un acuerdo (nace como `proposed`). `finalTotal` e `installmentValue` se calculan en el servidor. Los valores monetarios se envían como number.
    AgreementUpdateRequest:
      type: object
      properties:
        originalTotal:
          type: number
          exclusiveMinimum: 0
          description: Total original de la deuda renegociada.
        discountAmount:
          type: number
          minimum: 0
          description: Descuento concedido (predeterminado 0).
        numberOfInstallments:
          type: integer
          exclusiveMinimum: 0
          maximum: 120
          description: Número de cuotas (1 a 120).
        firstDueDate:
          type: string
          description: Vencimiento de la primera cuota (las demás vencen mensualmente).
          example: 2026-08-01
        termsAccepted:
          type:
            - string
            - "null"
          description: Términos aceptados por el deudor (texto/versión).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: Campos a actualizar (parcial). Cambiar valores o número de cuotas recalcula `finalTotal` e `installmentValue`.
    AgreementListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Agreement"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    AgreementAcceptRequest:
      type: object
      properties:
        acceptedIp:
          type: string
          description: IP registrada en la aceptación (pista de auditoría).
        termsAccepted:
          type: string
          description: Términos aceptados por el deudor (texto/versión).
      description: Datos de la aceptación (cuerpo opcional). Sin `acceptedIp`, la IP se extrae de los headers de la solicitud.
    AgreementCancelRequest:
      type: object
      properties:
        reason:
          type: string
          minLength: 1
          description: Motivo de la cancelación (obligatorio; guardado en `cancellationReason`).
      required:
        - reason
      description: Datos de la cancelación.
    AgreementInstallmentSummary:
      type: object
      properties:
        total:
          type: integer
          description: Total de cuotas.
        pending:
          type: integer
          description: Cuotas pendientes.
        paid:
          type: integer
          description: Cuotas pagadas.
        overdue:
          type: integer
          description: Cuotas vencidas.
        cancelled:
          type: integer
          description: Cuotas canceladas.
        totalAmount:
          type: number
          description: Suma de los valores de las cuotas.
          example: 1200
        paidAmount:
          type: number
          description: Suma de los valores pagados.
          example: 400
      required:
        - total
        - pending
        - paid
        - overdue
        - cancelled
        - totalAmount
        - paidAmount
      description: Resumen agregado de las cuotas del acuerdo. A diferencia de los campos del recurso, los totales monetarios aquí son number.
    AgreementInstallmentListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AgreementInstallment"
          description: Cuotas del acuerdo, ordenadas por número.
        summary:
          $ref: "#/components/schemas/AgreementInstallmentSummary"
      required:
        - data
        - summary
    AgreementInstallmentUpdateRequest:
      type: object
      properties:
        installmentNumber:
          type: integer
          exclusiveMinimum: 0
          description: Número de la cuota (desde 1).
          example: 1
        paidAmount:
          type: number
          exclusiveMinimum: 0
          description: Valor pagado de la cuota.
        paidAt:
          type: string
          description: Fecha de pago de la cuota.
          example: 2026-08-03
        status:
          type: string
          enum:
            - pending
            - paid
            - overdue
            - cancelled
          description: Estado (`pending` | `paid` | `overdue` | `cancelled`).
      required:
        - installmentNumber
      description: Liquidación/actualización de una cuota, identificada por `installmentNumber`. Marcar como `paid` sin `paidAt`/`paidAmount` completa con la fecha actual y el valor de la cuota.
    AgreementInstallmentUpdateResponse:
      type: object
      properties:
        installment:
          $ref: "#/components/schemas/AgreementInstallment"
        agreement:
          $ref: "#/components/schemas/Agreement"
      required:
        - installment
        - agreement
    DisputeChargeSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        documentNumber:
          type:
            - string
            - "null"
          description: Número de documento del cobro.
        currentAmount:
          type:
            - string
            - "null"
          description: Importe actual del cobro (cadena decimal).
          example: "150.00"
        status:
          type: string
          description: Estado del cobro.
      required:
        - id
        - documentNumber
        - currentAmount
        - status
      description: Resumen del cobro en disputa (en el listado).
    DisputePersonSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del cliente.
        documentNumber:
          type:
            - string
            - "null"
          description: CPF/CNPJ del cliente (presente en el detalle).
      required:
        - id
        - name
      description: Resumen del cliente de la disputa.
    DisputeAssigneeSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del responsable.
        email:
          type: string
          format: email
          description: Correo del responsable.
      required:
        - id
        - name
        - email
      description: Resumen del usuario responsable.
    Dispute:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) del registro.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio del registro.
        chargeId:
          type: string
          format: uuid
          description: Cobro en disputa.
        personId:
          type: string
          format: uuid
          description: Cliente (deudor) del cobro en disputa.
        type:
          type: string
          enum:
            - already_paid
            - not_recognized
            - wrong_amount
            - service_not_provided
            - incorrect_invoice
            - wrong_recipient
            - other
          description: Tipo de impugnación (`already_paid` | `not_recognized` | `wrong_amount` | `service_not_provided` | `incorrect_invoice` | `wrong_recipient` | `other`).
        reason:
          type:
            - string
            - "null"
          description: Relato del deudor/operador.
        disputedAmount:
          type:
            - string
            - "null"
          description: Importe impugnado, como cadena decimal; null cuando la impugnación es total.
          example: "150.00"
        isFullDispute:
          type: boolean
          description: true cuando la impugnación cubre el importe total del cobro.
        documents:
          type:
            - array
            - "null"
          items:
            type: object
            additionalProperties: {}
          description: Adjuntos/referencias de evidencia.
        status:
          type: string
          enum:
            - open
            - under_review
            - resolved_valid
            - resolved_invalid
            - canceled
          description: Estado (`open` | `under_review` | `resolved_valid` | `resolved_invalid` | `canceled`).
        assignedToId:
          type:
            - string
            - "null"
          format: uuid
          description: Usuario responsable de la revisión (debe pertenecer a la organización).
        slaDueAt:
          type:
            - string
            - "null"
          format: date-time
          description: Plazo (SLA) para la resolución.
        resolution:
          type:
            - string
            - "null"
          description: Decisión fundamentada de la resolución.
        resolutionEffect:
          type:
            - string
            - "null"
          enum:
            - resume_rule
            - adjust_amount
            - cancel_charge
            - write_off
            - null
          description: Efecto aplicado en la resolución (`resume_rule` | `adjust_amount` | `cancel_charge` | `write_off`).
        resolvedById:
          type:
            - string
            - "null"
          format: uuid
          description: Usuario que resolvió la disputa.
        openedAt:
          type: string
          format: date-time
          description: Apertura de la disputa (ISO 8601).
        resolvedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Resolución de la disputa (ISO 8601; null mientras está abierta).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customMetadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - chargeId
        - personId
        - type
        - reason
        - disputedAmount
        - isFullDispute
        - documents
        - status
        - assignedToId
        - slaDueAt
        - resolution
        - resolutionEffect
        - resolvedById
        - openedAt
        - resolvedAt
        - externalId
        - customMetadata
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: Disputa (impugnación) abierta por el deudor o un operador sobre un cobro. Mientras está abierta, la regla de cobro queda pausada.
    DisputeListItem:
      allOf:
        - $ref: "#/components/schemas/Dispute"
        - type: object
          properties:
            charge:
              $ref: "#/components/schemas/DisputeChargeSummary"
            person:
              $ref: "#/components/schemas/DisputePersonSummary"
          required:
            - charge
            - person
      description: "Elemento del listado: disputa con resúmenes del cobro y del cliente."
    DisputeDetail:
      allOf:
        - $ref: "#/components/schemas/Dispute"
        - type: object
          properties:
            charge:
              type: object
              additionalProperties: {}
              description: Cobro completo asociado a la disputa.
            person:
              $ref: "#/components/schemas/DisputePersonSummary"
            assignedTo:
              allOf:
                - $ref: "#/components/schemas/DisputeAssigneeSummary"
                - type:
                    - object
                    - "null"
                  description: Responsable de la revisión (null cuando no está asignada).
          required:
            - charge
            - person
            - assignedTo
      description: "Detalle de la disputa: incluye el cobro completo, el cliente y el responsable."
    DisputeCreateRequest:
      type: object
      properties:
        chargeId:
          type: string
          format: uuid
          description: Cobro en disputa.
        type:
          type: string
          enum:
            - already_paid
            - not_recognized
            - wrong_amount
            - service_not_provided
            - incorrect_invoice
            - wrong_recipient
            - other
          description: Tipo de impugnación (`already_paid` | `not_recognized` | `wrong_amount` | `service_not_provided` | `incorrect_invoice` | `wrong_recipient` | `other`).
        reason:
          type: string
          maxLength: 2000
          description: Relato del deudor/operador.
        disputedAmount:
          type: number
          exclusiveMinimum: 0
          description: Importe impugnado (número). Omite para impugnar el importe total.
          example: 150
        documents:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Adjuntos/referencias de evidencia.
        assignedToId:
          type: string
          format: uuid
          description: Usuario responsable de la revisión (debe pertenecer a la organización).
        slaDays:
          type: integer
          exclusiveMinimum: 0
          maximum: 60
          description: Días de SLA para resolver (máx. 60; valor por defecto del sistema si se omite).
          example: 5
      required:
        - chargeId
        - type
      description: Datos para abrir una disputa sobre un cobro.
    DisputeStartReviewRequest:
      type: object
      properties:
        action:
          type: string
          enum:
            - start_review
          description: "Acción: iniciar revisión (`start_review`)."
        assignedToId:
          type: string
          format: uuid
          description: Usuario responsable de la revisión (debe pertenecer a la organización).
      required:
        - action
      description: Mueve la disputa de `open` a `under_review`, asignando opcionalmente un responsable.
    DisputeResolveRequest:
      type: object
      properties:
        action:
          type: string
          enum:
            - resolve
          description: "Acción: resolver la disputa (`resolve`)."
        decision:
          type: string
          enum:
            - valid
            - invalid
          description: "Decisión: `valid` (procedente) o `invalid` (improcedente)."
        resolution:
          type: string
          minLength: 1
          maxLength: 2000
          description: Decisión fundamentada de la resolución.
        effect:
          type: string
          enum:
            - resume_rule
            - adjust_amount
            - cancel_charge
            - write_off
          description: Efecto de la resolución (`resume_rule` | `adjust_amount` | `cancel_charge` | `write_off`).
        adjustedAmount:
          type: number
          exclusiveMinimum: 0
          description: Nuevo importe del cobro cuando el efecto es `adjust_amount`.
          example: 120
      required:
        - action
        - decision
        - resolution
      description: Resuelve la disputa con una decisión fundamentada y un efecto sobre el cobro/la regla.
    DisputeCancelRequest:
      type: object
      properties:
        action:
          type: string
          enum:
            - cancel
          description: "Acción: cancelar la disputa (`cancel`)."
        reason:
          type: string
          maxLength: 2000
          description: Motivo de la cancelación.
      required:
        - action
      description: Cancela la disputa (p. ej., abierta por error); la regla del cobro se reanuda.
    DisputeActionRequest:
      oneOf:
        - $ref: "#/components/schemas/DisputeStartReviewRequest"
        - $ref: "#/components/schemas/DisputeResolveRequest"
        - $ref: "#/components/schemas/DisputeCancelRequest"
      discriminator:
        propertyName: action
        mapping:
          start_review: "#/components/schemas/DisputeStartReviewRequest"
          resolve: "#/components/schemas/DisputeResolveRequest"
          cancel: "#/components/schemas/DisputeCancelRequest"
      description: Acción sobre la disputa, discriminada por el campo `action` (`start_review` | `resolve` | `cancel`).
    DisputeResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/Dispute"
      required:
        - data
    DisputeDetailResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/DisputeDetail"
      required:
        - data
    DisputeListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/DisputeListItem"
        pagination:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - pagination
      description: "Sobre del listado de disputas. Atención: usa la clave `pagination` (mismos campos que `meta`)."
    TaskPersonSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del cliente.
        documentNumber:
          type:
            - string
            - "null"
          description: CPF/CNPJ del cliente.
      required:
        - id
        - name
        - documentNumber
      description: Resumen del cliente vinculado.
    TaskAssigneeSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del responsable.
        email:
          type: string
          format: email
          description: Correo del responsable.
        avatarUrl:
          type:
            - string
            - "null"
          description: URL del avatar del responsable.
      required:
        - id
        - name
        - email
        - avatarUrl
      description: Resumen del usuario responsable.
    TaskChargeSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        documentNumber:
          type:
            - string
            - "null"
          description: Número de documento del cobro.
        originalAmount:
          type: string
          description: Importe original del cobro (cadena decimal).
          example: "350.00"
        dueDate:
          type: string
          description: Vencimiento del cobro.
          example: 2026-08-01T00:00:00.000Z
        status:
          type: string
          description: Estado del cobro.
      required:
        - id
        - documentNumber
        - originalAmount
        - dueDate
        - status
      description: Resumen del cobro vinculado.
    Task:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) del registro.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio del registro.
        personId:
          type:
            - string
            - "null"
          format: uuid
          description: Cliente vinculado a la tarea.
        chargeId:
          type:
            - string
            - "null"
          format: uuid
          description: Cobro vinculado a la tarea.
        assignedToId:
          type:
            - string
            - "null"
          format: uuid
          description: Usuario responsable de la tarea.
        title:
          type: string
          description: Título de la tarea.
          example: Ligar para negociar parcela
        description:
          type:
            - string
            - "null"
          description: Descripción detallada.
        taskType:
          type: string
          enum:
            - call
            - email
            - visit
            - review
            - follow_up
          description: Tipo (`call` | `email` | `visit` | `review` | `follow_up`).
        priority:
          type: string
          enum:
            - low
            - medium
            - high
            - urgent
          description: Prioridad (`low` | `medium` | `high` | `urgent`).
        status:
          type: string
          enum:
            - pending
            - in_progress
            - completed
            - cancelled
          description: Estado (`pending` | `in_progress` | `completed` | `cancelled`).
        dueAt:
          type:
            - string
            - "null"
          format: date-time
          description: Plazo de la tarea (ISO 8601).
        completedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Finalización de la tarea (ISO 8601; null mientras está pendiente).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
        person:
          anyOf:
            - $ref: "#/components/schemas/TaskPersonSummary"
            - type: "null"
          description: Cliente vinculado (presente en creación/consulta o con `include=person`).
        assignedTo:
          anyOf:
            - $ref: "#/components/schemas/TaskAssigneeSummary"
            - type: "null"
          description: Responsable (presente en creación/consulta o con `include=assignedTo`).
        charge:
          anyOf:
            - $ref: "#/components/schemas/TaskChargeSummary"
            - type: "null"
          description: Cobro vinculado (presente en creación/consulta o con `include=charge`).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - personId
        - chargeId
        - assignedToId
        - title
        - description
        - taskType
        - priority
        - status
        - dueAt
        - completedAt
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: Tarea manual de cobro (llamada, correo, visita, revisión, seguimiento) asignable a un operador. Las respuestas unitarias no usan el sobre `{ data }`.
    TaskCreateRequest:
      type: object
      properties:
        personId:
          type:
            - string
            - "null"
          format: uuid
          description: Cliente vinculado a la tarea.
        chargeId:
          type:
            - string
            - "null"
          format: uuid
          description: Cobro vinculado a la tarea.
        assignedToId:
          type:
            - string
            - "null"
          format: uuid
          description: Usuario responsable de la tarea.
        title:
          type: string
          minLength: 1
          maxLength: 255
          description: Título de la tarea.
        description:
          type:
            - string
            - "null"
          description: Descripción detallada.
        taskType:
          type: string
          enum:
            - call
            - email
            - visit
            - review
            - follow_up
          description: Tipo (`call` | `email` | `visit` | `review` | `follow_up`).
        priority:
          type: string
          enum:
            - low
            - medium
            - high
            - urgent
          default: medium
          description: Prioridad inicial (`low` | `medium` | `high` | `urgent`; por defecto `medium`).
        status:
          type: string
          enum:
            - pending
            - in_progress
            - completed
            - cancelled
          default: pending
          description: Estado inicial (por defecto `pending`).
        dueAt:
          type:
            - string
            - "null"
          format: date-time
          description: Plazo de la tarea (ISO 8601).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - title
        - taskType
      description: Datos para crear una tarea.
    TaskUpdateRequest:
      type: object
      properties:
        personId:
          type:
            - string
            - "null"
          format: uuid
          description: Cliente vinculado a la tarea.
        chargeId:
          type:
            - string
            - "null"
          format: uuid
          description: Cobro vinculado a la tarea.
        assignedToId:
          type:
            - string
            - "null"
          format: uuid
          description: Usuario responsable de la tarea.
        title:
          type: string
          minLength: 1
          maxLength: 255
          description: Título de la tarea.
        description:
          type:
            - string
            - "null"
          description: Descripción detallada.
        taskType:
          type: string
          enum:
            - call
            - email
            - visit
            - review
            - follow_up
          description: Tipo (`call` | `email` | `visit` | `review` | `follow_up`).
        priority:
          type: string
          enum:
            - low
            - medium
            - high
            - urgent
          description: Prioridad (`low` | `medium` | `high` | `urgent`).
        status:
          type: string
          enum:
            - pending
            - in_progress
            - completed
            - cancelled
          description: Nuevo estado; `completed` completa `completedAt` automáticamente.
        dueAt:
          type:
            - string
            - "null"
          format: date-time
          description: Plazo de la tarea (ISO 8601).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: Campos a actualizar (parcial — envía solo lo que cambia). Cambiar el estado a `completed` completa `completedAt`; salir de `completed` limpia el campo.
    TaskListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Task"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    InteractionPersonSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del cliente.
        documentNumber:
          type:
            - string
            - "null"
          description: CPF/CNPJ del cliente.
      required:
        - id
        - name
        - documentNumber
      description: Resumen del cliente de la interacción.
    InteractionUserSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del usuario.
        email:
          type: string
          format: email
          description: Correo del usuario.
        avatarUrl:
          type:
            - string
            - "null"
          description: URL del avatar del usuario.
      required:
        - id
        - name
        - email
        - avatarUrl
      description: Resumen del usuario autor.
    InteractionChargeSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        documentNumber:
          type:
            - string
            - "null"
          description: Número de documento del cobro.
        originalAmount:
          type: string
          description: Importe original del cobro (cadena decimal).
          example: "350.00"
        dueDate:
          type: string
          description: Vencimiento del cobro.
          example: 2026-08-01T00:00:00.000Z
        status:
          type: string
          description: Estado del cobro.
      required:
        - id
        - documentNumber
        - originalAmount
        - dueDate
        - status
      description: Resumen del cobro relacionado.
    Interaction:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) del registro.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio del registro.
        personId:
          type: string
          format: uuid
          description: Cliente de la interacción.
        chargeId:
          type:
            - string
            - "null"
          format: uuid
          description: Cobro relacionado con la interacción.
        userId:
          type:
            - string
            - "null"
          format: uuid
          description: Usuario que registró la interacción (inferido de la sesión/token).
        interactionType:
          type: string
          enum:
            - call
            - email
            - whatsapp
            - meeting
            - note
          description: Tipo (`call` | `email` | `whatsapp` | `meeting` | `note`).
        direction:
          type: string
          enum:
            - inbound
            - outbound
          description: Dirección (`inbound` = el cliente contactó, `outbound` = contactamos al cliente).
        summary:
          type:
            - string
            - "null"
          description: Resumen/notas de la interacción.
        outcome:
          type:
            - string
            - "null"
          enum:
            - promise_to_pay
            - negotiation
            - dispute
            - no_contact
            - callback_requested
            - payment_confirmed
            - null
          description: Resultado (`promise_to_pay` | `negotiation` | `dispute` | `no_contact` | `callback_requested` | `payment_confirmed`).
        promiseDate:
          type:
            - string
            - "null"
          description: Fecha prometida de pago.
          example: 2026-08-15T00:00:00.000Z
        promisedAmount:
          type:
            - string
            - "null"
          description: Importe prometido (cadena decimal).
          example: "200.00"
        contactedAt:
          type: string
          format: date-time
          description: Momento del contacto (ISO 8601).
        durationSeconds:
          type:
            - integer
            - "null"
          description: Duración del contacto en segundos.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres de la interacción.
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
        person:
          allOf:
            - $ref: "#/components/schemas/InteractionPersonSummary"
            - description: Cliente (presente en creación/consulta o con `include=person`).
        user:
          anyOf:
            - $ref: "#/components/schemas/InteractionUserSummary"
            - type: "null"
          description: Usuario autor (presente en creación/consulta o con `include=user`).
        charge:
          anyOf:
            - $ref: "#/components/schemas/InteractionChargeSummary"
            - type: "null"
          description: Cobro relacionado (presente en creación/consulta o con `include=charge`).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - personId
        - chargeId
        - userId
        - interactionType
        - direction
        - summary
        - outcome
        - promiseDate
        - promisedAmount
        - contactedAt
        - durationSeconds
        - metadata
        - externalId
        - customData
        - tags
        - createdAt
        - updatedAt
        - deletedAt
      description: Interacción con el cliente (llamada, correo, WhatsApp, reunión, nota) registrada en el flujo de cobro. Las respuestas unitarias no usan el sobre `{ data }`.
    InteractionCreateRequest:
      type: object
      properties:
        personId:
          type: string
          format: uuid
          description: Cliente de la interacción.
        chargeId:
          type:
            - string
            - "null"
          format: uuid
          description: Cobro relacionado con la interacción.
        interactionType:
          type: string
          enum:
            - call
            - email
            - whatsapp
            - meeting
            - note
          description: Tipo (`call` | `email` | `whatsapp` | `meeting` | `note`).
        direction:
          type: string
          enum:
            - inbound
            - outbound
          description: Dirección (`inbound` = el cliente contactó, `outbound` = contactamos al cliente).
        summary:
          type:
            - string
            - "null"
          description: Resumen/notas de la interacción.
        outcome:
          type:
            - string
            - "null"
          enum:
            - promise_to_pay
            - negotiation
            - dispute
            - no_contact
            - callback_requested
            - payment_confirmed
            - null
          description: Resultado (`promise_to_pay` | `negotiation` | `dispute` | `no_contact` | `callback_requested` | `payment_confirmed`).
        promiseDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha prometida de pago.
        promisedAmount:
          type:
            - number
            - "null"
          exclusiveMinimum: 0
          description: Importe prometido (número positivo).
          example: 200
        contactedAt:
          type: string
          format: date-time
          description: "Momento del contacto (ISO 8601; por defecto: ahora)."
        durationSeconds:
          type:
            - integer
            - "null"
          minimum: 0
          description: Duración del contacto en segundos.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres de la interacción.
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - personId
        - interactionType
        - direction
      description: Datos para registrar una interacción. El usuario autor se infiere de la sesión/token.
    InteractionUpdateRequest:
      type: object
      properties:
        summary:
          type:
            - string
            - "null"
          description: Resumen/notas de la interacción.
        notes:
          type:
            - string
            - "null"
          description: Alias de `summary` (se guarda en el mismo campo; si se envían ambos, `notes` prevalece).
        outcome:
          type:
            - string
            - "null"
          enum:
            - promise_to_pay
            - negotiation
            - dispute
            - no_contact
            - callback_requested
            - payment_confirmed
            - null
          description: Resultado (`promise_to_pay` | `negotiation` | `dispute` | `no_contact` | `callback_requested` | `payment_confirmed`).
        promiseDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha prometida de pago.
        promisedAmount:
          type:
            - number
            - "null"
          exclusiveMinimum: 0
          description: Importe prometido (número positivo).
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres de la interacción.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: "Campos editables tras el registro: resumen/notas, resultado, promesa y metadatos. Tipo, dirección y vínculos no cambian."
    InteractionListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Interaction"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    Notification:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        personId:
          type: string
          format: uuid
          description: Cliente destinatario.
        chargeId:
          type:
            - string
            - "null"
          format: uuid
          description: Cobro relacionado con la notificación.
        collectionRuleStepId:
          type:
            - string
            - "null"
          format: uuid
          description: Paso de la regla de cobro que originó la notificación.
        channel:
          type: string
          enum:
            - email
            - sms
            - whatsapp
            - voice
            - manual
          description: Canal (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        status:
          type: string
          enum:
            - pending
            - queued
            - sent
            - delivered
            - read
            - failed
            - bounced
          description: Estado del ciclo de vida (`pending` | `queued` | `sent` | `delivered` | `read` | `failed` | `bounced`).
        recipient:
          type: string
          description: "Destinatario: correo para el canal `email`, teléfono para `sms`/`whatsapp`/`voice`."
          example: maria@example.com
        subject:
          type:
            - string
            - "null"
          description: Asunto (para canales que lo admiten, p. ej., correo).
        body:
          type: string
          description: Cuerpo del mensaje.
        idempotencyKey:
          type:
            - string
            - "null"
          description: Clave de idempotencia del envío (generada por el motor de la regla).
        stepExecutionId:
          type:
            - string
            - "null"
          format: uuid
          description: Ejecución del paso de la regla que generó la notificación.
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema o en el proveedor de envío.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        sentAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento del envío (ISO 8601).
        deliveredAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de la entrega (ISO 8601).
        readAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de la lectura (ISO 8601).
        clickedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento del clic (ISO 8601).
        failedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento del fallo (ISO 8601).
        failureReason:
          type:
            - string
            - "null"
          description: Motivo del fallo reportado por el proveedor.
        cost:
          type:
            - string
            - "null"
          description: Costo del envío (cadena decimal).
          example: "0.0450"
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos escritos por el sistema (p. ej., `resendOf` en reenvíos).
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        person:
          type: object
          additionalProperties: {}
          description: Cliente completo (con `include=person`).
        charge:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Cobro completo (con `include=charge`).
        collectionRuleStep:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Paso de la regla con su plantilla de mensaje (con `include=step` o `include=template`).
      required:
        - id
        - publicId
        - organizationId
        - personId
        - chargeId
        - collectionRuleStepId
        - channel
        - status
        - recipient
        - subject
        - body
        - idempotencyKey
        - stepExecutionId
        - externalId
        - customData
        - tags
        - sentAt
        - deliveredAt
        - readAt
        - clickedAt
        - failedAt
        - failureReason
        - cost
        - metadata
        - createdAt
        - updatedAt
      description: Notificación enviada (o por enviar) a un cliente por correo, SMS, WhatsApp, voz o canal manual — en general disparada por un paso de la regla de cobro. Las respuestas unitarias no usan el sobre `{ data }`.
    NotificationCreateRequest:
      type: object
      properties:
        personId:
          type: string
          format: uuid
          description: Cliente destinatario.
        chargeId:
          type:
            - string
            - "null"
          format: uuid
          description: Cobro relacionado con la notificación.
        collectionRuleStepId:
          type:
            - string
            - "null"
          format: uuid
          description: Paso de la regla de cobro que originó la notificación.
        channel:
          type: string
          enum:
            - email
            - sms
            - whatsapp
            - voice
            - manual
          description: Canal (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        recipient:
          type: string
          minLength: 1
          description: Destinatario; validado según el canal (correo para `email`, teléfono para `sms`/`whatsapp`/`voice`).
        subject:
          type:
            - string
            - "null"
          maxLength: 500
          description: Asunto (para canales que lo admiten, p. ej., correo).
        body:
          type: string
          minLength: 1
          description: Cuerpo del mensaje.
        externalId:
          type:
            - string
            - "null"
          maxLength: 100
          description: Identificador en TU sistema o en el proveedor de envío.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos escritos por el sistema (p. ej., `resendOf` en reenvíos).
      required:
        - personId
        - channel
        - recipient
        - body
      description: Datos para crear una notificación manual (nace con estado `pending`).
    NotificationUpdateRequest:
      type: object
      properties:
        status:
          type: string
          enum:
            - pending
            - queued
            - sent
            - delivered
            - read
            - failed
            - bounced
          description: Nuevo estado; completa el timestamp correspondiente (sentAt/deliveredAt/readAt/failedAt) cuando no se informa.
        sentAt:
          type:
            - string
            - "null"
          description: Timestamp ISO 8601 o fecha `YYYY-MM-DD`; null limpia el campo.
        deliveredAt:
          type:
            - string
            - "null"
          description: Timestamp ISO 8601 o fecha `YYYY-MM-DD`; null limpia el campo.
        readAt:
          type:
            - string
            - "null"
          description: Timestamp ISO 8601 o fecha `YYYY-MM-DD`; null limpia el campo.
        failedAt:
          type:
            - string
            - "null"
          description: Timestamp ISO 8601 o fecha `YYYY-MM-DD`; null limpia el campo.
        failureReason:
          type:
            - string
            - "null"
          maxLength: 500
          description: Motivo del fallo reportado por el proveedor.
        externalId:
          type:
            - string
            - "null"
          maxLength: 100
          description: Identificador en TU sistema o en el proveedor de envío.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos escritos por el sistema (p. ej., `resendOf` en reenvíos).
        cost:
          type:
            - number
            - "null"
          minimum: 0
          description: Costo del envío (número, mínimo 0).
      description: Campos a actualizar (parcial). Cambiar el estado completa automáticamente el timestamp correspondiente cuando no se informa.
    NotificationListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Notification"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ClassificationCriteria:
      type: object
      properties:
        minOnTimePercentage:
          type: number
          minimum: 0
          maximum: 100
          description: Porcentaje mínimo de pagos a tiempo (0-100).
        maxOnTimePercentage:
          type: number
          minimum: 0
          maximum: 100
          description: Porcentaje máximo de pagos a tiempo (0-100).
        maxConsecutiveDelays:
          type: integer
          minimum: 0
          description: Máximo de atrasos consecutivos tolerado.
        maxCurrentOverdueDays:
          type: integer
          minimum: 0
          description: Máximo de días de atraso actual.
        evaluationPeriodMonths:
          type: integer
          minimum: 1
          description: Ventana de evaluación, en meses.
        minChargesCount:
          type: integer
          minimum: 0
          description: Mínimo de cobros en el período.
        maxChargesCount:
          type: integer
          minimum: 0
          description: Máximo de cobros en el período.
        hasNegativationHistory:
          type: boolean
          description: Exige (true) o veta (false) historial de negativación.
        hasProtestHistory:
          type: boolean
          description: Exige (true) o veta (false) historial de protesto.
      description: Criterios de asignación automática (comportamiento de pago).
    Classification:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) asociada.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio del registro.
        name:
          type: string
          description: Nombre de la clasificación.
          example: Bom pagador
        code:
          type: string
          description: Código estable (minúsculas, números y guion bajo; único en la organización).
          example: good_payer
        description:
          type:
            - string
            - "null"
          description: Descripción de la clasificación.
        color:
          type:
            - string
            - "null"
          description: "Color hexadecimal mostrado en la interfaz (ej.: `#22c55e`)."
          example: "#22c55e"
        priority:
          type: integer
          description: Prioridad de evaluación (menor se evalúa primero).
        isDefault:
          type: boolean
          description: true cuando es la clasificación predeterminada de la organización (única).
        autoAssign:
          type: boolean
          description: true cuando los clientes se asignan automáticamente por los criterios.
        criteria:
          allOf:
            - $ref: "#/components/schemas/ClassificationCriteria"
            - type:
                - object
                - "null"
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
        _count:
          type: object
          properties:
            people:
              type: integer
              description: Clientes con esta clasificación.
            collectionRules:
              type: integer
              description: Reglas de cobranza que usan esta clasificación.
          required:
            - people
            - collectionRules
          description: Contadores relacionados (presente con `include=_count` y en las respuestas de creación/actualización).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - name
        - code
        - description
        - color
        - priority
        - isDefault
        - autoAssign
        - criteria
        - activatedAt
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: "Clasificación de clientes: segmenta la cartera (ej.: buen pagador) y dirige la regla de cobranza aplicada."
    ClassificationCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la clasificación.
        code:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9_]+$
          description: Código estable (minúsculas, números y guion bajo; único en la organización).
          example: good_payer
        description:
          type:
            - string
            - "null"
          maxLength: 1000
          description: Descripción de la clasificación.
        color:
          type:
            - string
            - "null"
          pattern: ^#[0-9A-Fa-f]{6}$
          description: "Color hexadecimal mostrado en la interfaz (ej.: `#22c55e`)."
          example: "#22c55e"
        priority:
          type: integer
          minimum: 0
          description: Prioridad de evaluación (menor se evalúa primero).
        isDefault:
          type: boolean
          description: true cuando es la clasificación predeterminada de la organización (única).
        autoAssign:
          type: boolean
          description: true cuando los clientes se asignan automáticamente por los criterios.
        criteria:
          type:
            - object
            - "null"
          properties:
            minOnTimePercentage:
              type: number
              minimum: 0
              maximum: 100
              description: Porcentaje mínimo de pagos a tiempo (0-100).
            maxOnTimePercentage:
              type: number
              minimum: 0
              maximum: 100
              description: Porcentaje máximo de pagos a tiempo (0-100).
            maxConsecutiveDelays:
              type: integer
              minimum: 0
              description: Máximo de atrasos consecutivos tolerado.
            maxCurrentOverdueDays:
              type: integer
              minimum: 0
              description: Máximo de días de atraso actual.
            evaluationPeriodMonths:
              type: integer
              minimum: 1
              description: Ventana de evaluación, en meses.
            minChargesCount:
              type: integer
              minimum: 0
              description: Mínimo de cobros en el período.
            maxChargesCount:
              type: integer
              minimum: 0
              description: Máximo de cobros en el período.
            hasNegativationHistory:
              type: boolean
              description: Exige (true) o veta (false) historial de negativación.
            hasProtestHistory:
              type: boolean
              description: Exige (true) o veta (false) historial de protesto.
          description: Criterios de asignación automática (comportamiento de pago).
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - name
        - code
      description: Datos para crear una clasificación. `code` es único en la organización.
    ClassificationUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la clasificación.
        code:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9_]+$
          description: Código estable (minúsculas, números y guion bajo; único en la organización).
          example: good_payer
        description:
          type:
            - string
            - "null"
          maxLength: 1000
          description: Descripción de la clasificación.
        color:
          type:
            - string
            - "null"
          pattern: ^#[0-9A-Fa-f]{6}$
          description: "Color hexadecimal mostrado en la interfaz (ej.: `#22c55e`)."
          example: "#22c55e"
        priority:
          type: integer
          minimum: 0
          description: Prioridad de evaluación (menor se evalúa primero).
        isDefault:
          type: boolean
          description: true cuando es la clasificación predeterminada de la organización (única).
        autoAssign:
          type: boolean
          description: true cuando los clientes se asignan automáticamente por los criterios.
        criteria:
          type:
            - object
            - "null"
          properties:
            minOnTimePercentage:
              type: number
              minimum: 0
              maximum: 100
              description: Porcentaje mínimo de pagos a tiempo (0-100).
            maxOnTimePercentage:
              type: number
              minimum: 0
              maximum: 100
              description: Porcentaje máximo de pagos a tiempo (0-100).
            maxConsecutiveDelays:
              type: integer
              minimum: 0
              description: Máximo de atrasos consecutivos tolerado.
            maxCurrentOverdueDays:
              type: integer
              minimum: 0
              description: Máximo de días de atraso actual.
            evaluationPeriodMonths:
              type: integer
              minimum: 1
              description: Ventana de evaluación, en meses.
            minChargesCount:
              type: integer
              minimum: 0
              description: Mínimo de cobros en el período.
            maxChargesCount:
              type: integer
              minimum: 0
              description: Máximo de cobros en el período.
            hasNegativationHistory:
              type: boolean
              description: Exige (true) o veta (false) historial de negativación.
            hasProtestHistory:
              type: boolean
              description: Exige (true) o veta (false) historial de protesto.
          description: Criterios de asignación automática (comportamiento de pago).
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: Campos a actualizar (parcial — envía solo lo que cambia).
    ClassificationListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Classification"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    MessageTemplate:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) asociada.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio del registro.
        name:
          type: string
          description: Nombre de la plantilla (único en la organización).
          example: Lembrete 3 dias antes
        channel:
          type: string
          enum:
            - email
            - sms
            - whatsapp
            - voice
            - manual
          description: Canal de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        category:
          type: string
          enum:
            - reminder
            - overdue
            - negotiation
            - negativation_warning
            - protest_warning
            - payment_confirmation
          description: Categoría (`reminder` | `overdue` | `negotiation` | `negativation_warning` | `protest_warning` | `payment_confirmation`).
        tone:
          type:
            - string
            - "null"
          enum:
            - friendly
            - neutral
            - firm
            - urgent
            - null
          description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
        subject:
          type:
            - string
            - "null"
          description: Asunto (usado en correo).
        body:
          type: string
          description: Cuerpo del mensaje, con variables en formato `{{variable}}`.
        variables:
          type: array
          items:
            type: string
          description: Variables disponibles en el cuerpo/asunto.
          example:
            - customer_name
            - amount
            - due_date
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones; único en la organización).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - name
        - channel
        - category
        - tone
        - subject
        - body
        - variables
        - activatedAt
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: Plantilla de mensaje reutilizada por las etapas de la regla de cobranza.
    MessageTemplateCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la plantilla (único en la organización).
        channel:
          type: string
          enum:
            - email
            - sms
            - whatsapp
            - voice
            - manual
          description: Canal de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        category:
          type: string
          enum:
            - reminder
            - overdue
            - negotiation
            - negativation_warning
            - protest_warning
            - payment_confirmation
          description: Categoría (`reminder` | `overdue` | `negotiation` | `negativation_warning` | `protest_warning` | `payment_confirmation`).
        tone:
          type:
            - string
            - "null"
          enum:
            - friendly
            - neutral
            - firm
            - urgent
            - null
          description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
        subject:
          type:
            - string
            - "null"
          maxLength: 255
          description: Asunto (usado en correo).
        body:
          type: string
          minLength: 1
          description: Cuerpo del mensaje, con variables en formato `{{variable}}`.
        variables:
          type: array
          items:
            type: string
          description: Variables disponibles en el cuerpo/asunto.
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones; único en la organización).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - name
        - channel
        - category
        - body
      description: Datos para crear una plantilla. `name` y `externalId` son únicos en la organización.
    MessageTemplateUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la plantilla (único en la organización).
        channel:
          type: string
          enum:
            - email
            - sms
            - whatsapp
            - voice
            - manual
          description: Canal de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        category:
          type: string
          enum:
            - reminder
            - overdue
            - negotiation
            - negativation_warning
            - protest_warning
            - payment_confirmation
          description: Categoría (`reminder` | `overdue` | `negotiation` | `negativation_warning` | `protest_warning` | `payment_confirmation`).
        tone:
          type:
            - string
            - "null"
          enum:
            - friendly
            - neutral
            - firm
            - urgent
            - null
          description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
        subject:
          type:
            - string
            - "null"
          maxLength: 255
          description: Asunto (usado en correo).
        body:
          type: string
          minLength: 1
          description: Cuerpo del mensaje, con variables en formato `{{variable}}`.
        variables:
          type: array
          items:
            type: string
          description: Variables disponibles en el cuerpo/asunto.
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones; único en la organización).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: Campos a actualizar (parcial — envía solo lo que cambia).
    MessageTemplateDuplicateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la plantilla (único en la organización).
      required:
        - name
      description: Nombre de la nueva plantilla generada por la duplicación (único en la organización).
    MessageTemplateListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/MessageTemplate"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CollectionRuleStep:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        collectionRuleId:
          type: string
          format: uuid
          description: Regla de cobranza dueña de la etapa.
        messageTemplateId:
          type:
            - string
            - "null"
          format: uuid
          description: Plantilla de mensaje usada por la etapa (debe pertenecer a la organización).
        position:
          type: integer
          description: Posición de la etapa en la regla (desde 1).
          example: 1
        name:
          type: string
          description: Nombre de la etapa.
          example: Lembrete 3 dias antes
        triggerType:
          type: string
          enum:
            - before_due
            - on_due
            - after_due
            - after_issue
            - after_payment
          description: Disparador (`before_due` | `on_due` | `after_due` | `after_issue` | `after_payment`).
        triggerDays:
          type: integer
          description: Días relativos al disparador (0 = el mismo día).
          example: 3
        actionType:
          type: string
          enum:
            - notification
            - negativation
            - protest
            - manual_task
            - negotiation_offer
            - judicial
          description: Acción (`notification` | `negativation` | `protest` | `manual_task` | `negotiation_offer` | `judicial`).
        channels:
          type: array
          items:
            type: string
            enum:
              - email
              - sms
              - whatsapp
              - voice
              - manual
          description: Canales de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        tone:
          type:
            - string
            - "null"
          enum:
            - friendly
            - neutral
            - firm
            - urgent
            - null
          description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
        preferredTime:
          type:
            - string
            - "null"
          description: "Horario preferido de envío (ej.: `09:00`)."
          example: 09:00
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
        messageTemplate:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
              description: Identificador (UUID) del recurso.
            publicId:
              type: string
              description: Identificador público estable (visible en la interfaz).
            organizationId:
              type: string
              format: uuid
              description: Organización dueña del registro.
            workspaceId:
              type:
                - string
                - "null"
              format: uuid
              description: Workspace del registro (jerarquía multi-tenant).
            companyId:
              type:
                - string
                - "null"
              format: uuid
              description: Empresa (acreedora) asociada.
            businessUnitId:
              type:
                - string
                - "null"
              format: uuid
              description: Unidad de negocio del registro.
            name:
              type: string
              description: Nombre de la plantilla (único en la organización).
              example: Lembrete 3 dias antes
            channel:
              type: string
              enum:
                - email
                - sms
                - whatsapp
                - voice
                - manual
              description: Canal de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
            category:
              type: string
              enum:
                - reminder
                - overdue
                - negotiation
                - negativation_warning
                - protest_warning
                - payment_confirmation
              description: Categoría (`reminder` | `overdue` | `negotiation` | `negativation_warning` | `protest_warning` | `payment_confirmation`).
            tone:
              type:
                - string
                - "null"
              enum:
                - friendly
                - neutral
                - firm
                - urgent
                - null
              description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
            subject:
              type:
                - string
                - "null"
              description: Asunto (usado en correo).
            body:
              type: string
              description: Cuerpo del mensaje, con variables en formato `{{variable}}`.
            variables:
              type: array
              items:
                type: string
              description: Variables disponibles en el cuerpo/asunto.
              example:
                - customer_name
                - amount
                - due_date
            activatedAt:
              type:
                - string
                - "null"
              format: date-time
              description: Momento de activación (null = inactiva).
            externalId:
              type:
                - string
                - "null"
              description: Identificador en TU sistema (clave de las integraciones; único en la organización).
            customData:
              type:
                - object
                - "null"
              additionalProperties: {}
              description: Metadatos personalizables por el consumidor de la API.
            tags:
              type: array
              items:
                type: string
              description: Etiquetas libres.
            metadata:
              type: object
              additionalProperties: {}
              description: Metadatos escritos por el sistema.
            createdAt:
              type: string
              format: date-time
              description: Creado el (ISO 8601).
            updatedAt:
              type: string
              format: date-time
              description: Actualizado el (ISO 8601).
            deletedAt:
              type:
                - string
                - "null"
              format: date-time
              description: Eliminado el (borrado lógico; null mientras activo).
          required:
            - id
            - publicId
            - organizationId
            - workspaceId
            - companyId
            - businessUnitId
            - name
            - channel
            - category
            - tone
            - subject
            - body
            - variables
            - activatedAt
            - externalId
            - customData
            - tags
            - metadata
            - createdAt
            - updatedAt
            - deletedAt
          description: Plantilla de mensaje incluida (presente en los endpoints de etapas).
      required:
        - id
        - publicId
        - collectionRuleId
        - messageTemplateId
        - position
        - name
        - triggerType
        - triggerDays
        - actionType
        - channels
        - tone
        - preferredTime
        - activatedAt
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: "Etapa de la regla: acción disparada según el vencimiento (notificación, negativación, protesto, etc.)."
    CollectionRule:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) asociada.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio del registro.
        classificationId:
          type:
            - string
            - "null"
          format: uuid
          description: Clasificación de clientes atendida por la regla (segmentación).
        name:
          type: string
          description: Nombre de la regla.
          example: Régua padrão
        description:
          type:
            - string
            - "null"
          description: Descripción de la regla.
        isDefault:
          type: boolean
          description: true cuando es la regla predeterminada de la organización (única).
        priority:
          type: integer
          description: Prioridad en la selección de regla (mayor gana).
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
        classification:
          allOf:
            - $ref: "#/components/schemas/Classification"
            - type:
                - object
                - "null"
              description: Clasificación asociada (objeto completo; null cuando no está segmentada).
        steps:
          type: array
          items:
            $ref: "#/components/schemas/CollectionRuleStep"
          description: Etapas de la regla ordenadas por `position` (presente con `include=steps` y en las respuestas de creación/actualización).
        _count:
          type: object
          properties:
            charges:
              type: integer
              description: Cobros activos en la regla (estado pending, overdue o negotiated).
          required:
            - charges
          description: Contadores relacionados.
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - classificationId
        - name
        - description
        - isDefault
        - priority
        - activatedAt
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
        - classification
      description: "Regla de cobranza: secuencia de etapas automatizadas aplicada a los cobros de la cartera."
    CollectionRuleStepInput:
      type: object
      properties:
        messageTemplateId:
          type:
            - string
            - "null"
          format: uuid
          description: Plantilla de mensaje usada por la etapa (debe pertenecer a la organización).
        position:
          type: integer
          exclusiveMinimum: 0
          description: Posición de la etapa en la regla (desde 1).
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la etapa.
        triggerType:
          type: string
          enum:
            - before_due
            - on_due
            - after_due
            - after_issue
            - after_payment
          description: Disparador (`before_due` | `on_due` | `after_due` | `after_issue` | `after_payment`).
        triggerDays:
          type: integer
          minimum: 0
          description: Días relativos al disparador (0 = el mismo día).
        actionType:
          type: string
          enum:
            - notification
            - negativation
            - protest
            - manual_task
            - negotiation_offer
            - judicial
          description: Acción (`notification` | `negativation` | `protest` | `manual_task` | `negotiation_offer` | `judicial`).
        channels:
          type: array
          items:
            type: string
            enum:
              - email
              - sms
              - whatsapp
              - voice
              - manual
          minItems: 1
          description: Canales de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        tone:
          type:
            - string
            - "null"
          enum:
            - friendly
            - neutral
            - firm
            - urgent
            - null
          description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
        preferredTime:
          type:
            - string
            - "null"
          maxLength: 10
          description: "Horario preferido de envío (ej.: `09:00`)."
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - name
        - triggerType
        - triggerDays
        - actionType
        - channels
      description: Etapa creada junto con la regla. Sin `position`, las etapas se numeran en el orden del array.
    CollectionRuleStepUpsert:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la etapa.
        position:
          type: integer
          minimum: 1
          description: Posición de la etapa en la regla (desde 1).
        triggerType:
          type: string
          enum:
            - before_due
            - on_due
            - after_due
            - after_issue
            - after_payment
          description: Disparador (`before_due` | `on_due` | `after_due` | `after_issue` | `after_payment`).
        triggerDays:
          type: integer
          minimum: 0
          description: Días relativos al disparador (0 = el mismo día).
        actionType:
          type: string
          enum:
            - notification
            - negativation
            - protest
            - manual_task
            - negotiation_offer
            - judicial
          description: Acción (`notification` | `negativation` | `protest` | `manual_task` | `negotiation_offer` | `judicial`).
        channels:
          type: array
          items:
            type: string
            enum:
              - email
              - sms
              - whatsapp
              - voice
              - manual
          description: Canales de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        messageTemplateId:
          type:
            - string
            - "null"
          format: uuid
          description: Plantilla de mensaje usada por la etapa (debe pertenecer a la organización).
        tone:
          type:
            - string
            - "null"
          enum:
            - friendly
            - neutral
            - firm
            - urgent
            - null
          description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
        preferredTime:
          type:
            - string
            - "null"
          description: "Horario preferido de envío (ej.: `09:00`)."
      required:
        - name
        - position
        - triggerType
        - triggerDays
        - actionType
      description: "Etapa en el PUT de la regla: con `id` actualiza la existente; sin `id` crea; las etapas ausentes del array se eliminan (borrado lógico)."
    CollectionRuleCreateRequest:
      type: object
      properties:
        classificationId:
          type:
            - string
            - "null"
          format: uuid
          description: Clasificación de clientes atendida por la regla (segmentación).
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la regla.
        description:
          type:
            - string
            - "null"
          maxLength: 1000
          description: Descripción de la regla.
        isDefault:
          type: boolean
          description: true cuando es la regla predeterminada de la organización (única).
        priority:
          type: integer
          minimum: 0
          description: Prioridad en la selección de regla (mayor gana).
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        steps:
          type: array
          items:
            $ref: "#/components/schemas/CollectionRuleStepInput"
          description: Etapas de la regla ordenadas por `position` (presente con `include=steps` y en las respuestas de creación/actualización).
      required:
        - name
      description: Datos para crear una regla de cobranza, opcionalmente con etapas inline.
    CollectionRuleUpdateRequest:
      type: object
      properties:
        classificationId:
          type:
            - string
            - "null"
          format: uuid
          description: Clasificación de clientes atendida por la regla (segmentación).
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la regla.
        description:
          type:
            - string
            - "null"
          maxLength: 1000
          description: Descripción de la regla.
        isDefault:
          type: boolean
          description: true cuando es la regla predeterminada de la organización (única).
        priority:
          type: integer
          minimum: 0
          description: Prioridad en la selección de regla (mayor gana).
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        steps:
          type: array
          items:
            $ref: "#/components/schemas/CollectionRuleStepUpsert"
          description: "Conjunto completo de etapas (upsert): con `id` actualiza, sin `id` crea, las ausentes se eliminan."
      description: Campos a actualizar (parcial). Si envías `steps`, el array reemplaza el conjunto de etapas (upsert).
    CollectionRuleStepCreateRequest:
      allOf:
        - $ref: "#/components/schemas/CollectionRuleStepInput"
        - type: object
          properties:
            triggerType:
              type: string
              enum:
                - before_due
                - on_due
                - after_due
              description: Disparador (`before_due` | `on_due` | `after_due`). Los disparadores `after_issue`/`after_payment` solo se definen vía la regla.
      description: Datos para crear una etapa. Este endpoint solo acepta disparadores relativos al vencimiento (`before_due` | `on_due` | `after_due`).
    CollectionRuleStepUpdateRequest:
      type: object
      properties:
        messageTemplateId:
          type:
            - string
            - "null"
          format: uuid
          description: Plantilla de mensaje usada por la etapa (debe pertenecer a la organización).
        position:
          type: integer
          exclusiveMinimum: 0
          description: Posición de la etapa en la regla (desde 1).
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la etapa.
        triggerType:
          type: string
          enum:
            - before_due
            - on_due
            - after_due
          description: Disparador (`before_due` | `on_due` | `after_due`). Los disparadores `after_issue`/`after_payment` solo se definen vía la regla.
        triggerDays:
          type: integer
          minimum: 0
          description: Días relativos al disparador (0 = el mismo día).
        actionType:
          type: string
          enum:
            - notification
            - negativation
            - protest
            - manual_task
            - negotiation_offer
            - judicial
          description: Acción (`notification` | `negativation` | `protest` | `manual_task` | `negotiation_offer` | `judicial`).
        channels:
          type: array
          items:
            type: string
            enum:
              - email
              - sms
              - whatsapp
              - voice
              - manual
          minItems: 1
          description: Canales de envío (`email` | `sms` | `whatsapp` | `voice` | `manual`).
        tone:
          type:
            - string
            - "null"
          enum:
            - friendly
            - neutral
            - firm
            - urgent
            - null
          description: Tono del mensaje (`friendly` | `neutral` | `firm` | `urgent`).
        preferredTime:
          type:
            - string
            - "null"
          maxLength: 10
          description: "Horario preferido de envío (ej.: `09:00`)."
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = inactiva).
        externalId:
          type:
            - string
            - "null"
          maxLength: 60
          description: Identificador en TU sistema (clave de las integraciones).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: Campos de la etapa a actualizar (parcial — envía solo lo que cambia).
    CollectionRuleListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CollectionRule"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CollectionRuleShowResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/CollectionRule"
      required:
        - data
    CollectionRuleStepListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CollectionRuleStep"
      required:
        - data
    NegotiationCampaign:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        name:
          type: string
          description: Nombre de la campaña.
          example: 30% à vista · 30-60 dias
        discountPercentage:
          type:
            - string
            - "null"
          description: Porcentaje de descuento (0-100). Devuelto como string decimal.
          example: "30"
        maxInstallments:
          type:
            - integer
            - "null"
          description: Máximo de cuotas permitido (1 = solo pago único).
          example: 1
        minDaysOverdue:
          type:
            - integer
            - "null"
          description: Inicio del rango de días de atraso en que la campaña aplica (null = sin mínimo).
          example: 30
        maxDaysOverdue:
          type:
            - integer
            - "null"
          description: Fin del rango de días de atraso en que la campaña aplica (null = sin máximo).
          example: 60
        validUntil:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha límite de validez de la campaña (null = sin plazo).
        terms:
          type:
            - string
            - "null"
          description: Condiciones mostradas al deudor.
        activatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Momento de activación (null = campaña finalizada).
      required:
        - id
        - publicId
        - name
        - discountPercentage
        - maxInstallments
        - minDaysOverdue
        - maxDaysOverdue
        - validUntil
        - terms
        - activatedAt
      description: "Campaña de negociación: oferta de descuento reutilizable, válida para toda la organización y segmentada por rango de días de atraso. El portal del deudor ofrece automáticamente la campaña activa aplicable."
    NegotiationCampaignDetail:
      allOf:
        - $ref: "#/components/schemas/NegotiationCampaign"
        - type: object
          properties:
            organizationId:
              type: string
              format: uuid
              description: Organización dueña del registro.
            workspaceId:
              type:
                - string
                - "null"
              format: uuid
              description: Workspace del registro (jerarquía multi-tenant).
            companyId:
              type:
                - string
                - "null"
              format: uuid
              description: Empresa (acreedora) asociada.
            businessUnitId:
              type:
                - string
                - "null"
              format: uuid
              description: Unidad de negocio del registro.
            chargeId:
              type:
                - string
                - "null"
              format: uuid
              description: Cobro vinculado. Siempre null en campañas (la oferta es org-wide).
            offerType:
              type: string
              enum:
                - discount
                - installment
                - extension
              description: Tipo de oferta (`discount` | `installment` | `extension`). Las campañas creadas por la API usan `discount`.
            minimumEntryPercentage:
              type:
                - string
                - "null"
              description: Porcentaje mínimo de entrada (string decimal; null cuando no aplica).
            externalId:
              type:
                - string
                - "null"
              description: Identificador en TU sistema (clave de las integraciones).
            customData:
              type:
                - object
                - "null"
              additionalProperties: {}
              description: Metadatos personalizables por el consumidor de la API.
            tags:
              type: array
              items:
                type: string
              description: Etiquetas libres.
            metadata:
              type: object
              additionalProperties: {}
              description: Metadatos escritos por el sistema.
            createdAt:
              type: string
              format: date-time
              description: Creado el (ISO 8601).
            updatedAt:
              type: string
              format: date-time
              description: Actualizado el (ISO 8601).
            deletedAt:
              type:
                - string
                - "null"
              format: date-time
              description: Eliminado el (borrado lógico; null mientras activo).
          required:
            - organizationId
            - workspaceId
            - companyId
            - businessUnitId
            - chargeId
            - offerType
            - minimumEntryPercentage
            - externalId
            - customData
            - tags
            - metadata
            - createdAt
            - updatedAt
            - deletedAt
      description: Registro completo de la campaña devuelto en la creación (modelo entero de oferta de negociación).
    NegotiationCampaignCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 2
          maxLength: 120
          description: Nombre de la campaña.
        discountPercentage:
          type: number
          minimum: 0
          maximum: 100
          description: Porcentaje de descuento (0-100). Devuelto como string decimal.
          example: 30
        maxInstallments:
          type: integer
          minimum: 1
          maximum: 36
          description: Máximo de cuotas permitido (1 = solo pago único).
          example: 1
        minDaysOverdue:
          type:
            - integer
            - "null"
          minimum: 0
          description: Inicio del rango de días de atraso en que la campaña aplica (null = sin mínimo).
        maxDaysOverdue:
          type:
            - integer
            - "null"
          minimum: 0
          description: Fin del rango de días de atraso en que la campaña aplica (null = sin máximo).
        validUntil:
          type:
            - string
            - "null"
          format: date
          description: Fecha límite de validez de la campaña (null = sin plazo).
          example: 2026-12-31
        terms:
          type:
            - string
            - "null"
          maxLength: 500
          description: Condiciones mostradas al deudor.
      required:
        - name
        - discountPercentage
      description: Datos para crear la campaña. Nace activa; `minDaysOverdue` no puede ser mayor que `maxDaysOverdue`.
    NegotiationCampaignListResponse:
      type: object
      properties:
        campaigns:
          type: array
          items:
            $ref: "#/components/schemas/NegotiationCampaign"
      required:
        - campaigns
    NegotiationCampaignCreateResponse:
      type: object
      properties:
        campaign:
          $ref: "#/components/schemas/NegotiationCampaignDetail"
      required:
        - campaign
    NegativationPersonSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del cliente.
        documentNumber:
          type:
            - string
            - "null"
          description: CPF/CNPJ del cliente (solo dígitos).
        documentType:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Tipo de documento (`cpf` | `cnpj`).
      required:
        - id
        - name
        - documentNumber
        - documentType
      description: Resumen del cliente (presente con `include=person` y en las respuestas de creación/consulta).
    NegativationChargeSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        documentNumber:
          type:
            - string
            - "null"
          description: Número de documento del cobro.
        description:
          type:
            - string
            - "null"
          description: Descripción del cobro.
        originalAmount:
          type: string
          description: Monto original del cobro (decimal como string).
          example: "150.00"
        currentAmount:
          type:
            - string
            - "null"
          description: Monto actualizado del cobro (decimal como string).
          example: "175.50"
        dueDate:
          type: string
          format: date-time
          description: Vencimiento del cobro (ISO 8601).
        status:
          type: string
          description: Estado del cobro.
          example: overdue
      required:
        - id
        - documentNumber
        - description
        - originalAmount
        - currentAmount
        - dueDate
        - status
      description: Resumen del cobro (presente con `include=charge` y en las respuestas de creación/consulta).
    Negativation:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) del registro.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio (BU) del registro.
        chargeId:
          type: string
          format: uuid
          description: Cobro negativado (UUID).
        personId:
          type: string
          format: uuid
          description: Cliente negativado (UUID).
        bureau:
          type: string
          enum:
            - serasa
            - spc
            - boa_vista
          description: Buró de crédito (`serasa` | `spc` | `boa_vista`).
        status:
          type: string
          enum:
            - pending
            - active
            - removed
            - failed
          description: Estado (`pending` = esperando revisión humana obligatoria, `active` = efectivada en el buró, `removed` = dada de baja, `failed` = falló).
        amount:
          type: string
          description: Monto negativado (decimal serializado como string).
          example: "150.00"
        registeredAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha en que la negativación se efectivó en el buró (ISO 8601).
        removedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de la baja en el buró (ISO 8601).
        removalReason:
          type:
            - string
            - "null"
          description: Motivo de la baja de la negativación.
        responseData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Respuesta cruda del buró (payload de la integración).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema o protocolo en el buró.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        person:
          $ref: "#/components/schemas/NegativationPersonSummary"
        charge:
          $ref: "#/components/schemas/NegativationChargeSummary"
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - chargeId
        - personId
        - bureau
        - status
        - amount
        - registeredAt
        - removedAt
        - removalReason
        - responseData
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: "Registro de negativación de un cobro en un buró de crédito (Serasa, SPC o Boa Vista). Flujo legal: todo registro nace `pending` y pasa por revisión humana obligatoria antes de efectivizarse en el buró (evento `negativation.review_required`)."
    NegativationCreateRequest:
      type: object
      properties:
        personId:
          type: string
          format: uuid
          description: Cliente negativado (UUID).
        chargeId:
          type: string
          format: uuid
          description: Cobro negativado (UUID).
        bureau:
          type: string
          enum:
            - serasa
            - spc
            - boa_vista
          description: Buró de crédito (`serasa` | `spc` | `boa_vista`).
        amount:
          type: number
          exclusiveMinimum: 0
          description: Monto a negativar (número decimal positivo).
          example: 150
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema o protocolo en el buró.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - personId
        - chargeId
        - bureau
        - amount
      description: Datos para registrar una negativación. El cobro debe pertenecer al cliente indicado.
    NegativationUpdateRequest:
      type: object
      properties:
        status:
          type: string
          enum:
            - pending
            - active
            - removed
            - failed
          description: Estado (`pending` = esperando revisión humana obligatoria, `active` = efectivada en el buró, `removed` = dada de baja, `failed` = falló).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema o protocolo en el buró.
        registeredAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha en que la negativación se efectivó en el buró (ISO 8601).
        removedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de la baja en el buró (ISO 8601).
        removalReason:
          type:
            - string
            - "null"
          description: Motivo de la baja de la negativación.
        responseData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Respuesta cruda del buró (payload de la integración).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: Campos a actualizar (parcial). Lo usa la revisión humana para aprobar el registro y las integraciones para guardar la respuesta del buró.
    NegativationRemoveRequest:
      type: object
      properties:
        removalReason:
          type: string
          description: Motivo de la baja de la negativación.
      description: Cuerpo opcional con el motivo de la baja de la negativación.
    NegativationListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Negativation"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ProtestPersonSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type: string
          description: Nombre del cliente.
        documentNumber:
          type:
            - string
            - "null"
          description: CPF/CNPJ del cliente (solo dígitos).
        documentType:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Tipo de documento (`cpf` | `cnpj`).
      required:
        - id
        - name
        - documentNumber
        - documentType
      description: Resumen del cliente (presente con `include=person` y en las respuestas de creación/consulta).
    ProtestChargeSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        documentNumber:
          type:
            - string
            - "null"
          description: Número de documento del cobro.
        description:
          type:
            - string
            - "null"
          description: Descripción del cobro.
        originalAmount:
          type: string
          description: Monto original del cobro (decimal como string).
          example: "150.00"
        currentAmount:
          type:
            - string
            - "null"
          description: Monto actualizado del cobro (decimal como string).
          example: "175.50"
        dueDate:
          type: string
          format: date-time
          description: Vencimiento del cobro (ISO 8601).
        status:
          type: string
          description: Estado del cobro.
          example: overdue
      required:
        - id
        - documentNumber
        - description
        - originalAmount
        - currentAmount
        - dueDate
        - status
      description: Resumen del cobro (presente con `include=charge` y en las respuestas de creación/consulta).
    Protest:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del registro.
        workspaceId:
          type:
            - string
            - "null"
          format: uuid
          description: Workspace del registro (jerarquía multi-tenant).
        companyId:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa (acreedora) del registro.
        businessUnitId:
          type:
            - string
            - "null"
          format: uuid
          description: Unidad de negocio (BU) del registro.
        chargeId:
          type: string
          format: uuid
          description: Cobro protestado (UUID).
        personId:
          type: string
          format: uuid
          description: Cliente protestado (UUID).
        notary:
          type:
            - string
            - "null"
          description: Nombre de la notaría de protesto.
        notaryCode:
          type:
            - string
            - "null"
          description: Código/identificador de la notaría.
        status:
          type: string
          enum:
            - pending
            - sent
            - intimated
            - protested
            - paid
            - cancelled
          description: Estado (`pending` = esperando revisión humana obligatoria, `sent` = enviado a la notaría, `intimated` = deudor intimado, `protested` = protestado, `paid` = pagado, `cancelled` = cancelado).
        amount:
          type: string
          description: Monto protestado (decimal serializado como string).
          example: "150.00"
        fees:
          type:
            - string
            - "null"
          description: Aranceles/costas de la notaría (decimal como string).
          example: "12.50"
        sentAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de envío a la notaría (ISO 8601).
        intimationDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de intimación del deudor (ISO 8601).
        protestDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de registro del protesto (ISO 8601).
        paymentDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de pago en la notaría (ISO 8601).
        cancellationDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de cancelación del protesto (ISO 8601).
        responseData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Respuesta cruda de la notaría (payload de la integración).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema o protocolo en la notaría.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        person:
          $ref: "#/components/schemas/ProtestPersonSummary"
        charge:
          $ref: "#/components/schemas/ProtestChargeSummary"
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
      required:
        - id
        - publicId
        - organizationId
        - workspaceId
        - companyId
        - businessUnitId
        - chargeId
        - personId
        - notary
        - notaryCode
        - status
        - amount
        - fees
        - sentAt
        - intimationDate
        - protestDate
        - paymentDate
        - cancellationDate
        - responseData
        - externalId
        - customData
        - tags
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: "Registro de protesto de un cobro en notaría. Flujo legal: todo registro nace `pending` y pasa por revisión humana obligatoria antes del envío a la notaría (evento `protest.review_required`)."
    ProtestCreateRequest:
      type: object
      properties:
        personId:
          type: string
          format: uuid
          description: Cliente protestado (UUID).
        chargeId:
          type: string
          format: uuid
          description: Cobro protestado (UUID).
        amount:
          type: number
          exclusiveMinimum: 0
          description: Monto a protestar (número decimal positivo).
          example: 150
        notary:
          type:
            - string
            - "null"
          description: Nombre de la notaría de protesto.
        notaryCode:
          type:
            - string
            - "null"
          description: Código/identificador de la notaría.
        fees:
          type:
            - number
            - "null"
          description: Aranceles/costas de la notaría (número decimal).
          example: 12.5
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema o protocolo en la notaría.
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      required:
        - personId
        - chargeId
        - amount
      description: Datos para registrar un protesto. El cobro debe pertenecer al cliente indicado.
    ProtestUpdateRequest:
      type: object
      properties:
        status:
          type: string
          enum:
            - pending
            - sent
            - intimated
            - protested
            - paid
            - cancelled
          description: Estado (`pending` = esperando revisión humana obligatoria, `sent` = enviado a la notaría, `intimated` = deudor intimado, `protested` = protestado, `paid` = pagado, `cancelled` = cancelado).
        notary:
          type:
            - string
            - "null"
          description: Nombre de la notaría de protesto.
        notaryCode:
          type:
            - string
            - "null"
          description: Código/identificador de la notaría.
        fees:
          type:
            - number
            - "null"
          description: Aranceles/costas de la notaría (número decimal).
        externalId:
          type:
            - string
            - "null"
          description: Identificador en TU sistema o protocolo en la notaría.
        sentAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de envío a la notaría (ISO 8601).
        intimationDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de intimación del deudor (ISO 8601).
        protestDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de registro del protesto (ISO 8601).
        paymentDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de pago en la notaría (ISO 8601).
        cancellationDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de cancelación del protesto (ISO 8601).
        responseData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Respuesta cruda de la notaría (payload de la integración).
        customData:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizables por el consumidor de la API.
        tags:
          type: array
          items:
            type: string
          description: Etiquetas libres.
      description: Campos a actualizar (parcial). Lo usa la revisión humana para aprobar/avanzar el registro y las integraciones para guardar fechas y respuesta de la notaría.
    ProtestCancelRequest:
      type: object
      properties:
        cancellationReason:
          type: string
          description: Motivo de la cancelación.
      description: Cuerpo opcional con el motivo de la cancelación (guardado en `responseData.cancellationReason`).
    ProtestListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Protest"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ImportRowError:
      type: object
      properties:
        line:
          type: integer
          description: Número de línea en el archivo (la línea 1 es el encabezado; los datos empiezan en la 2).
          example: 2
        error:
          type: string
          description: Motivo del rechazo de la línea.
          example: Nome obrigatório
      required:
        - line
        - error
      description: Error de una línea rechazada de la planilla.
    ImportSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        resourceType:
          type: string
          enum:
            - people
            - charges
          description: Tipo de recurso importado (`people` = clientes | `charges` = cobros).
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          description: Estado del procesamiento (`pending` | `processing` | `completed` | `failed`).
        fileName:
          type:
            - string
            - "null"
          description: Nombre del archivo enviado.
        totalRows:
          type:
            - integer
            - "null"
          description: Total de líneas de datos del archivo (null hasta que inicia el procesamiento).
        processedRows:
          type: integer
          description: Líneas ya procesadas.
        createdCount:
          type: integer
          description: Registros creados.
        updatedCount:
          type: integer
          description: Registros actualizados (deduplicación por `external_id`/documento).
        errorCount:
          type: integer
          description: Líneas rechazadas.
        errorMessage:
          type:
            - string
            - "null"
          description: Mensaje de error cuando falla el lote completo.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        completedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fin del procesamiento (ISO 8601).
      required:
        - id
        - publicId
        - resourceType
        - status
        - fileName
        - totalRows
        - processedRows
        - createdCount
        - updatedCount
        - errorCount
        - errorMessage
        - createdAt
        - completedAt
      description: Resumen de una importación (el listado no incluye el contenido del archivo ni los errores por línea).
    Import:
      allOf:
        - $ref: "#/components/schemas/ImportSummary"
        - type: object
          properties:
            errors:
              type: array
              items:
                $ref: "#/components/schemas/ImportRowError"
              description: Errores por línea rechazada (`[{ line, error }]`).
            startedAt:
              type:
                - string
                - "null"
              format: date-time
              description: Inicio del procesamiento (ISO 8601).
          required:
            - errors
            - startedAt
      description: Importación de planilla CSV (clientes o cobros), incluyendo los errores por línea rechazada.
    ImportCreateRequest:
      type: object
      properties:
        resourceType:
          type: string
          enum:
            - people
            - charges
          description: Tipo de recurso importado (`people` = clientes | `charges` = cobros).
        fileName:
          type: string
          maxLength: 200
          description: Nombre del archivo enviado.
          example: clientes.csv
        content:
          type: string
          minLength: 10
          description: Contenido del CSV como texto plano (UTF-8), incluyendo la línea de encabezado. Máx. 2 MB.
          example: |-
            nome,documento,email
            Maria da Silva,12345678901,maria@example.com
      required:
        - resourceType
        - content
      description: "Carga en JSON: el contenido del CSV va como TEXTO plano en el campo `content` (no es multipart ni base64). Límite de 2 MB (~10-20 mil líneas); por encima la API responde 413."
    ImportCreated:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        resourceType:
          type: string
          enum:
            - people
            - charges
          description: Tipo de recurso importado (`people` = clientes | `charges` = cobros).
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          description: Estado del procesamiento (`pending` | `processing` | `completed` | `failed`).
        fileName:
          type:
            - string
            - "null"
          description: Nombre del archivo enviado.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
      required:
        - id
        - publicId
        - resourceType
        - status
        - fileName
        - createdAt
      description: Importación recién creada (encolada para procesamiento).
    ImportListResponse:
      type: object
      properties:
        imports:
          type: array
          items:
            $ref: "#/components/schemas/ImportSummary"
      required:
        - imports
      description: Envoltura `{ imports }` con las importaciones más recientes.
    ImportShowResponse:
      type: object
      properties:
        import:
          $ref: "#/components/schemas/Import"
      required:
        - import
      description: Envoltura `{ import }` con el detalle de la importación.
    ImportCreatedResponse:
      type: object
      properties:
        import:
          $ref: "#/components/schemas/ImportCreated"
      required:
        - import
      description: Envoltura `{ import }` con el job recién creado.
    ExportJobFilters:
      type: object
      properties:
        status:
          type: string
          maxLength: 40
          description: Filtra los cobros por estado.
          example: overdue
        from:
          type: string
          format: date
          description: Vencimiento desde (fecha ISO `YYYY-MM-DD`).
          example: 2026-01-01
        to:
          type: string
          format: date
          description: Vencimiento hasta (fecha ISO `YYYY-MM-DD`).
          example: 2026-06-30
      description: Filtros aplicados a la exportación (solo `charges`); quedan guardados en el job para auditoría.
    ExportJob:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        resourceType:
          type: string
          enum:
            - people
            - charges
          description: Tipo de recurso exportado (`people` = clientes | `charges` = cobros).
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          description: Estado del procesamiento (`pending` | `processing` | `completed` | `failed`).
        fileName:
          type:
            - string
            - "null"
          description: "Nombre del archivo generado (por defecto: `{resourceType}.csv`)."
        rowCount:
          type:
            - integer
            - "null"
          description: Total de líneas del CSV generado (null hasta concluir).
        truncated:
          type: boolean
          description: true cuando el CSV alcanzó el tope de seguridad y salió parcial.
        errorMessage:
          type:
            - string
            - "null"
          description: Mensaje de error cuando la generación falla.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        completedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fin de la generación (ISO 8601).
        expiresAt:
          type:
            - string
            - "null"
          format: date-time
          description: Validez de la descarga (ISO 8601); después el contenido se limpia y la ruta de descarga responde 410.
      required:
        - id
        - publicId
        - resourceType
        - status
        - fileName
        - rowCount
        - truncated
        - errorMessage
        - createdAt
        - completedAt
        - expiresAt
      description: Job de exportación CSV asíncrona (sin el tope de 10 mil líneas del modo síncrono). El CSV listo se obtiene en GET /exports/jobs/{id}/download hasta que expira.
    ExportJobCreateRequest:
      type: object
      properties:
        resourceType:
          type: string
          enum:
            - people
            - charges
          description: Tipo de recurso exportado (`people` = clientes | `charges` = cobros).
        fileName:
          type: string
          maxLength: 200
          description: "Nombre del archivo generado (por defecto: `{resourceType}.csv`)."
          example: cobrancas-junho.csv
        filters:
          $ref: "#/components/schemas/ExportJobFilters"
      required:
        - resourceType
      description: Datos para agendar una exportación asíncrona. `filters` solo aplica a `charges`.
    ExportJobCreated:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable (visible en la interfaz).
        resourceType:
          type: string
          enum:
            - people
            - charges
          description: Tipo de recurso exportado (`people` = clientes | `charges` = cobros).
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          description: Estado del procesamiento (`pending` | `processing` | `completed` | `failed`).
        fileName:
          type:
            - string
            - "null"
          description: "Nombre del archivo generado (por defecto: `{resourceType}.csv`)."
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
      required:
        - id
        - publicId
        - resourceType
        - status
        - fileName
        - createdAt
      description: Exportación recién agendada (encolada para procesamiento).
    ExportJobListResponse:
      type: object
      properties:
        exports:
          type: array
          items:
            $ref: "#/components/schemas/ExportJob"
      required:
        - exports
      description: Envoltura `{ exports }` con las exportaciones más recientes.
    ExportJobCreatedResponse:
      type: object
      properties:
        export:
          $ref: "#/components/schemas/ExportJobCreated"
      required:
        - export
      description: Envoltura `{ export }` con el job recién creado.
    WebhookEventType:
      type: string
      enum:
        - person.created
        - person.updated
        - person.deleted
        - person.classification_changed
        - charge.created
        - charge.updated
        - charge.deleted
        - charge.paid
        - charge.overdue
        - agreement.created
        - agreement.updated
        - agreement.deleted
        - agreement.accepted
        - agreement.cancelled
        - agreement.broken
        - agreement.completed
        - dispute.created
        - dispute.updated
        - dispute.resolved
        - dispute.cancelled
        - task.created
        - task.updated
        - task.completed
        - task.deleted
        - collection_rule.created
        - collection_rule.updated
        - collection_rule.deleted
        - template.created
        - template.updated
        - template.deleted
        - classification.created
        - classification.updated
        - classification.deleted
        - interaction.created
        - notification.sent
        - notification.delivered
        - notification.read
        - notification.failed
        - negativation.review_required
        - negativation.registered
        - negativation.removed
        - protest.review_required
        - protest.registered
        - protest.paid
        - protest.cancelled
      description: "Tipo de evento suscribible del catálogo (ej.: `charge.paid`)."
      example: charge.paid
    WebhookDeliveryEventType:
      type: string
      enum:
        - person.created
        - person.updated
        - person.deleted
        - person.classification_changed
        - charge.created
        - charge.updated
        - charge.deleted
        - charge.paid
        - charge.overdue
        - agreement.created
        - agreement.updated
        - agreement.deleted
        - agreement.accepted
        - agreement.cancelled
        - agreement.broken
        - agreement.completed
        - dispute.created
        - dispute.updated
        - dispute.resolved
        - dispute.cancelled
        - task.created
        - task.updated
        - task.completed
        - task.deleted
        - collection_rule.created
        - collection_rule.updated
        - collection_rule.deleted
        - template.created
        - template.updated
        - template.deleted
        - classification.created
        - classification.updated
        - classification.deleted
        - interaction.created
        - notification.sent
        - notification.delivered
        - notification.read
        - notification.failed
        - negativation.review_required
        - negativation.registered
        - negativation.removed
        - protest.review_required
        - protest.registered
        - protest.paid
        - protest.cancelled
        - webhook.test
      description: "Tipo de evento de una entrega: catálogo suscribible + `webhook.test` (disparado bajo demanda vía `POST /webhook-endpoints/{id}/test`)."
      example: charge.paid
    WebhookDeliveryStats:
      type: object
      properties:
        total:
          type: integer
          description: Total de entregas.
        pending:
          type: integer
          description: Entregas en espera de procesamiento.
        success:
          type: integer
          description: Entregas confirmadas (2xx).
        failed:
          type: integer
          description: Entregas fallidas (reintento agendado).
        exhausted:
          type: integer
          description: Entregas con intentos agotados (cola muerta).
      required:
        - total
      description: Contadores de entregas del endpoint, por estado.
    WebhookEndpoint:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable.
        url:
          type: string
          format: uri
          description: URL HTTPS de destino de las entregas (validada contra direcciones internas — SSRF).
          example: https://example.com/webhooks/kobana
        description:
          type:
            - string
            - "null"
          description: Descripción libre del endpoint.
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
          description: Tipos de evento suscritos. Lista vacía = todos los eventos.
        isActive:
          type: boolean
          description: Un endpoint activo recibe entregas; uno inactivo es ignorado por el worker.
        authType:
          type: string
          enum:
            - none
            - basic
            - bearer
            - header
          description: Autenticación del request contra tu endpoint (`none` | `basic` | `bearer` | `header`), además de la firma HMAC (siempre enviada).
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        deliveries:
          $ref: "#/components/schemas/WebhookDeliveryStats"
      required:
        - id
        - publicId
        - url
        - description
        - events
        - isActive
        - authType
        - createdAt
        - deliveries
      description: 'Endpoint de webhook: recibe los eventos de la organización vía HTTP POST (JSON). Cada entrega envía los headers `X-Webhook-Event` (tipo del evento), `X-Webhook-Event-Id`, `X-Webhook-Delivery-Id`, `X-Webhook-Attempt`, `X-Webhook-Timestamp` (ISO 8601) y `X-Webhook-Signature` en el formato `t=<unix>,v1=<hex>` — HMAC-SHA256 de `"{t}.{cuerpo}"` con el secret del endpoint; valida la firma y rechaza `t` fuera de una ventana de 5 minutos (anti-replay) antes de procesar. Además de la firma (siempre enviada), el request puede autenticarse contra tu endpoint según `authType`: `none`, `basic` (usuario/contraseña), `bearer` (token) o `header` (header personalizado); las credenciales viven en `authConfig`, cifrado en reposo y nunca devuelto. `events` vacío = suscrito a todos los eventos. Las entregas fallidas se reintentan con backoff exponencial y cada intento queda registrado.'
    WebhookDeliverySummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        status:
          type: string
          enum:
            - pending
            - success
            - failed
            - exhausted
          description: "Estado de la entrega: `pending` | `success` | `failed` (reintento agendado) | `exhausted` (intentos agotados)."
        attempts:
          type: integer
          description: Número de intentos ya realizados.
        responseStatus:
          type:
            - integer
            - "null"
          description: Estado HTTP devuelto por el destino (null sin respuesta).
          example: 200
        error:
          type:
            - string
            - "null"
          description: Mensaje del último error (null en éxito).
        eventType:
          $ref: "#/components/schemas/WebhookDeliveryEventType"
        eventAt:
          type: string
          format: date-time
          description: Cuándo ocurrió el evento (ISO 8601).
        deliveredAt:
          type:
            - string
            - "null"
          format: date-time
          description: Cuándo se confirmó la entrega (2xx); null si aún no fue entregada.
        lastAttemptAt:
          type:
            - string
            - "null"
          format: date-time
          description: Último intento (ISO 8601).
      required:
        - id
        - status
        - attempts
        - responseStatus
        - error
        - eventType
        - eventAt
        - deliveredAt
        - lastAttemptAt
      description: Resumen de una entrega reciente del endpoint.
    WebhookEndpointDetail:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable.
        url:
          type: string
          format: uri
          description: URL HTTPS de destino de las entregas (validada contra direcciones internas — SSRF).
          example: https://example.com/webhooks/kobana
        description:
          type:
            - string
            - "null"
          description: Descripción libre del endpoint.
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
          description: Tipos de evento suscritos. Lista vacía = todos los eventos.
        isActive:
          type: boolean
          description: Un endpoint activo recibe entregas; uno inactivo es ignorado por el worker.
        authType:
          type: string
          enum:
            - none
            - basic
            - bearer
            - header
          description: Autenticación del request contra tu endpoint (`none` | `basic` | `bearer` | `header`), además de la firma HMAC (siempre enviada).
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        deliveries:
          type: array
          items:
            $ref: "#/components/schemas/WebhookDeliverySummary"
          description: Las últimas 20 entregas del endpoint.
      required:
        - id
        - publicId
        - url
        - description
        - events
        - isActive
        - authType
        - createdAt
        - deliveries
      description: 'Endpoint de webhook: recibe los eventos de la organización vía HTTP POST (JSON). Cada entrega envía los headers `X-Webhook-Event` (tipo del evento), `X-Webhook-Event-Id`, `X-Webhook-Delivery-Id`, `X-Webhook-Attempt`, `X-Webhook-Timestamp` (ISO 8601) y `X-Webhook-Signature` en el formato `t=<unix>,v1=<hex>` — HMAC-SHA256 de `"{t}.{cuerpo}"` con el secret del endpoint; valida la firma y rechaza `t` fuera de una ventana de 5 minutos (anti-replay) antes de procesar. Además de la firma (siempre enviada), el request puede autenticarse contra tu endpoint según `authType`: `none`, `basic` (usuario/contraseña), `bearer` (token) o `header` (header personalizado); las credenciales viven en `authConfig`, cifrado en reposo y nunca devuelto. `events` vacío = suscrito a todos los eventos. Las entregas fallidas se reintentan con backoff exponencial y cada intento queda registrado.'
    WebhookDelivery:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        eventId:
          type: string
          format: uuid
          description: Evento entregado (UUID).
        eventType:
          $ref: "#/components/schemas/WebhookDeliveryEventType"
        status:
          type: string
          enum:
            - pending
            - success
            - failed
            - exhausted
          description: "Estado de la entrega: `pending` | `success` | `failed` (reintento agendado) | `exhausted` (intentos agotados)."
        responseStatus:
          type:
            - integer
            - "null"
          description: Estado HTTP devuelto por el destino (null sin respuesta).
          example: 200
        attempts:
          type: integer
          description: Número de intentos ya realizados.
        error:
          type:
            - string
            - "null"
          description: Mensaje del último error (null en éxito).
        requestHeaders:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Headers enviados (firma y credenciales redactadas).
        requestBody:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Cuerpo JSON enviado al destino.
        responseHeaders:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Headers de la respuesta (headers sensibles redactados).
        responseBody:
          type:
            - string
            - "null"
          description: Cuerpo de la respuesta (texto).
        deliveredAt:
          type:
            - string
            - "null"
          format: date-time
          description: Cuándo se confirmó la entrega (2xx); null si aún no fue entregada.
        lastAttemptAt:
          type:
            - string
            - "null"
          format: date-time
          description: Último intento (ISO 8601).
        nextRetryAt:
          type:
            - string
            - "null"
          format: date-time
          description: Próximo reintento agendado (null cuando no hay).
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
      required:
        - id
        - eventId
        - eventType
        - status
        - responseStatus
        - attempts
        - error
        - requestHeaders
        - requestBody
        - responseHeaders
        - responseBody
        - deliveredAt
        - lastAttemptAt
        - nextRetryAt
        - createdAt
      description: "Entrega de webhook: un intento de envío de un evento a un endpoint, con captura de request/response para inspección. Los headers de firma y credenciales se redactan antes de persistir."
    WebhookEndpointCreateRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
          description: URL HTTPS de destino de las entregas (validada contra direcciones internas — SSRF).
          example: https://example.com/webhooks/kobana
        description:
          type:
            - string
            - "null"
          maxLength: 300
          description: Descripción libre del endpoint.
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
          description: Tipos de evento suscritos. Lista vacía = todos los eventos.
          example:
            - charge.paid
            - charge.overdue
        authType:
          type: string
          enum:
            - none
            - basic
            - bearer
            - header
          description: Autenticación del request contra tu endpoint (`none` | `basic` | `bearer` | `header`), además de la firma HMAC (siempre enviada).
        authConfig:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: 'Credenciales del `authType`: `basic` → `{"username","password"}`; `bearer` → `{"token"}`; `header` → `{"key","value"}`. Cifrado en reposo; nunca devuelto en las consultas.'
          example:
            token: meu-token-secreto
      required:
        - url
      description: Datos para crear un endpoint de webhook.
    WebhookEndpointUpdateRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
          description: URL HTTPS de destino de las entregas (validada contra direcciones internas — SSRF).
        description:
          type:
            - string
            - "null"
          maxLength: 300
          description: Descripción libre del endpoint.
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEventType"
          description: Tipos de evento suscritos. Lista vacía = todos los eventos.
        isActive:
          type: boolean
          description: Un endpoint activo recibe entregas; uno inactivo es ignorado por el worker.
        authType:
          type: string
          enum:
            - none
            - basic
            - bearer
            - header
          description: Autenticación del request contra tu endpoint (`none` | `basic` | `bearer` | `header`), además de la firma HMAC (siempre enviada).
        authConfig:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: 'Credenciales del `authType`: `basic` → `{"username","password"}`; `bearer` → `{"token"}`; `header` → `{"key","value"}`. Cifrado en reposo; nunca devuelto en las consultas.'
          example:
            token: meu-token-secreto
      description: Campos a actualizar (parcial — envía solo lo que cambia).
    WebhookEndpointCreateResponse:
      type: object
      properties:
        endpoint:
          type: object
          properties:
            id:
              type: string
              format: uuid
              description: Identificador (UUID) del recurso.
            publicId:
              type: string
              description: Identificador público estable.
            url:
              type: string
              format: uri
              description: URL HTTPS de destino de las entregas (validada contra direcciones internas — SSRF).
            description:
              type:
                - string
                - "null"
              description: Descripción libre del endpoint.
            events:
              type: array
              items:
                $ref: "#/components/schemas/WebhookEventType"
              description: Tipos de evento suscritos. Lista vacía = todos los eventos.
            isActive:
              type: boolean
              description: Un endpoint activo recibe entregas; uno inactivo es ignorado por el worker.
            secret:
              type: string
              description: Secret HMAC (`whsec_…`) usado en la firma `X-Webhook-Signature`. Mostrado solo en la creación y en la rotación — guárdalo con seguridad.
              example: whsec_6f2a…
          required:
            - id
            - publicId
            - url
            - description
            - events
            - isActive
            - secret
      required:
        - endpoint
    WebhookEndpointListResponse:
      type: object
      properties:
        endpoints:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEndpoint"
      required:
        - endpoints
    WebhookEndpointShowResponse:
      type: object
      properties:
        endpoint:
          $ref: "#/components/schemas/WebhookEndpointDetail"
      required:
        - endpoint
    WebhookEndpointUpdateResponse:
      type: object
      properties:
        endpoint:
          type: object
          properties:
            id:
              type: string
              format: uuid
              description: Identificador (UUID) del recurso.
            url:
              type: string
              format: uri
              description: URL HTTPS de destino de las entregas (validada contra direcciones internas — SSRF).
            description:
              type:
                - string
                - "null"
              description: Descripción libre del endpoint.
            events:
              type: array
              items:
                $ref: "#/components/schemas/WebhookEventType"
              description: Tipos de evento suscritos. Lista vacía = todos los eventos.
            isActive:
              type: boolean
              description: Un endpoint activo recibe entregas; uno inactivo es ignorado por el worker.
          required:
            - id
            - url
            - description
            - events
            - isActive
      required:
        - endpoint
    WebhookEndpointRotateSecretResponse:
      type: object
      properties:
        secret:
          type: string
          description: Secret HMAC (`whsec_…`) usado en la firma `X-Webhook-Signature`. Mostrado solo en la creación y en la rotación — guárdalo con seguridad.
          example: whsec_6f2a…
      required:
        - secret
    WebhookDeliveryListResponse:
      type: object
      properties:
        endpoint:
          type: object
          properties:
            id:
              type: string
              format: uuid
              description: Identificador (UUID) del recurso.
            url:
              type: string
              format: uri
              description: URL HTTPS de destino de las entregas (validada contra direcciones internas — SSRF).
          required:
            - id
            - url
          description: Endpoint dueño de las entregas.
        deliveries:
          type: array
          items:
            $ref: "#/components/schemas/WebhookDelivery"
        meta:
          type: object
          properties:
            page:
              type: integer
              description: Página actual.
            perPage:
              type: integer
              description: Elementos por página aplicados.
            total:
              type: integer
              description: Total de registros que coinciden con el filtro.
            totalPages:
              type: integer
              description: Total de páginas.
          required:
            - page
            - perPage
            - total
            - totalPages
          description: Metadatos de paginación de los listados.
      required:
        - endpoint
        - deliveries
        - meta
    WebhookDeliveryRetryResponse:
      type: object
      properties:
        deliveryId:
          type: string
          format: uuid
          description: Identificador (UUID) de la entrega.
        status:
          type: string
          enum:
            - queued
          description: Siempre `queued` — la entrega fue reencolada.
      required:
        - deliveryId
        - status
    WebhookEndpointTestResponse:
      type: object
      properties:
        eventId:
          type: string
          format: uuid
          description: Evento `webhook.test` creado (UUID).
        deliveryId:
          type: string
          format: uuid
          description: Identificador (UUID) de la entrega.
        status:
          type: string
          enum:
            - queued
          description: Siempre `queued` — la entrega fue encolada.
      required:
        - eventId
        - deliveryId
        - status
    AuditLogActor:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        name:
          type:
            - string
            - "null"
          description: Nombre del usuario.
        email:
          type: string
          format: email
          description: Correo del usuario.
      required:
        - id
        - name
        - email
      description: Datos del usuario cuando `actorType` = `user` (null en caso contrario).
    AuditLog:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        action:
          type: string
          description: "Acción registrada (ej.: `webhook_endpoint.created`, `dispute.resolved`)."
          example: webhook_endpoint.created
        entityType:
          type: string
          description: "Tipo de la entidad afectada (ej.: `WebhookEndpoint`)."
          example: WebhookEndpoint
        entityId:
          type:
            - string
            - "null"
          description: ID de la entidad afectada (null cuando no aplica).
        actorType:
          type: string
          enum:
            - user
            - system
            - job
          description: "Quién ejecutó: `user` (humano), `system` o `job`."
        actorId:
          type:
            - string
            - "null"
          description: ID del usuario, nombre del job o del sistema.
        actor:
          allOf:
            - $ref: "#/components/schemas/AuditLogActor"
            - type:
                - object
                - "null"
        before:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Estado (parcial) antes del cambio.
        after:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Estado (parcial) después del cambio.
        reason:
          type:
            - string
            - "null"
          description: Justificación informada, cuando exista.
        ip:
          type:
            - string
            - "null"
          description: IP de origen de la solicitud.
        userAgent:
          type:
            - string
            - "null"
          description: User-Agent de la solicitud.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
      required:
        - id
        - action
        - entityType
        - entityId
        - actorType
        - actorId
        - actor
        - before
        - after
        - reason
        - ip
        - userAgent
        - createdAt
      description: "Registro inmutable (append-only) de la pista de auditoría: quién hizo qué, en qué entidad, con el estado antes/después. La escritura es exclusiva del sistema; la API expone solo lectura."
    AuditLogListResponse:
      type: object
      properties:
        logs:
          type: array
          items:
            $ref: "#/components/schemas/AuditLog"
        pagination:
          type: object
          properties:
            page:
              type: integer
              description: Página actual.
            perPage:
              type: integer
              description: Elementos por página aplicados.
            total:
              type: integer
              description: Total de registros que coinciden con el filtro.
            totalPages:
              type: integer
              description: Total de páginas.
          required:
            - page
            - perPage
            - total
            - totalPages
          description: Metadatos de paginación de los listados.
      required:
        - logs
        - pagination
    RecoveryMetrics:
      type: object
      properties:
        period:
          type: object
          properties:
            since:
              type: string
              description: Inicio del período (YYYY-MM-DD).
              example: 2026-01-01
            months:
              type: integer
              description: Meses considerados.
              example: 6
          required:
            - since
            - months
          description: Período considerado.
        totals:
          type: object
          properties:
            recoveredAmount:
              type: number
              description: Valor total recuperado en el período.
              example: 15230.5
            recoveredCount:
              type: integer
              description: Cantidad de cobros recuperados.
              example: 42
            avgDaysOverdue:
              type:
                - integer
                - "null"
              description: Mora promedio (días) al momento del pago.
              example: 12
            avgStepsToRecover:
              type:
                - number
                - "null"
              description: Promedio de etapas de la regla ejecutadas hasta el pago.
              example: 2.4
            recoveryRate:
              type:
                - number
                - "null"
              description: Recuperado ÷ (recuperado + en mora hoy); null sin base de cálculo.
              example: 0.63
            overdueAmountNow:
              type: number
              description: Valor de la cartera en mora hoy.
              example: 8940
          required:
            - recoveredAmount
            - recoveredCount
            - avgDaysOverdue
            - avgStepsToRecover
            - recoveryRate
            - overdueAmountNow
          description: Totales del período.
        byMonth:
          type: array
          items:
            type: object
            properties:
              month:
                type: string
                description: Mes (YYYY-MM).
                example: 2026-03
              amount:
                type: number
                description: Valor recuperado en el mes.
              count:
                type: integer
                description: Cobros recuperados en el mes.
            required:
              - month
              - amount
              - count
          description: Serie mensual de recuperación.
        byMethod:
          type: array
          items:
            type: object
            properties:
              method:
                type: string
                enum:
                  - rule
                  - agreement
                  - overdue_no_rule
                description: "Método: `rule` (en regla) | `agreement` (estaba negociado) | `overdue_no_rule` (en mora sin regla)."
              amount:
                type: number
                description: Valor recuperado en el grupo.
              count:
                type: integer
                description: Cantidad de cobros en el grupo.
            required:
              - method
              - amount
              - count
          description: Atribución por método de recuperación.
        byChannel:
          type: array
          items:
            type: object
            properties:
              channel:
                type: string
                description: "Canal (ej.: `email`, `sms`, `whatsapp`)."
                example: email
              amount:
                type: number
                description: Valor recuperado en el grupo.
              count:
                type: integer
                description: Cantidad de cobros en el grupo.
            required:
              - channel
              - amount
              - count
          description: Atribución por el canal de la última etapa enviada antes del pago.
      required:
        - period
        - totals
        - byMonth
        - byMethod
        - byChannel
      description: "Métricas de recuperación: lo que la regla de cobranza recuperó en el período — totales, serie mensual, atribución por método y por canal, y tasa de recuperación (recuperado ÷ (recuperado + cartera en mora hoy))."
    EmailLayoutListItem:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable.
        name:
          type: string
          description: Nombre del layout.
          example: Layout institucional
        htmlBody:
          type: string
          description: HTML completo del layout; debe contener el placeholder `{{content}}`.
        isDefault:
          type: boolean
          description: Layout predeterminado de la organización (solo uno; definir uno nuevo desmarca el anterior).
        isActive:
          type: boolean
          description: Layout disponible para uso.
        companiesCount:
          type: integer
          description: Cantidad de empresas que usan este layout.
      required:
        - id
        - publicId
        - name
        - htmlBody
        - isDefault
        - isActive
        - companiesCount
      description: "Layout de correo: HTML que envuelve el contenido de los mensajes enviados por la regla. Debe contener el placeholder `{{content}}`, reemplazado por el cuerpo de la notificación al enviar."
    EmailLayout:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Identificador (UUID) del recurso.
        publicId:
          type: string
          description: Identificador público estable.
        organizationId:
          type: string
          format: uuid
          description: Organización dueña del layout.
        name:
          type: string
          description: Nombre del layout.
        htmlBody:
          type: string
          description: HTML completo del layout; debe contener el placeholder `{{content}}`.
        isDefault:
          type: boolean
          description: Layout predeterminado de la organización (solo uno; definir uno nuevo desmarca el anterior).
        isActive:
          type: boolean
          description: Layout disponible para uso.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos escritos por el sistema.
        createdAt:
          type: string
          format: date-time
          description: Creado el (ISO 8601).
        updatedAt:
          type: string
          format: date-time
          description: Actualizado el (ISO 8601).
        deletedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Eliminado el (borrado lógico; null mientras activo).
      required:
        - id
        - publicId
        - organizationId
        - name
        - htmlBody
        - isDefault
        - isActive
        - metadata
        - createdAt
        - updatedAt
        - deletedAt
      description: "Layout de correo: HTML que envuelve el contenido de los mensajes enviados por la regla. Debe contener el placeholder `{{content}}`, reemplazado por el cuerpo de la notificación al enviar."
    EmailLayoutCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 2
          maxLength: 80
          description: Nombre del layout.
          example: Layout institucional
        htmlBody:
          type: string
          minLength: 20
          description: HTML completo del layout; debe contener el placeholder `{{content}}`.
          example: <html><body><header>ACME</header>{{content}}</body></html>
        isDefault:
          type: boolean
          description: Layout predeterminado de la organización (solo uno; definir uno nuevo desmarca el anterior).
      required:
        - name
        - htmlBody
      description: Datos para crear un layout de correo.
    EmailLayoutListResponse:
      type: object
      properties:
        layouts:
          type: array
          items:
            $ref: "#/components/schemas/EmailLayoutListItem"
        builtinLayout:
          type: string
          description: Layout integrado de referencia (usado cuando la organización no tiene layout predeterminado).
      required:
        - layouts
        - builtinLayout
    EmailLayoutCreateResponse:
      type: object
      properties:
        layout:
          $ref: "#/components/schemas/EmailLayout"
      required:
        - layout
    NotificationPreference:
      type: object
      properties:
        type:
          type: string
          enum:
            - reminder
            - overdue
            - negotiation
            - negativation_warning
            - protest_warning
            - payment_confirmation
          description: "Tipo de mensaje: `reminder` (recordatorio previo al vencimiento) | `overdue` (cobranza tras el vencimiento) | `negotiation` (propuesta de negociación) | `negativation_warning` (aviso de reporte crediticio) | `protest_warning` (aviso de protesto) | `payment_confirmation` (confirmación de pago)."
          example: reminder
        label:
          type: string
          description: Etiqueta legible del tipo (pt-BR).
          example: Lembrete pré-vencimento
        emailEnabled:
          type: boolean
          description: Canal de correo habilitado para el tipo.
        smsEnabled:
          type: boolean
          description: Canal SMS habilitado para el tipo.
        whatsappEnabled:
          type: boolean
          description: Canal WhatsApp habilitado para el tipo.
      required:
        - type
        - label
        - emailEnabled
        - smsEnabled
        - whatsappEnabled
      description: "Preferencia de notificación: habilita/deshabilita canales (correo, SMS, WhatsApp) por tipo de mensaje de la regla. Sin registro guardado, todos los canales quedan habilitados."
    NotificationPreferenceListResponse:
      type: object
      properties:
        preferences:
          type: array
          items:
            $ref: "#/components/schemas/NotificationPreference"
      required:
        - preferences
    NotificationPreferenceUpdateRequest:
      type: object
      properties:
        preferences:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
                enum:
                  - reminder
                  - overdue
                  - negotiation
                  - negativation_warning
                  - protest_warning
                  - payment_confirmation
                description: "Tipo de mensaje: `reminder` (recordatorio previo al vencimiento) | `overdue` (cobranza tras el vencimiento) | `negotiation` (propuesta de negociación) | `negativation_warning` (aviso de reporte crediticio) | `protest_warning` (aviso de protesto) | `payment_confirmation` (confirmación de pago)."
                example: reminder
              emailEnabled:
                type: boolean
                description: Canal de correo habilitado para el tipo.
              smsEnabled:
                type: boolean
                description: Canal SMS habilitado para el tipo.
              whatsappEnabled:
                type: boolean
                description: Canal WhatsApp habilitado para el tipo.
            required:
              - type
              - emailEnabled
              - smsEnabled
              - whatsappEnabled
          minItems: 1
      required:
        - preferences
      description: Preferencias a guardar (mínimo 1 tipo).
    NotificationPreferenceUpdateResponse:
      type: object
      properties:
        ok:
          type: boolean
          description: Siempre `true` en caso de éxito.
          example: true
      required:
        - ok
  parameters: {}
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: "Clave de API en el header `Authorization: Bearer <token>`. Crea y gestiona claves en el panel (Configuración → Seguridad), con alcances opcionales y expiración. Una clave sin alcances tiene acceso total al negocio; la administración de la cuenta (miembros, roles, claves) nunca está disponible vía API."
paths:
  /api/v1/people:
    get:
      tags:
        - Clientes
      summary: Listar clientes
      description: Lista los clientes de la organización con paginación y filtros.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: "Campo de ordenación. Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación. Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            description: Búsqueda por nombre, documento o correo.
          required: false
          description: Búsqueda por nombre, documento o correo.
          name: search
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por clasificación (UUID).
          required: false
          description: Filtra por clasificación (UUID).
          name: classification_id
          in: query
        - schema:
            type: string
            enum:
              - active
              - deleted
            description: Filtra por estado del registro (`active` | `deleted`).
          required: false
          description: Filtra por estado del registro (`active` | `deleted`).
          name: status
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de clientes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PersonListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-people
    post:
      tags:
        - Clientes
      summary: Crear cliente
      description: Crea un cliente en la cartera. `externalId` y `documentNumber` se usan para deduplicación en importaciones e integraciones.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PersonCreateRequest"
      responses:
        "201":
          description: Cliente creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Person"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-people
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/people/{id}:
    get:
      tags:
        - Clientes
      summary: Consultar cliente
      description: Devuelve un cliente por ID.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Cliente encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Person"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-people-id
    put:
      tags:
        - Clientes
      summary: Actualizar cliente
      description: Actualiza los datos de un cliente (parcial).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PersonUpdateRequest"
      responses:
        "200":
          description: Cliente actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Person"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-people-id
    delete:
      tags:
        - Clientes
      summary: Eliminar cliente
      description: Elimina (borrado lógico) un cliente. Los cobros y el historial permanecen para la pista de auditoría.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Cliente eliminado.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-people-id
  /api/v1/people/{id}/statement-pdf:
    get:
      tags:
        - Clientes
      summary: Estado de cuenta del deudor (PDF)
      description: "Genera el estado de cuenta del deudor en PDF: todos los cobros de la persona (documento, vencimiento, estado, importe original y actual) con los totales pendiente, vencido y ya pagado. Delimitado por la organización. Responde `application/pdf` como adjunto binario."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: PDF del estado de cuenta del deudor (adjunto binario `application/pdf`).
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-people-id-statement-pdf
  /api/v1/charges:
    get:
      tags:
        - Cobros
      summary: Listar cobros
      description: Lista los cobros de la organización con paginación y filtros. Los cobros pendientes vencidos se promueven a `overdue` en la lectura.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: "Campo de ordenación. Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación. Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            description: Búsqueda por número de documento, descripción, `externalId`, nombre o documento del cliente.
          required: false
          description: Búsqueda por número de documento, descripción, `externalId`, nombre o documento del cliente.
          name: search
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por los cobros de un cliente (UUID).
          required: false
          description: Filtra por los cobros de un cliente (UUID).
          name: person_id
          in: query
        - schema:
            type: string
            description: Filtra por estado. Acepta un valor o una lista separada por comas (`pending`, `paid`, `overdue`, `cancelled`, `negotiated`, `protested`, `negatived`, `written_off`).
            example: pending,overdue
          required: false
          description: Filtra por estado. Acepta un valor o una lista separada por comas (`pending`, `paid`, `overdue`, `cancelled`, `negotiated`, `protested`, `negatived`, `written_off`).
          name: status
          in: query
        - schema:
            type: string
            description: Vencimiento desde (inclusive, `YYYY-MM-DD` o datetime ISO).
            example: 2026-01-01
          required: false
          description: Vencimiento desde (inclusive, `YYYY-MM-DD` o datetime ISO).
          name: due_date_from
          in: query
        - schema:
            type: string
            description: Vencimiento hasta (inclusive, `YYYY-MM-DD` o datetime ISO).
            example: 2026-12-31
          required: false
          description: Vencimiento hasta (inclusive, `YYYY-MM-DD` o datetime ISO).
          name: due_date_to
          in: query
        - schema:
            type: string
            description: Relaciones a incluir en la respuesta (`person`).
            example: person
          required: false
          description: Relaciones a incluir en la respuesta (`person`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de cobros.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChargeListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-charges
    post:
      tags:
        - Cobros
      summary: Crear cobro
      description: Crea un cobro para un cliente. Sin `collectionRuleId`, se aplica la regla de cobranza predeterminada de la organización. `currentAmount` = original + intereses + multa − descuento; un cobro ya vencido entra como `overdue`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChargeCreateRequest"
      responses:
        "201":
          description: Cobro creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Charge"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-charges
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/charges/{id}:
    get:
      tags:
        - Cobros
      summary: Consultar cobro
      description: Devuelve un cobro por ID. Usa `include=engine` para recibir la telemetría del motor (inscripción en la regla y ejecuciones de etapa).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            description: Relaciones a incluir en la respuesta (`person`, `engine` — telemetría de la regla).
            example: person,engine
          required: false
          description: Relaciones a incluir en la respuesta (`person`, `engine` — telemetría de la regla).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Cobro encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Charge"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-charges-id
    put:
      tags:
        - Cobros
      summary: Actualizar cobro
      description: Actualiza los datos de un cobro (parcial).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChargeUpdateRequest"
      responses:
        "200":
          description: Cobro actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Charge"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-charges-id
    delete:
      tags:
        - Cobros
      summary: Eliminar cobro
      description: Elimina (borrado lógico) un cobro junto con las tareas y ofertas relacionadas. Un cobro con notificaciones, interacciones, negativaciones o protestos asociados no puede eliminarse (409).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Cobro eliminado (devuelve un mensaje de confirmación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChargeDeleteResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-charges-id
  /api/v1/charges/{id}/settlement-pdf:
    get:
      tags:
        - Cobros
      summary: Carta de finiquito (PDF)
      description: Genera la carta de finiquito del cobro en PDF (documento jurídico), delimitada por la organización. Solo se permite cuando el cobro está pagado (estado `paid`); de lo contrario devuelve 409 (`CHARGE_NOT_SETTLED`). Responde `application/pdf` como adjunto binario.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: PDF de la carta de finiquito (adjunto binario `application/pdf`).
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: El cobro no está finiquitado — la carta solo puede generarse para el estado `paid` (código `CHARGE_NOT_SETTLED`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-charges-id-settlement-pdf
  /api/v1/charges/{id}/notify:
    post:
      tags:
        - Cobros
      summary: Notificar cobro
      description: Crea una notificación manual para el cobro y la envía a la cola de procesamiento. Actualiza `lastNotificationAt` y, cuando se informa `collectionRuleStepId`, el `currentStep` del cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChargeNotifyRequest"
      responses:
        "201":
          description: Notificación creada y encolada (estado `queued`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChargeNotifyResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-charges-id-notify
  /api/v1/agreements:
    get:
      tags:
        - Acuerdos
      summary: Listar acuerdos
      description: Lista los acuerdos de la organización con paginación y filtros.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: "Campo de ordenación. Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación. Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - proposed
              - accepted
              - active
              - completed
              - cancelled
              - defaulted
            description: Filtra por estado del acuerdo.
          required: false
          description: Filtra por estado del acuerdo.
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por los acuerdos de un cliente (UUID).
          required: false
          description: Filtra por los acuerdos de un cliente (UUID).
          name: person_id
          in: query
        - schema:
            type: string
            description: Relaciones a incluir en la respuesta (`person`, `installments`, `negotiationOffer`).
            example: person,installments
          required: false
          description: Relaciones a incluir en la respuesta (`person`, `installments`, `negotiationOffer`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de acuerdos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgreementListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-agreements
    post:
      tags:
        - Acuerdos
      summary: Crear acuerdo
      description: Crea un acuerdo con estado `proposed`. `finalTotal` = `originalTotal` − `discountAmount` (debe ser mayor que cero) e `installmentValue` = `finalTotal` / `numberOfInstallments`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgreementCreateRequest"
      responses:
        "201":
          description: Acuerdo creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Agreement"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-agreements
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/agreements/{id}:
    get:
      tags:
        - Acuerdos
      summary: Consultar acuerdo
      description: Devuelve un acuerdo por ID (siempre incluye datos resumidos del cliente).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            description: Relaciones a incluir en la respuesta (`installments`, `negotiationOffer`).
            example: installments,negotiationOffer
          required: false
          description: Relaciones a incluir en la respuesta (`installments`, `negotiationOffer`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Acuerdo encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Agreement"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-agreements-id
    put:
      tags:
        - Acuerdos
      summary: Actualizar acuerdo
      description: Actualiza un acuerdo (parcial). Permitido solo con estado `proposed` o `accepted` (409 en los demás).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgreementUpdateRequest"
      responses:
        "200":
          description: Acuerdo actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Agreement"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-agreements-id
    delete:
      tags:
        - Acuerdos
      summary: Eliminar acuerdo
      description: Elimina (borrado lógico) un acuerdo, marcándolo como `cancelled`. Los acuerdos `completed` no pueden eliminarse (409).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Acuerdo eliminado.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-agreements-id
  /api/v1/agreements/{id}/accept:
    post:
      tags:
        - Acuerdos
      summary: Aceptar acuerdo
      description: "Acepta un acuerdo `proposed` (409 en los demás estados): registra `acceptedAt`/`acceptedIp` y crea las cuotas con vencimientos mensuales a partir de `firstDueDate`."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgreementAcceptRequest"
      responses:
        "200":
          description: Acuerdo aceptado (devuelve el acuerdo con las cuotas creadas).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Agreement"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-agreements-id-accept
  /api/v1/agreements/{id}/cancel:
    post:
      tags:
        - Acuerdos
      summary: Cancelar acuerdo
      description: Cancela un acuerdo, registrando `cancelledAt` y el motivo, y cancela las cuotas pendientes/vencidas. Los acuerdos `completed` o ya cancelados devuelven 409.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgreementCancelRequest"
      responses:
        "200":
          description: Acuerdo cancelado (devuelve el acuerdo con sus cuotas).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Agreement"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-agreements-id-cancel
  /api/v1/agreements/{id}/pdf:
    get:
      tags:
        - Acuerdos
      summary: Término de acuerdo (PDF)
      description: Genera el término de acuerdo en PDF (documento jurídico) con acreedor, deudor, totales y cuotas, delimitado por la organización. Responde `application/pdf` como adjunto binario.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: PDF del término de acuerdo (adjunto binario `application/pdf`).
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-agreements-id-pdf
  /api/v1/agreements/{id}/installments:
    get:
      tags:
        - Acuerdos
      summary: Listar cuotas del acuerdo
      description: Lista las cuotas de un acuerdo (ordenadas por número) con un resumen agregado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Cuotas del acuerdo con resumen.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgreementInstallmentListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-agreements-id-installments
    put:
      tags:
        - Acuerdos
      summary: Actualizar cuota del acuerdo
      description: Actualiza una cuota (pago/estado). Permitido solo en acuerdos `accepted` o `active`; las cuotas canceladas no pueden modificarse (409). El primer pago activa el acuerdo; con todas las cuotas pagadas, el acuerdo se completa (`completed`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgreementInstallmentUpdateRequest"
      responses:
        "200":
          description: Cuota actualizada (devuelve la cuota y el acuerdo actualizado).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgreementInstallmentUpdateResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-agreements-id-installments
  /api/v1/disputes:
    get:
      tags:
        - Disputas
      summary: Listar disputas
      description: Lista las disputas de la organización, ordenadas por apertura (la más reciente primero). No admite ordenación personalizada.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            enum:
              - open
              - under_review
              - resolved_valid
              - resolved_invalid
              - canceled
            description: Filtra por estado (`open` | `under_review` | `resolved_valid` | `resolved_invalid` | `canceled`).
          required: false
          description: Filtra por estado (`open` | `under_review` | `resolved_valid` | `resolved_invalid` | `canceled`).
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cobro (UUID).
          required: false
          description: Filtra por cobro (UUID).
          name: charge_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cliente (UUID).
          required: false
          description: Filtra por cliente (UUID).
          name: person_id
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de disputas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DisputeListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-disputes
    post:
      tags:
        - Disputas
      summary: Abrir disputa
      description: Abre una disputa sobre un cobro y pausa la regla de inmediato. Si el cobro ya tiene una disputa abierta o en revisión, devuelve la existente (no duplica). Admite `Idempotency-Key`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DisputeCreateRequest"
      responses:
        "201":
          description: Disputa abierta (o la disputa abierta existente del cobro).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DisputeResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-disputes
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/disputes/{id}:
    get:
      tags:
        - Disputas
      summary: Consultar disputa
      description: Devuelve una disputa por ID, con el cobro completo, el cliente y el responsable.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Disputa encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DisputeDetailResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-disputes-id
    patch:
      tags:
        - Disputas
      summary: Ejecutar acción en la disputa
      description: "Ejecuta una acción de flujo en la disputa: `start_review`, `resolve` o `cancel`. Las transiciones inválidas (p. ej., `start_review` cuando el estado no es `open`) devuelven 409."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DisputeActionRequest"
      responses:
        "200":
          description: Disputa actualizada por la acción.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DisputeResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: patch-disputes-id
  /api/v1/tasks:
    get:
      tags:
        - Tareas
      summary: Listar tareas
      description: Lista las tareas de la organización con paginación y filtros. Ordenable vía `sort_by` (`title` | `priority` | `status` | `dueAt` | `createdAt` | `updatedAt`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: "Campo de ordenación. Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación. Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - pending
              - in_progress
              - completed
              - cancelled
            description: Filtra por estado (`pending` | `in_progress` | `completed` | `cancelled`).
          required: false
          description: Filtra por estado (`pending` | `in_progress` | `completed` | `cancelled`).
          name: status
          in: query
        - schema:
            type: string
            enum:
              - low
              - medium
              - high
              - urgent
            description: Filtra por prioridad (`low` | `medium` | `high` | `urgent`).
          required: false
          description: Filtra por prioridad (`low` | `medium` | `high` | `urgent`).
          name: priority
          in: query
        - schema:
            type: string
            enum:
              - call
              - email
              - visit
              - review
              - follow_up
            description: Filtra por tipo (`call` | `email` | `visit` | `review` | `follow_up`).
          required: false
          description: Filtra por tipo (`call` | `email` | `visit` | `review` | `follow_up`).
          name: task_type
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por responsable (UUID).
          required: false
          description: Filtra por responsable (UUID).
          name: assigned_to_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cliente (UUID).
          required: false
          description: Filtra por cliente (UUID).
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cobro (UUID).
          required: false
          description: Filtra por cobro (UUID).
          name: charge_id
          in: query
        - schema:
            type: string
            description: Relaciones a incluir, separadas por comas (`person`, `assignedTo`, `charge`).
            example: person,assignedTo,charge
          required: false
          description: Relaciones a incluir, separadas por comas (`person`, `assignedTo`, `charge`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de tareas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-tasks
    post:
      tags:
        - Tareas
      summary: Crear tarea
      description: Crea una tarea, opcionalmente vinculada a un cliente, un cobro y un responsable (todos de la propia organización). Admite `Idempotency-Key`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskCreateRequest"
      responses:
        "201":
          description: Tarea creada (incluye los resúmenes de cliente, responsable y cobro).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Task"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-tasks
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/tasks/{id}:
    get:
      tags:
        - Tareas
      summary: Consultar tarea
      description: Devuelve una tarea por ID, con resúmenes de cliente, responsable y cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Tarea encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Task"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-tasks-id
    put:
      tags:
        - Tareas
      summary: Actualizar tarea
      description: Actualiza los datos de una tarea (parcial).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaskUpdateRequest"
      responses:
        "200":
          description: Tarea actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Task"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-tasks-id
    delete:
      tags:
        - Tareas
      summary: Eliminar tarea
      description: "Elimina lógicamente una tarea: el estado pasa a `cancelled` (el registro permanece)."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Tarea cancelada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-tasks-id
  /api/v1/tasks/{id}/complete:
    post:
      tags:
        - Tareas
      summary: Completar tarea
      description: Marca la tarea como completada y completa `completedAt`. Devuelve 409 si la tarea ya está completada o cancelada.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Tarea completada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Task"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-tasks-id-complete
  /api/v1/interactions:
    get:
      tags:
        - Interacciones
      summary: Listar interacciones
      description: Lista las interacciones de la organización con paginación y filtros. Ordenable vía `sort_by` (`contactedAt` | `interactionType` | `createdAt` | `updatedAt`; por defecto `contactedAt` desc).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: "Campo de ordenación. Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación. Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - call
              - email
              - whatsapp
              - meeting
              - note
            description: Filtra por tipo (`call` | `email` | `whatsapp` | `meeting` | `note`).
          required: false
          description: Filtra por tipo (`call` | `email` | `whatsapp` | `meeting` | `note`).
          name: interaction_type
          in: query
        - schema:
            type: string
            enum:
              - inbound
              - outbound
            description: Filtra por dirección (`inbound` | `outbound`).
          required: false
          description: Filtra por dirección (`inbound` | `outbound`).
          name: direction
          in: query
        - schema:
            type: string
            enum:
              - promise_to_pay
              - negotiation
              - dispute
              - no_contact
              - callback_requested
              - payment_confirmed
            description: Filtra por resultado (`promise_to_pay` | `negotiation` | `dispute` | `no_contact` | `callback_requested` | `payment_confirmed`).
          required: false
          description: Filtra por resultado (`promise_to_pay` | `negotiation` | `dispute` | `no_contact` | `callback_requested` | `payment_confirmed`).
          name: outcome
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cliente (UUID).
          required: false
          description: Filtra por cliente (UUID).
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cobro (UUID).
          required: false
          description: Filtra por cobro (UUID).
          name: charge_id
          in: query
        - schema:
            type: string
            description: Relaciones a incluir, separadas por comas (`person`, `user`, `charge`).
            example: person,user,charge
          required: false
          description: Relaciones a incluir, separadas por comas (`person`, `user`, `charge`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de interacciones.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InteractionListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-interactions
    post:
      tags:
        - Interacciones
      summary: Registrar interacción
      description: Registra una interacción con un cliente, opcionalmente vinculada a un cobro. Admite `Idempotency-Key`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InteractionCreateRequest"
      responses:
        "201":
          description: Interacción registrada (incluye los resúmenes de cliente, usuario y cobro).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Interaction"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-interactions
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/interactions/{id}:
    get:
      tags:
        - Interacciones
      summary: Consultar interacción
      description: Devuelve una interacción por ID, con resúmenes de cliente, usuario y cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Interacción encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Interaction"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-interactions-id
    put:
      tags:
        - Interacciones
      summary: Actualizar interacción
      description: Actualiza los campos editables de una interacción (parcial).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InteractionUpdateRequest"
      responses:
        "200":
          description: Interacción actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Interaction"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-interactions-id
    delete:
      tags:
        - Interacciones
      summary: Eliminar interacción
      description: Elimina (borrado lógico) una interacción; el registro permanece para la pista de auditoría.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Interacción eliminada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-interactions-id
  /api/v1/notifications:
    get:
      tags:
        - Notificaciones
      summary: Listar notificaciones
      description: Lista las notificaciones de la organización con paginación y filtros. Ordenable vía `sort_by` (`createdAt` | `updatedAt` | `sentAt` | `deliveredAt` | `channel` | `status`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: "Campo de ordenación. Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación. Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            description: Filtra por canal; acepta un valor único o una lista separada por comas (`email` | `sms` | `whatsapp` | `voice` | `manual`).
            example: email,whatsapp
          required: false
          description: Filtra por canal; acepta un valor único o una lista separada por comas (`email` | `sms` | `whatsapp` | `voice` | `manual`).
          name: channel
          in: query
        - schema:
            type: string
            description: Filtra por estado; acepta un valor único o una lista separada por comas (`pending` | `queued` | `sent` | `delivered` | `read` | `failed` | `bounced`).
            example: failed,bounced
          required: false
          description: Filtra por estado; acepta un valor único o una lista separada por comas (`pending` | `queued` | `sent` | `delivered` | `read` | `failed` | `bounced`).
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cliente (UUID).
          required: false
          description: Filtra por cliente (UUID).
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cobro (UUID).
          required: false
          description: Filtra por cobro (UUID).
          name: charge_id
          in: query
        - schema:
            type: string
            description: Relaciones a incluir, separadas por comas (`person`, `charge`, `step`; `template` equivale a `step`).
            example: person,charge,step
          required: false
          description: Relaciones a incluir, separadas por comas (`person`, `charge`, `step`; `template` equivale a `step`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de notificaciones.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-notifications
    post:
      tags:
        - Notificaciones
      summary: Crear notificación
      description: Crea una notificación manual para un cliente, opcionalmente vinculada a un cobro y a un paso de la regla. El destinatario se valida según el canal.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationCreateRequest"
      responses:
        "201":
          description: Notificación creada con estado `pending`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Notification"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-notifications
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/notifications/{id}:
    get:
      tags:
        - Notificaciones
      summary: Consultar notificación
      description: Devuelve una notificación por ID; usa `include` para anexar relaciones.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            description: Relaciones a incluir, separadas por comas (`person`, `charge`, `step`; `template` equivale a `step`).
            example: person,charge,step
          required: false
          description: Relaciones a incluir, separadas por comas (`person`, `charge`, `step`; `template` equivale a `step`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Notificación encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Notification"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-notifications-id
    put:
      tags:
        - Notificaciones
      summary: Actualizar notificación
      description: Actualiza el estado, los timestamps de entrega y los metadatos de una notificación (parcial). Útil para integraciones que confirman envío/entrega.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationUpdateRequest"
      responses:
        "200":
          description: Notificación actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Notification"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-notifications-id
    delete:
      tags:
        - Notificaciones
      summary: Eliminar notificación
      description: Elimina una notificación DEFINITIVAMENTE (borrado físico — el modelo no tiene borrado lógico).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Notificación eliminada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-notifications-id
  /api/v1/notifications/{id}/resend:
    post:
      tags:
        - Notificaciones
      summary: Reenviar notificación
      description: Crea una NUEVA notificación `pending` copiando la original (el `metadata` de la nueva referencia la original en `resendOf`). Solo se permite cuando la original está `failed` o `bounced`; en caso contrario devuelve 400. Admite `Idempotency-Key`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      responses:
        "201":
          description: Nueva notificación creada a partir de la original.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Notification"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-notifications-id-resend
  /api/v1/collection-rules:
    get:
      tags:
        - Reglas de cobranza
      summary: Listar reglas
      description: Lista las reglas de cobranza de la organización, ordenadas por prioridad. `_count.charges` trae el total de cobros activos en la regla.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por la clasificación asociada (UUID).
          required: false
          description: Filtra por la clasificación asociada (UUID).
          name: classification_id
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filtra por la regla predeterminada (`true` | `false`).
          required: false
          description: Filtra por la regla predeterminada (`true` | `false`).
          name: is_default
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: "Filtra por activación: `true` = activas, `false` = inactivas."
          required: false
          description: "Filtra por activación: `true` = activas, `false` = inactivas."
          name: activated_at
          in: query
        - schema:
            type: string
            description: "Relaciones a incluir, separadas por coma. Soportado: `steps`."
            example: steps
          required: false
          description: "Relaciones a incluir, separadas por coma. Soportado: `steps`."
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de reglas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionRuleListResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-collection-rules
    post:
      tags:
        - Reglas de cobranza
      summary: Crear regla
      description: Crea una regla de cobranza, opcionalmente ya con etapas. Marcar `isDefault` desmarca la regla predeterminada anterior.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CollectionRuleCreateRequest"
      responses:
        "201":
          description: Regla creada (con clasificación y etapas).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionRule"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-collection-rules
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/collection-rules/{id}:
    get:
      tags:
        - Reglas de cobranza
      summary: Consultar regla
      description: Devuelve una regla por ID. Usa `include=steps` para incluir las etapas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            description: "Relaciones a incluir, separadas por coma. Soportado: `steps`."
            example: steps
          required: false
          description: "Relaciones a incluir, separadas por coma. Soportado: `steps`."
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Regla encontrada (envelope `data`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionRuleShowResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-collection-rules-id
    put:
      tags:
        - Reglas de cobranza
      summary: Actualizar regla
      description: Actualiza los datos de la regla (parcial). El array `steps`, cuando se envía, hace upsert y elimina las etapas ausentes.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CollectionRuleUpdateRequest"
      responses:
        "200":
          description: Regla actualizada (envelope `data`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionRuleShowResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-collection-rules-id
    delete:
      tags:
        - Reglas de cobranza
      summary: Eliminar regla
      description: Elimina (borrado lógico) la regla y sus etapas. Falla con 409 si hay cobros usando la regla.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Regla eliminada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-collection-rules-id
  /api/v1/collection-rules/{id}/steps:
    get:
      tags:
        - Reglas de cobranza
      summary: Listar etapas de la regla
      description: Lista las etapas de la regla ordenadas por `position`, con la plantilla de mensaje incluida.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Etapas de la regla (envelope `data`, sin paginación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionRuleStepListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-collection-rules-id-steps
    post:
      tags:
        - Reglas de cobranza
      summary: Crear etapa
      description: Crea una etapa en la regla. Sin `position` entra al final; con `position`, desplaza las etapas siguientes.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CollectionRuleStepCreateRequest"
      responses:
        "201":
          description: Etapa creada (con plantilla de mensaje).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionRuleStep"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-collection-rules-id-steps
  /api/v1/collection-rules/{id}/steps/{stepId}:
    put:
      tags:
        - Reglas de cobranza
      summary: Actualizar etapa
      description: Actualiza una etapa (parcial). Cambiar `position` reordena automáticamente las demás etapas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) de la etapa.
          required: true
          description: Identificador (UUID) de la etapa.
          name: stepId
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CollectionRuleStepUpdateRequest"
      responses:
        "200":
          description: Etapa actualizada (con plantilla de mensaje).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionRuleStep"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-collection-rules-id-steps-stepid
    delete:
      tags:
        - Reglas de cobranza
      summary: Eliminar etapa
      description: Elimina (borrado lógico) una etapa y reordena las restantes. Falla con 409 si la etapa tiene notificaciones asociadas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) de la etapa.
          required: true
          description: Identificador (UUID) de la etapa.
          name: stepId
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Etapa eliminada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-collection-rules-id-steps-stepid
  /api/v1/templates:
    get:
      tags:
        - Plantillas de mensaje
      summary: Listar plantillas
      description: Lista las plantillas de mensaje de la organización con paginación, búsqueda y filtros.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: Búsqueda por nombre, asunto o cuerpo.
          required: false
          description: Búsqueda por nombre, asunto o cuerpo.
          name: search
          in: query
        - schema:
            type: string
            enum:
              - email
              - sms
              - whatsapp
              - voice
              - manual
            description: Filtra por canal.
          required: false
          description: Filtra por canal.
          name: channel
          in: query
        - schema:
            type: string
            enum:
              - reminder
              - overdue
              - negotiation
              - negativation_warning
              - protest_warning
              - payment_confirmation
            description: Filtra por categoría.
          required: false
          description: Filtra por categoría.
          name: category
          in: query
        - schema:
            type: string
            enum:
              - friendly
              - neutral
              - firm
              - urgent
            description: Filtra por tono.
          required: false
          description: Filtra por tono.
          name: tone
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: "Filtra por activación: `true` = activas, `false` = inactivas."
          required: false
          description: "Filtra por activación: `true` = activas, `false` = inactivas."
          name: is_active
          in: query
        - schema:
            type: string
            enum:
              - name
              - channel
              - category
              - createdAt
              - updatedAt
            description: "Campo de ordenación. Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación. Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de plantillas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplateListResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-templates
    post:
      tags:
        - Plantillas de mensaje
      summary: Crear plantilla
      description: Crea una plantilla de mensaje. Falla con 409 si ya existe una plantilla con el mismo `name` o `externalId`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MessageTemplateCreateRequest"
      responses:
        "201":
          description: Plantilla creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplate"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-templates
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/templates/{id}:
    get:
      tags:
        - Plantillas de mensaje
      summary: Consultar plantilla
      description: Devuelve una plantilla por ID.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Plantilla encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplate"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-templates-id
    put:
      tags:
        - Plantillas de mensaje
      summary: Actualizar plantilla
      description: Actualiza una plantilla (parcial). Falla con 409 si el nuevo `name` o `externalId` ya está en uso.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MessageTemplateUpdateRequest"
      responses:
        "200":
          description: Plantilla actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplate"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-templates-id
    delete:
      tags:
        - Plantillas de mensaje
      summary: Eliminar plantilla
      description: Elimina (borrado lógico) una plantilla. Falla con 409 si está en uso por etapas de regla.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Plantilla eliminada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-templates-id
  /api/v1/templates/{id}/duplicate:
    post:
      tags:
        - Plantillas de mensaje
      summary: Duplicar plantilla
      description: Crea una copia de la plantilla con el nombre indicado. La copia nace inactiva (`activatedAt` null) y sin `externalId`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MessageTemplateDuplicateRequest"
      responses:
        "201":
          description: Plantilla duplicada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplate"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-templates-id-duplicate
  /api/v1/classifications:
    get:
      tags:
        - Clasificaciones
      summary: Listar clasificaciones
      description: Lista las clasificaciones de la organización, por defecto ordenadas por `priority` y `name`. Usa `include=_count` para los contadores.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filtra por la clasificación predeterminada (`true` | `false`).
          required: false
          description: Filtra por la clasificación predeterminada (`true` | `false`).
          name: is_default
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filtra por asignación automática (`true` | `false`).
          required: false
          description: Filtra por asignación automática (`true` | `false`).
          name: auto_assign
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
              - "null"
            description: "Filtra por activación: `true` = activas, `false` o `null` = inactivas."
          required: false
          description: "Filtra por activación: `true` = activas, `false` o `null` = inactivas."
          name: activated_at
          in: query
        - schema:
            type: string
            description: "Relaciones a incluir. Soportado: `_count` (clientes y reglas)."
            example: _count
          required: false
          description: "Relaciones a incluir. Soportado: `_count` (clientes y reglas)."
          name: include
          in: query
        - schema:
            type: string
            enum:
              - name
              - code
              - priority
              - createdAt
              - updatedAt
            description: Campo de ordenación (`name` | `code` | `priority` | `createdAt` | `updatedAt`).
          required: false
          description: Campo de ordenación (`name` | `code` | `priority` | `createdAt` | `updatedAt`).
          name: sort
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Dirección de ordenación (`asc` | `desc`).
          required: false
          description: Dirección de ordenación (`asc` | `desc`).
          name: order
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de clasificaciones.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClassificationListResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-classifications
    post:
      tags:
        - Clasificaciones
      summary: Crear clasificación
      description: Crea una clasificación. Marcar `isDefault` desmarca la predeterminada anterior; un `code` duplicado falla con 409.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClassificationCreateRequest"
      responses:
        "201":
          description: Clasificación creada (con contadores).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Classification"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-classifications
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/classifications/{id}:
    get:
      tags:
        - Clasificaciones
      summary: Consultar clasificación
      description: Devuelve una clasificación por ID. Usa `include=_count` para los contadores.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            description: "Relaciones a incluir. Soportado: `_count` (clientes y reglas)."
            example: _count
          required: false
          description: "Relaciones a incluir. Soportado: `_count` (clientes y reglas)."
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Clasificación encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Classification"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-classifications-id
    put:
      tags:
        - Clasificaciones
      summary: Actualizar clasificación
      description: Actualiza una clasificación (parcial). Un `code` duplicado falla con 409.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClassificationUpdateRequest"
      responses:
        "200":
          description: Clasificación actualizada (con contadores).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Classification"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-classifications-id
    delete:
      tags:
        - Clasificaciones
      summary: Eliminar clasificación
      description: Elimina (borrado lógico) una clasificación. Falla con 409 si hay clientes o reglas asociados. El `code` sigue reservado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Clasificación eliminada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-classifications-id
  /api/v1/negotiation-campaigns:
    get:
      tags:
        - Campañas de negociación
      summary: Listar campañas
      description: Lista las campañas de negociación de la organización (activas y finalizadas), de la más reciente a la más antigua. Sin paginación.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Campañas de la organización (envelope `campaigns`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NegotiationCampaignListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-negotiation-campaigns
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
    post:
      tags:
        - Campañas de negociación
      summary: Crear campaña
      description: Crea y activa una campaña de descuento. El portal del deudor comienza a ofrecerla a los cobros dentro del rango de atraso configurado.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NegotiationCampaignCreateRequest"
      responses:
        "201":
          description: Campaña creada y activada (envelope `campaign`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NegotiationCampaignCreateResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-negotiation-campaigns
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/negotiation-campaigns/{id}:
    delete:
      tags:
        - Campañas de negociación
      summary: Finalizar campaña
      description: Finaliza la campaña (desactiva; `activatedAt` pasa a null). El registro se mantiene y el portal deja de ofrecerla.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Campaña finalizada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-negotiation-campaigns-id
  /api/v1/negativations:
    get:
      tags:
        - Negativaciones
      summary: Listar negativaciones
      description: Lista las negativaciones de la organización con paginación y filtros. Usa `include=person,charge` para incluir los resúmenes de cliente y cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            enum:
              - createdAt
              - updatedAt
              - amount
              - registeredAt
            description: "Campo de ordenación (`createdAt` | `updatedAt` | `amount` | `registeredAt`). Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación (`createdAt` | `updatedAt` | `amount` | `registeredAt`). Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - pending
              - active
              - removed
              - failed
            description: Filtra por estado (`pending` | `active` | `removed` | `failed`).
          required: false
          description: Filtra por estado (`pending` | `active` | `removed` | `failed`).
          name: status
          in: query
        - schema:
            type: string
            enum:
              - serasa
              - spc
              - boa_vista
            description: Filtra por buró de crédito (`serasa` | `spc` | `boa_vista`).
          required: false
          description: Filtra por buró de crédito (`serasa` | `spc` | `boa_vista`).
          name: bureau
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cliente (UUID).
          required: false
          description: Filtra por cliente (UUID).
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cobro (UUID).
          required: false
          description: Filtra por cobro (UUID).
          name: charge_id
          in: query
        - schema:
            type: string
            description: Relaciones a incluir, separadas por coma (`person`, `charge`).
            example: person,charge
          required: false
          description: Relaciones a incluir, separadas por coma (`person`, `charge`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de negativaciones.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NegativationListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-negativations
    post:
      tags:
        - Negativaciones
      summary: Crear negativación
      description: Registra una negativación para un cobro. El registro nace `pending` y SOLO se envía al buró tras la revisión y aprobación humana (flujo legal; evento `negativation.review_required`). Devuelve 409 si ya existe una negativación `pending`/`active` para el mismo cobro en el mismo buró.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NegativationCreateRequest"
      responses:
        "201":
          description: Negativación creada (esperando revisión humana).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Negativation"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-negativations
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/negativations/{id}:
    get:
      tags:
        - Negativaciones
      summary: Consultar negativación
      description: Devuelve una negativación por ID, con los resúmenes de cliente y cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Negativación encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Negativation"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-negativations-id
    put:
      tags:
        - Negativaciones
      summary: Actualizar negativación
      description: "Actualiza una negativación (parcial). Es la etapa de aprobación del flujo legal: exige el permiso de revisión (`approve`) y es donde el estado pasa de `pending` a `active` tras la revisión humana."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NegativationUpdateRequest"
      responses:
        "200":
          description: Negativación actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Negativation"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-negativations-id
    delete:
      tags:
        - Negativaciones
      summary: Eliminar negativación
      description: "Baja lógica: marca la negativación como `removed` y registra `removedAt`. El registro permanece para la pista de auditoría."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Negativación eliminada.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-negativations-id
  /api/v1/negativations/{id}/remove:
    post:
      tags:
        - Negativaciones
      summary: Solicitar baja de la negativación
      description: Solicita la baja de una negativación en el buró. Solo se permite para registros `active`; el estado pasa a `removed` y el motivo (si se envía) queda en `removalReason`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NegativationRemoveRequest"
      responses:
        "200":
          description: Baja registrada; negativación actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Negativation"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-negativations-id-remove
  /api/v1/protests:
    get:
      tags:
        - Protestos
      summary: Listar protestos
      description: Lista los protestos de la organización con paginación y filtros. Usa `include=person,charge` para incluir los resúmenes de cliente y cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            enum:
              - createdAt
              - updatedAt
              - amount
              - protestDate
              - sentAt
            description: "Campo de ordenación (`createdAt` | `updatedAt` | `amount` | `protestDate` | `sentAt`). Alias legado: `sortField`."
            example: createdAt
          required: false
          description: "Campo de ordenación (`createdAt` | `updatedAt` | `amount` | `protestDate` | `sentAt`). Alias legado: `sortField`."
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          required: false
          description: "Dirección de ordenación (`asc` | `desc`). Alias legado: `sortDirection`."
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - pending
              - sent
              - intimated
              - protested
              - paid
              - cancelled
            description: Filtra por estado (`pending` | `sent` | `intimated` | `protested` | `paid` | `cancelled`).
          required: false
          description: Filtra por estado (`pending` | `sent` | `intimated` | `protested` | `paid` | `cancelled`).
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cliente (UUID).
          required: false
          description: Filtra por cliente (UUID).
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cobro (UUID).
          required: false
          description: Filtra por cobro (UUID).
          name: charge_id
          in: query
        - schema:
            type: string
            description: Relaciones a incluir, separadas por coma (`person`, `charge`).
            example: person,charge
          required: false
          description: Relaciones a incluir, separadas por coma (`person`, `charge`).
          name: include
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de protestos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProtestListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-protests
    post:
      tags:
        - Protestos
      summary: Crear protesto
      description: Registra un protesto para un cobro. El registro nace `pending` y SOLO se envía a la notaría tras la revisión y aprobación humana (flujo legal; evento `protest.review_required`). Devuelve 409 si ya existe un protesto activo (`pending`/`sent`/`intimated`/`protested`) para el mismo cobro.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProtestCreateRequest"
      responses:
        "201":
          description: Protesto creado (esperando revisión humana).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Protest"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-protests
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/protests/{id}:
    get:
      tags:
        - Protestos
      summary: Consultar protesto
      description: Devuelve un protesto por ID, con los resúmenes de cliente y cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Protesto encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Protest"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-protests-id
    put:
      tags:
        - Protestos
      summary: Actualizar protesto
      description: "Actualiza un protesto (parcial). Es la etapa de aprobación del flujo legal: exige el permiso de revisión (`approve`) y es donde el estado avanza de `pending` a `sent`/`intimated`/`protested` tras la revisión humana."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProtestUpdateRequest"
      responses:
        "200":
          description: Protesto actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Protest"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-protests-id
    delete:
      tags:
        - Protestos
      summary: Eliminar protesto
      description: "Baja lógica: marca el protesto como `cancelled` y registra `cancellationDate`. El registro permanece para la pista de auditoría."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Protesto cancelado.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-protests-id
  /api/v1/protests/{id}/cancel:
    post:
      tags:
        - Protestos
      summary: Solicitar cancelación del protesto
      description: Solicita la cancelación de un protesto. No se permite para protestos ya `cancelled` ni `paid`; el estado pasa a `cancelled` y el motivo (si se envía) queda en `responseData.cancellationReason`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProtestCancelRequest"
      responses:
        "200":
          description: Cancelación registrada; protesto actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Protest"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-protests-id-cancel
  /api/v1/imports:
    get:
      tags:
        - Importaciones
      summary: Listar importaciones
      description: Historial de las 50 importaciones más recientes de la organización (sin paginación). El contenido del archivo y los errores por línea quedan fuera; consulta GET /imports/{id} para el detalle.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Lista de las importaciones más recientes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImportListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-imports
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
    post:
      tags:
        - Importaciones
      summary: Crear importación
      description: "Sube una planilla CSV de clientes o cobros y agenda el procesamiento asíncrono (cola). El cuerpo es JSON con el CSV en texto plano en el campo `content` (máx. 2 MB). Las líneas inválidas no tumban el lote: se registran en `errors` (`[{ line, error }]`) y el resto se procesa. Sigue el avance con GET /imports/{id}. Responde 202."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImportCreateRequest"
      responses:
        "202":
          description: Importación aceptada y encolada para procesamiento.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImportCreatedResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "413":
          description: Archivo mayor a 2 MB — divide la planilla en partes más pequeñas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-imports
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/imports/{id}:
    get:
      tags:
        - Importaciones
      summary: Consultar importación
      description: "Devuelve el estado de una importación con contadores de progreso y los errores por línea rechazada (`errors: [{ line, error }]`; la línea 1 es el encabezado, los datos empiezan en la línea 2)."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Importación encontrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImportShowResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-imports-id
  /api/v1/exports/people:
    get:
      tags:
        - Exportaciones
      summary: Exportar clientes (CSV síncrono)
      description: "Genera y responde al instante el CSV de clientes (`text/csv; charset=utf-8` con BOM, `Content-Disposition: attachment`). Columnas: nome, documento, tipo_documento, email, telefone, classificacao, external_id, tags, criado_em. Tope de 10 mil líneas; por encima usa la exportación asíncrona (POST /exports/jobs)."
      security:
        - bearerAuth: []
      responses:
        "200":
          description: "Archivo CSV (`text/csv; charset=utf-8` con BOM para que Excel muestre los acentos correctamente; `Content-Disposition: attachment`)."
          content:
            text/csv:
              schema:
                type: string
                description: Contenido del archivo CSV.
                example: |-
                  nome,documento,email
                  Maria da Silva,12345678901,maria@example.com
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-exports-people
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
  /api/v1/exports/charges:
    get:
      tags:
        - Exportaciones
      summary: Exportar cobros (CSV síncrono)
      description: "Genera y responde al instante el CSV de cobros (`text/csv; charset=utf-8` con BOM, `Content-Disposition: attachment`), con filtros opcionales de estado y período de vencimiento. Columnas: cliente, documento_cliente, numero_documento, descricao, valor_original, valor_atual, vencimento, dias_atraso, status, origem, external_id. Tope de 10 mil líneas; por encima usa la exportación asíncrona (POST /exports/jobs)."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Filtra los cobros por estado (ej.: `pending`, `overdue`, `paid`)."
            example: overdue
          required: false
          description: "Filtra los cobros por estado (ej.: `pending`, `overdue`, `paid`)."
          name: status
          in: query
        - schema:
            type: string
            format: date
            description: Vencimiento desde (fecha ISO `YYYY-MM-DD`).
            example: 2026-01-01
          required: false
          description: Vencimiento desde (fecha ISO `YYYY-MM-DD`).
          name: from
          in: query
        - schema:
            type: string
            format: date
            description: Vencimiento hasta (fecha ISO `YYYY-MM-DD`).
            example: 2026-06-30
          required: false
          description: Vencimiento hasta (fecha ISO `YYYY-MM-DD`).
          name: to
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: "Archivo CSV (`text/csv; charset=utf-8` con BOM para que Excel muestre los acentos correctamente; `Content-Disposition: attachment`)."
          content:
            text/csv:
              schema:
                type: string
                description: Contenido del archivo CSV.
                example: |-
                  nome,documento,email
                  Maria da Silva,12345678901,maria@example.com
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-exports-charges
  /api/v1/exports/jobs:
    get:
      tags:
        - Exportaciones
      summary: Listar exportaciones asíncronas
      description: Historial de las 50 exportaciones asíncronas más recientes de la organización (sin paginación). Usa esta ruta para seguir el estado de los jobs; el CSV en sí no viene en el listado.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Lista de las exportaciones más recientes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExportJobListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-exports-jobs
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
    post:
      tags:
        - Exportaciones
      summary: Crear exportación asíncrona
      description: Agenda la generación del CSV en un worker, sin el tope de 10 mil líneas del modo síncrono y sin bloquear la request. Sigue el estado en GET /exports/jobs y descarga el archivo en GET /exports/jobs/{id}/download antes de `expiresAt`. Responde 202.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExportJobCreateRequest"
      responses:
        "202":
          description: Exportación aceptada y encolada para procesamiento.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExportJobCreatedResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-exports-jobs
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/exports/jobs/{id}/download:
    get:
      tags:
        - Exportaciones
      summary: Descargar CSV de la exportación
      description: Entrega el CSV ya generado de una exportación asíncrona (`text/csv; charset=utf-8` con BOM). Solo disponible cuando el job está `completed` y dentro de la validez (`expiresAt`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: "Archivo CSV (`text/csv; charset=utf-8` con BOM para que Excel muestre los acentos correctamente; `Content-Disposition: attachment`)."
          content:
            text/csv:
              schema:
                type: string
                description: Contenido del archivo CSV.
                example: |-
                  nome,documento,email
                  Maria da Silva,12345678901,maria@example.com
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Exportación aún no concluida (código `NOT_READY`; el campo `status` informa el estado actual).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "410":
          description: Exportación expirada — genera una nueva (código `EXPIRED`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-exports-jobs-id-download
  /api/v1/webhook-endpoints:
    get:
      tags:
        - Webhooks
      summary: Listar endpoints de webhook
      description: Lista los endpoints de webhook de la organización con contadores de entrega por estado. `secret` y `authConfig` nunca aparecen en el listado.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Lista de endpoints.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-webhook-endpoints
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
    post:
      tags:
        - Webhooks
      summary: Crear endpoint de webhook
      description: Crea un endpoint de webhook. La URL exige HTTPS y se valida contra direcciones internas (SSRF). El `secret` se devuelve solo en esta respuesta — guárdalo con seguridad.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEndpointCreateRequest"
      responses:
        "201":
          description: Endpoint creado (incluye `secret` — se muestra una única vez).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointCreateResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-webhook-endpoints
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/webhook-endpoints/{id}:
    get:
      tags:
        - Webhooks
      summary: Consultar endpoint de webhook
      description: Devuelve el endpoint con sus últimas 20 entregas. El `secret` nunca se devuelve.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Endpoint con entregas recientes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointShowResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-webhook-endpoints-id
    patch:
      tags:
        - Webhooks
      summary: Actualizar endpoint de webhook
      description: Actualiza el endpoint (parcial). La URL, si se envía, se revalida (HTTPS + SSRF); `authConfig` se cifra y nunca se devuelve.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEndpointUpdateRequest"
      responses:
        "200":
          description: Endpoint actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointUpdateResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: patch-webhook-endpoints-id
    delete:
      tags:
        - Webhooks
      summary: Eliminar endpoint de webhook
      description: Elimina (borrado lógico) el endpoint; las entregas pendientes dejan de procesarse.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "204":
          description: Endpoint eliminado.
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: delete-webhook-endpoints-id
  /api/v1/webhook-endpoints/{id}/rotate-secret:
    post:
      tags:
        - Webhooks
      summary: Rotar secret
      description: Genera un nuevo secret HMAC; el anterior deja de validar de inmediato. El nuevo secret se devuelve solo en esta respuesta — guárdalo con seguridad.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Nuevo secret (se muestra una única vez).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointRotateSecretResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-webhook-endpoints-id-rotate-secret
  /api/v1/webhook-endpoints/{id}/test:
    post:
      tags:
        - Webhooks
      summary: Disparar evento de prueba
      description: Entrega un evento `webhook.test` REAL al endpoint, por el mismo pipeline de los eventos de negocio (cola, firma HMAC, captura de request/response, retry) — valida tu consumidor de punta a punta sin esperar un evento real. Entregado solo al endpoint objetivo, independiente de la lista de eventos suscritos. Falla con 409 si el endpoint está inactivo.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      responses:
        "202":
          description: Evento de prueba encolado para entrega.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointTestResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-webhook-endpoints-id-test
  /api/v1/webhook-endpoints/{id}/deliveries:
    get:
      tags:
        - Webhooks
      summary: Listar entregas del endpoint
      description: Lista las entregas del endpoint (paginado, más recientes primero) con la captura de request/response para inspección y debug.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            enum:
              - pending
              - success
              - failed
              - exhausted
            description: Filtra por el estado de la entrega.
          required: false
          description: Filtra por el estado de la entrega.
          name: status
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de entregas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDeliveryListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-webhook-endpoints-id-deliveries
  /api/v1/webhook-endpoints/{id}/deliveries/{deliveryId}/retry:
    post:
      tags:
        - Webhooks
      summary: Reenviar entrega
      description: Reencola una entrega para un reintento inmediato (el estado vuelve a `pending`). Falla con 409 si el endpoint está inactivo o eliminado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) del recurso.
          required: true
          description: Identificador (UUID) del recurso.
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: Identificador (UUID) de la entrega.
          required: true
          description: Identificador (UUID) de la entrega.
          name: deliveryId
          in: path
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Entrega reencolada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDeliveryRetryResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Conflicto con el estado actual del recurso (o clave de idempotencia reutilizada).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-webhook-endpoints-id-deliveries-deliveryid-retry
  /api/v1/audit-logs:
    get:
      tags:
        - Pista de auditoría
      summary: Consultar pista de auditoría
      description: Lista los registros de la pista de auditoría de la organización (más recientes primero), con filtros por acción, entidad, actor y período.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            exclusiveMinimum: 0
            description: Número de página (desde 1).
            example: 1
          required: false
          description: Número de página (desde 1).
          name: page
          in: query
        - schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 100
            description: "Elementos por página (máx. 100). Alias legado: `limit`."
            example: 20
          required: false
          description: "Elementos por página (máx. 100). Alias legado: `limit`."
          name: per_page
          in: query
        - schema:
            type: string
            description: Filtra por acción (búsqueda parcial, `contains`).
          required: false
          description: Filtra por acción (búsqueda parcial, `contains`).
          name: action
          in: query
        - schema:
            type: string
            description: Filtra por tipo de entidad.
          required: false
          description: Filtra por tipo de entidad.
          name: entity_type
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por entidad (UUID).
          required: false
          description: Filtra por entidad (UUID).
          name: entity_id
          in: query
        - schema:
            type: string
            enum:
              - user
              - system
              - job
            description: Filtra por tipo de actor (`user` | `system` | `job`).
          required: false
          description: Filtra por tipo de actor (`user` | `system` | `job`).
          name: actor_type
          in: query
        - schema:
            type: string
            description: Filtra por actor (ID del usuario, nombre del job o del sistema).
          required: false
          description: Filtra por actor (ID del usuario, nombre del job o del sistema).
          name: actor_id
          in: query
        - schema:
            type: string
            format: date-time
            description: Inicio del período (ISO 8601, inclusive).
          required: false
          description: Inicio del período (ISO 8601, inclusive).
          name: from
          in: query
        - schema:
            type: string
            format: date-time
            description: Fin del período (ISO 8601, inclusive).
          required: false
          description: Fin del período (ISO 8601, inclusive).
          name: to
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Lista paginada de registros de auditoría.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuditLogListResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-audit-logs
  /api/v1/metrics/recovery:
    get:
      tags:
        - Métricas
      summary: Métricas de recuperación
      description: "Devuelve las métricas de recuperación del período (por defecto: últimos 6 meses, contados desde el primer día del mes inicial)."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            maximum: 24
            description: Ventana en meses (1–24; por defecto 6).
            example: 6
          required: false
          description: Ventana en meses (1–24; por defecto 6).
          name: months
          in: query
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
      responses:
        "200":
          description: Métricas calculadas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RecoveryMetrics"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-metrics-recovery
  /api/v1/email-layouts:
    get:
      tags:
        - Layouts de correo
      summary: Listar layouts de correo
      description: Lista los layouts de correo de la organización (predeterminado primero) y el layout integrado de referencia.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Lista de layouts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailLayoutListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-email-layouts
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
    post:
      tags:
        - Layouts de correo
      summary: Crear layout de correo
      description: "Crea un layout de correo. El `htmlBody` debe contener el placeholder `{{content}}` (si no, 400 `MISSING_CONTENT_PLACEHOLDER`). `isDefault: true` desmarca el predeterminado anterior."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailLayoutCreateRequest"
      responses:
        "201":
          description: Layout creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailLayoutCreateResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: post-email-layouts
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
  /api/v1/notification-preferences:
    get:
      tags:
        - Preferencias de notificación
      summary: Consultar preferencias de notificación
      description: Devuelve el gate efectivo por tipo de mensaje — los tipos sin registro guardado aparecen con todos los canales habilitados.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Preferencias efectivas por tipo.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationPreferenceListResponse"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: get-notification-preferences
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
    put:
      tags:
        - Preferencias de notificación
      summary: Actualizar preferencias de notificación
      description: "Hace upsert de las preferencias enviadas (parcial: solo los tipos incluidos se modifican)."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationPreferenceUpdateRequest"
      responses:
        "200":
          description: Preferencias guardadas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotificationPreferenceUpdateResponse"
        "400":
          description: Solicitud inválida (error de validación).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Clave de API ausente, inválida, expirada o revocada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: La clave no tiene el alcance requerido por la operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Recurso inexistente o fuera de tu organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Límite de solicitudes excedido. Espera y reintenta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Error interno. Reintenta; si persiste, contacta al soporte.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      operationId: put-notification-preferences
      parameters:
        - name: User-Agent
          in: header
          required: true
          description: "Identificación del integrador. Incluya nombre y correo de contacto — ej.: `Kevin Mitnick <kmitnick@kobana.com.br>`. Usado para soporte y auditoría."
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@kobana.com.br>
          example: Kevin Mitnick <kmitnick@kobana.com.br>
        - name: X-Idempotency-Key
          in: header
          required: false
          description: "Clave única (se recomienda UUID v4) para reintentar la misma solicitud sin efectos secundarios. Solicitudes con la misma clave en 24h devuelven el resultado original (header `X-Idempotent-Replay: true`)."
          schema:
            type: string
            format: uuid
webhooks: {}
