openapi: 3.1.0
info:
  title: API do Faturamento Automático
  version: 1.0.0
  description: |-
    ¡Tú que eres dev, diviértete! ✨

    > **Índice de la documentación** — accede al índice completo en [https://docs.billing.kobana.com.br/llms.txt](https://docs.billing.kobana.com.br/llms.txt). Usa este archivo para descubrir todas las páginas disponibles antes de explorar.

    > **Atención a las versiones de la API** — estás en la documentación de la **v1.0** de la API de Facturación Automática, que cubre los endpoints de **Suscripciones** (creación, cambio de plan, pausa, cancelación, facturación manual, sincronización de ítems) y recursos relacionados. Recursos como **Pagos, Transferencias, Extracto y Recepción por Pix** están disponibles en otras APIs de Kobana — usa el selector de producto en el menú superior para acceder.

    ## Formato

    La API solo acepta `JSON`. Todas las solicitudes deben usar `Content-Type: application/json`. Todas las respuestas usan `snake_case`.

    | Tipo de Campo | Formato |
    | :--- | :--- |
    | **DateTime** | Formato [ISO8601](https://es.wikipedia.org/wiki/ISO_8601). Ejemplos — Fecha: `2026-01-24`. Fecha y hora: `2026-01-24T10:07Z` |
    | **Money (`_cents`)** | Entero en centavos (÷100). Ej.: `10000` = R$ 100,00 |
    | **Money (`_subcents`)** | Entero en subcentavos (÷10000), usado en precios. Ej.: `1000000` = R$ 100,00 |
    | **UUID** | Identificadores de recursos en formato UUID v4 |

    ## Convenciones

    Convenciones usadas en esta documentación:

    | Convención | Descripción |
    | :--- | :--- |
    | **`:variable`** | Nombre de variable que debe sustituirse en una URL. |
    | **`#{variable}`** | Nombre de variable que debe sustituirse por valores de tu cuenta. |
    | **`...`** | Contenido de la respuesta truncado para facilitar la lectura. |
    | **`$KOBANA_TOKEN`** | Token de acceso. Para pruebas en línea de comandos, expórtalo: `export KOBANA_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxx` y pega los comandos de la documentación en la terminal. |

    ## Códigos de Retorno

    La API devuelve códigos HTTP estándar:

    | | Código | Descripción |
    | :--- | :--- | :--- |
    | ✅ | **200 OK** | Solicitud exitosa con cuerpo de respuesta. |
    | ✅ | **201 Created** | Recurso creado con éxito. |
    | ✅ | **204 No Content** | Solicitud exitosa sin cuerpo de respuesta. |
    | ❌ | **400 Bad Request** | Solicitud inválida, generalmente contenido mal formado. |
    | ❌ | **401 Unauthorized** | Token de acceso ausente o inválido. |
    | ❌ | **403 Forbidden** | Acceso a la API bloqueado o usuario sin permiso. |
    | ❌ | **404 Not Found** | El recurso no existe. |
    | ❌ | **422 Unprocessable Entity** | Solicitud válida, pero los datos enviados no lo son. |
    | ❌ | **429 Too Many Requests** | Límite de solicitudes alcanzado. |
    | ❌ | **500 Internal Server Error** | Error interno de procesamiento. Consulta el [estado de los servidores](https://status.kobana.com.br). |

    ## ID de las Solicitudes (Request ID)

    Cada solicitud tiene un identificador asociado, disponible en la cabecera `Request-Id` de la respuesta. Este valor ayuda en la depuración y auditoría — las solicitudes y sus IDs pueden consultarse en el panel del sistema. El registro de solicitudes está disponible por **30 días**. Al abrir un ticket de soporte sobre una solicitud específica, informa el `Request-Id` para acelerar la investigación.

    ## Seguridad

    La API de Kobana usa certificados **SSL 2048 bits**. Toda solicitud debe hacerse vía **HTTPS** — las llamadas al puerto 80 son redirigidas al 443.

    Los clientes deben soportar `TLSv1.2` o `TLSv1.3` con una de las cifras: `TLS_AES_128_GCM_SHA256`, `TLS_AES_256_GCM_SHA384`, `TLS_CHACHA20_POLY1305_SHA256`, `ECDHE-RSA-AES128-GCM-SHA256`, `ECDHE-RSA-AES128-SHA256`, `ECDHE-RSA-AES256-GCM-SHA384`, `ECDHE-RSA-CHACHA20-POLY1305`, `ECDHE-RSA-AES256-SHA384`. `TLSv1` y `TLSv1.1` no son soportados.

    ## Caché HTTP

    Usa las cabeceras HTTP de caché para reducir carga y ganar velocidad. La mayoría de las respuestas incluye `ETag` y/o `Last-Modified` — guarda estos valores y reenvíalos en las siguientes solicitudes vía `If-None-Match` e `If-Modified-Since`. Si el recurso no cambió, la respuesta será `304 Not Modified`, sin cuerpo y sin reprocesamiento. Más información: [HTTP Cache Docs](http://www.mnot.net/cache_docs/).

    ## Manejo de Errores

    Los errores 5xx indican fallas del servidor: **500 Internal Server Error** (aplicación indisponible), **502 Bad Gateway**, **503 Service Unavailable** y **504 Gateway Timeout** (fallas puntuales de infraestructura). Tu aplicación debe identificar estos códigos y reintentar la solicitud después de algunos minutos con backoff. Estado de los servidores: [https://status.kobana.com.br](https://status.kobana.com.br).

    ## Valores monetarios

    Los valores monetarios siguen dos convenciones: campos con sufijo `_cents` (÷100) y campos de precios con sufijo `_subcents` (÷10000). Usa estas convenciones para convertir a reales (BRL) antes de mostrar al usuario final.
  license:
    name: Proprietário — Kobana
    url: https://kobana.com.br
servers:
  - url: https://api.billing.kobana.com.br
    description: Producción. Sustituido en tiempo de ejecución cuando `API_V1_PUBLIC_URL` está definido.
  - url: https://api.billing.sandbox.kobana.com.br
    description: Sandbox — entorno de pruebas aislado de producción.
components:
  schemas:
    ApiV1Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Código de error estable. Usa este campo para tratar el error programáticamente — nunca hagas parse del `message`.
              example: invalid_token
            message:
              type: string
              description: Mensaje legible para humanos. Nunca incluye datos del usuario verbatim.
              example: Cabeçalho Authorization ausente.
            details:
              description: "Payload opcional específico del error (ej.: `{ required, granted }` en `insufficient_scope`)."
          required:
            - code
            - message
          description: Envelope de error. Presente en toda respuesta 4xx y 5xx.
      required:
        - error
    PaginationMeta:
      type: object
      properties:
        total:
          type: integer
          description: Total de ítems en todas las páginas.
        page:
          type: integer
          description: Página actual (comienza en 1).
        per_page:
          type: integer
          description: Ítems por página.
        total_pages:
          type: integer
          description: Total de páginas.
      required:
        - total
        - page
        - per_page
        - total_pages
    DeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    SubscriptionV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        billing_account_id:
          type: string
          format: uuid
        company_id:
          type:
            - string
            - "null"
          format: uuid
        plan_id:
          type:
            - string
            - "null"
          format: uuid
        product_group_id:
          type:
            - string
            - "null"
          format: uuid
        status:
          type: string
          enum:
            - draft
            - confirmed
            - trialing
            - active
            - past_due
            - canceled
            - paused
          description: "Estado de la suscripción. Ciclo: `draft` → `confirmed` → `trialing` → `active` ↔ `past_due` → `canceled`. También acepta `paused`."
        billing_cycle:
          type: string
          enum:
            - monthly
            - quarterly
            - semiannual
            - annual
          description: "Periodicidad de cobro: mensual, trimestral, semestral o anual."
        billing_cycle_anchor:
          type: string
          format: date-time
          description: Fecha ancla usada en el cálculo de los períodos de cobro.
        current_period_start:
          type: string
          format: date-time
          description: Inicio del período de cobro actual.
        current_period_end:
          type: string
          format: date-time
          description: Fin del período de cobro actual.
        trial_start:
          type:
            - string
            - "null"
          format: date-time
        trial_end:
          type:
            - string
            - "null"
          format: date-time
        activated_at:
          type:
            - string
            - "null"
          format: date-time
        confirmed_at:
          type:
            - string
            - "null"
          format: date-time
        cancel_at_period_end:
          type: boolean
          description: Cuando es `true`, la suscripción se cancela al fin del período actual en vez de inmediatamente.
        canceled_at:
          type:
            - string
            - "null"
          format: date-time
        cancellation_reason:
          type:
            - string
            - "null"
        pause_collection:
          description: Cuando está pausada, contiene `{ behavior, resumes_at }`. `null` cuando está activa.
        paused_at:
          type:
            - string
            - "null"
          format: date-time
        resumes_at:
          type:
            - string
            - "null"
          format: date-time
        resumed_at:
          type:
            - string
            - "null"
          format: date-time
        discount_code_id:
          type:
            - string
            - "null"
          format: uuid
        discount_percent:
          type:
            - string
            - "null"
          description: 'Decimal como string (ej.: `"10.00"` = 10%). Rango: 0–100.'
        recurring_amount_cents:
          type:
            - integer
            - "null"
          description: Importe recurrente en centavos (÷100 → BRL).
          example: 9990
        proration_behavior:
          type: string
          enum:
            - create_prorations
            - none
            - always_invoice
          description: "Comportamiento de prorrata al cambiar de plan. `create_prorations` (por defecto): genera ajuste; `none`: ignora; `always_invoice`: emite factura inmediata."
        collection_method:
          type: string
          enum:
            - charge_automatically
            - manual_charge
            - manual_invoice
          description: "`charge_automatically`: facturación y cobro automáticos. `manual_charge`: facturación automática, cobro manual. `manual_invoice`: ambos manuales."
        nfe_issuance_policy:
          type:
            - string
            - "null"
          enum:
            - disabled
            - on_finalization
            - on_full_payment
            - per_installment
          description: "Política de emisión de NF-e: `disabled`, `on_finalization`, `on_full_payment` o `per_installment`."
        renewal_settings:
          description: '`{ type: "auto_renew" | "cancel" | "downgrade_to_flexible", ... }`.'
        commitment: {}
        allow_pause:
          type: boolean
        allow_cancel:
          type: boolean
        proposal_id:
          type:
            - string
            - "null"
          format: uuid
        notes:
          type:
            - string
            - "null"
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - billing_account_id
        - company_id
        - plan_id
        - product_group_id
        - status
        - billing_cycle
        - billing_cycle_anchor
        - current_period_start
        - current_period_end
        - trial_start
        - trial_end
        - activated_at
        - confirmed_at
        - cancel_at_period_end
        - canceled_at
        - cancellation_reason
        - discount_code_id
        - discount_percent
        - recurring_amount_cents
        - proration_behavior
        - collection_method
        - nfe_issuance_policy
        - allow_pause
        - allow_cancel
        - proposal_id
        - notes
        - custom_metadata
        - created_at
        - updated_at
    SubscriptionListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/SubscriptionV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    SubscriptionResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/SubscriptionV1"
      required:
        - data
    SubscriptionCreateRequest:
      type: object
      properties:
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro que recibirá esta suscripción.
        company_id:
          type: string
          format: uuid
        plan_id:
          type: string
          format: uuid
          description: UUID del plan. Obligatorio cuando no se envía `items`.
        product_group_id:
          type: string
          format: uuid
        billing_cycle:
          type: string
          enum:
            - monthly
            - quarterly
            - semiannual
            - annual
        collection_method:
          type: string
          enum:
            - charge_automatically
            - manual_charge
            - manual_invoice
        trial_end:
          type: string
          format: date-time
          description: Fecha de finalización del trial (ISO 8601).
        trial_period_days:
          type: integer
          minimum: 0
          description: Alternativa a `trial_end`. Días de trial a partir de la creación.
        discount_code_id:
          type: string
          format: uuid
        proration_behavior:
          type: string
          enum:
            - create_prorations
            - none
            - always_invoice
        nfe_issuance_policy:
          type: string
          enum:
            - disabled
            - on_finalization
            - on_full_payment
            - per_installment
        items:
          type: array
          items:
            type: object
            properties:
              product_id:
                type: string
                format: uuid
              price_id:
                type: string
                format: uuid
              quantity:
                type: integer
                minimum: 1
                default: 1
              unit_amount_subcents:
                type: integer
                minimum: 0
                description: Precisión de 4 decimales (÷10000 → BRL). Sobrescribe el importe del precio.
            required:
              - product_id
              - price_id
        notes:
          type:
            - string
            - "null"
        custom_metadata:
          type: object
          additionalProperties: {}
      required:
        - billing_account_id
    SubscriptionUpdateRequest:
      type: object
      properties:
        plan_id:
          type:
            - string
            - "null"
          format: uuid
        billing_cycle:
          type: string
          enum:
            - monthly
            - quarterly
            - semiannual
            - annual
        collection_method:
          type: string
          enum:
            - charge_automatically
            - manual_charge
            - manual_invoice
        cancel_at_period_end:
          type: boolean
        discount_code_id:
          type:
            - string
            - "null"
          format: uuid
        discount_percent:
          type:
            - number
            - "null"
          minimum: 0
          maximum: 100
        proration_behavior:
          type: string
          enum:
            - create_prorations
            - none
            - always_invoice
        nfe_issuance_policy:
          type:
            - string
            - "null"
          enum:
            - disabled
            - on_finalization
            - on_full_payment
            - per_installment
        allow_pause:
          type: boolean
        allow_cancel:
          type: boolean
        notes:
          type:
            - string
            - "null"
        custom_metadata:
          type: object
          additionalProperties: {}
    SubscriptionPauseRequest:
      type: object
      properties:
        behavior:
          type: string
          enum:
            - mark_uncollectible
            - keep_as_draft
            - void
          description: "Cómo tratar facturas generadas durante la pausa. Por defecto: `mark_uncollectible`."
        resumes_at:
          type: string
          format: date-time
          description: Fecha para reanudación automática. Omita para reanudación manual.
    SubscriptionChangePlanRequest:
      type: object
      properties:
        plan_id:
          type: string
          format: uuid
          description: UUID del nuevo plan.
        timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Cuándo entra en vigor el cambio de plan. Por defecto: `immediate`."
        reason:
          type: string
          description: Motivo del cambio (opcional, registrado en el audit log).
      required:
        - plan_id
    SubscriptionCancelRequest:
      type: object
      properties:
        cancel_at_period_end:
          type: boolean
          description: "Cuando es `true`, programa la cancelación para el fin del período actual. Por defecto: `false` (cancela inmediatamente)."
        reason:
          type: string
    BillingAccountCustomerV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        external_id:
          type:
            - string
            - "null"
        name:
          type: string
        nickname:
          type:
            - string
            - "null"
        legal_name:
          type:
            - string
            - "null"
        document_type:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
        document_number:
          type:
            - string
            - "null"
        kind:
          type:
            - string
            - "null"
          enum:
            - individual
            - company
        birthday:
          type:
            - string
            - "null"
        notes:
          type:
            - string
            - "null"
        addresses: {}
        phones: {}
        emails: {}
        websites: {}
        tags:
          type:
            - array
            - "null"
          items:
            type: string
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - name
    BillingAccountV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        external_id:
          type:
            - string
            - "null"
          description: Identificador externo de la cuenta en el sistema del cliente.
        currency:
          type: string
          description: "Moneda ISO 4217 (ej.: `BRL`)."
          example: BRL
        billing_email:
          type:
            - string
            - "null"
          format: email
          description: Correo al que se envían facturas y avisos.
        payment_terms_days:
          type: integer
          description: "Plazo de pago en días (por defecto: 10)."
          example: 10
        auto_collection:
          type: boolean
          description: Cuando es `true`, los cobros se realizan automáticamente.
        balance_cents:
          type: integer
          description: Saldo de la cuenta en centavos (÷100 → BRL).
          example: 0
        status:
          type: string
          enum:
            - active
            - suspended
            - closed
          description: "Estado de la cuenta: `active`, `suspended` o `closed`."
        suspended_at:
          type:
            - string
            - "null"
          format: date-time
        suspension_reason:
          type:
            - string
            - "null"
        tax_exempt:
          type: boolean
          description: Indica si la cuenta está exenta de impuestos.
        tax_exemption_certificate:
          type:
            - string
            - "null"
          description: Número del certificado de exención tributaria.
        customer_tax_type:
          type:
            - string
            - "null"
          enum:
            - individual
            - business
            - exempt
          description: "Tipo tributario del cliente: persona física, jurídica o exento."
        withhold_iss:
          type: boolean
          description: Retener ISS.
        withhold_irrf:
          type: boolean
          description: Retener IRRF.
        withhold_csrf:
          type: boolean
          description: Retener CSRF (PIS/COFINS/CSLL).
        withhold_inss:
          type: boolean
          description: Retener INSS.
        municipal_registration:
          type:
            - string
            - "null"
          description: Inscripción municipal del cliente.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        customer:
          allOf:
            - $ref: "#/components/schemas/BillingAccountCustomerV1"
            - description: Cliente asociado, presente cuando la relación está cargada.
      required:
        - id
        - external_id
        - currency
        - billing_email
        - payment_terms_days
        - auto_collection
        - balance_cents
        - status
        - suspended_at
        - suspension_reason
        - tax_exempt
        - tax_exemption_certificate
        - customer_tax_type
        - withhold_iss
        - withhold_irrf
        - withhold_csrf
        - withhold_inss
        - municipal_registration
        - created_at
        - updated_at
    BillingAccountListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/BillingAccountV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    BillingAccountResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/BillingAccountV1"
      required:
        - data
    BillingAccountCustomerInput:
      type: object
      properties:
        id:
          type: string
          description: UUID o external_code del cliente existente.
        external_id:
          type: string
          description: External code para localizar/crear el cliente.
        document_number:
          type: string
          description: CPF o CNPJ para localizar/crear el cliente.
        name:
          type: string
          description: Obligatorio al crear un nuevo cliente.
        nickname:
          type: string
        legal_name:
          type: string
        kind:
          type: string
          enum:
            - individual
            - company
        birthday:
          type: string
        email:
          type: string
          format: email
        phone:
          type: string
        notes:
          type: string
        addresses: {}
        phones: {}
        emails: {}
        websites: {}
        tags:
          type: array
          items:
            type: string
        custom_metadata:
          type: object
          additionalProperties: {}
    BillingAccountCreateRequest:
      type: object
      properties:
        customer:
          allOf:
            - $ref: "#/components/schemas/BillingAccountCustomerInput"
            - description: Cliente a asociar — existente o nuevo.
        external_id:
          type: string
          description: Identificador externo de la cuenta en el sistema del cliente.
        billing_email:
          type: string
          format: email
          description: "Correo de cobro. Por defecto: `customer.email`."
        payment_terms_days:
          type: integer
          minimum: 1
          maximum: 90
          description: "Plazo de pago en días (1–90). Por defecto: 10."
        auto_collection:
          type: boolean
          description: "Habilita el cobro automático. Por defecto: `true`."
      required:
        - customer
    BillingAccountUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        email:
          type: string
          format: email
        phone:
          type: string
        address:
          type: object
          additionalProperties: {}
        billing_email:
          type: string
          format: email
        payment_terms_days:
          type: integer
          minimum: 1
          maximum: 90
        auto_collection:
          type: boolean
        custom_metadata:
          type: object
          additionalProperties: {}
    BillingAccountReasonRequest:
      type: object
      properties:
        reason:
          type: string
          description: Motivo de la operación (registrado en el audit log).
    CustomerV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        external_id:
          type:
            - string
            - "null"
          description: Identificador externo del cliente (alias interno `external_code`).
        name:
          type: string
        nickname:
          type:
            - string
            - "null"
        legal_name:
          type:
            - string
            - "null"
          description: Razón social (para persona jurídica).
        document_type:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
          description: Tipo del documento principal. Inferido por la longitud de `document_number`.
        document_number:
          type:
            - string
            - "null"
          description: CPF (11 dígitos) o CNPJ (14 dígitos). Puede contener máscara.
        kind:
          type:
            - string
            - "null"
          enum:
            - natural
            - juridical
          description: "`natural` para persona física; `juridical` para persona jurídica."
        birthday:
          type:
            - string
            - "null"
          description: Fecha de nacimiento (ISO 8601).
        notes:
          type:
            - string
            - "null"
        addresses:
          type:
            - array
            - "null"
          items:
            type: object
            properties:
              kind:
                type: string
                description: "Tipo de la dirección (ej.: `billing`, `shipping`)."
              street:
                type: string
              number:
                type: string
              complement:
                type:
                  - string
                  - "null"
              neighborhood:
                type: string
              city:
                type: string
              state:
                type: string
              postal_code:
                type: string
              country:
                type: string
                description: "Código ISO del país (ej.: `BR`)."
        phones:
          type:
            - array
            - "null"
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        emails:
          type:
            - array
            - "null"
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        websites:
          type:
            - array
            - "null"
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        tags:
          type:
            - array
            - "null"
          items:
            type: string
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizados definidos por la organización.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        billing_accounts:
          type: array
          items:
            $ref: "#/components/schemas/BillingAccountV1"
          description: Incluido solo cuando el cliente se carga con cuentas de cobro.
      required:
        - id
        - external_id
        - name
        - nickname
        - legal_name
        - document_type
        - document_number
        - kind
        - birthday
        - notes
        - addresses
        - phones
        - emails
        - websites
        - tags
        - custom_metadata
        - created_at
        - updated_at
    CustomerListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CustomerV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CustomerResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/CustomerV1"
      required:
        - data
    CustomerCreateResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            customer:
              $ref: "#/components/schemas/CustomerV1"
            billing_account:
              allOf:
                - $ref: "#/components/schemas/BillingAccountV1"
                - type:
                    - object
                    - "null"
          required:
            - customer
            - billing_account
      required:
        - data
    CustomerCreateRequest:
      type: object
      properties:
        name:
          type: string
          description: Nombre del cliente (obligatorio).
        external_id:
          type: string
          description: Identificador externo del cliente en el sistema de origen.
        nickname:
          type: string
        legal_name:
          type: string
          description: Razón social (para persona jurídica).
        document_number:
          type: string
          description: CPF o CNPJ. El `document_type` se infiere por la longitud (≤11 → cpf, si no cnpj).
        kind:
          type: string
          enum:
            - natural
            - juridical
        birthday:
          type: string
          description: Fecha de nacimiento (ISO 8601).
        email:
          type: string
          description: Correo principal de contacto.
        phone:
          type: string
        notes:
          type: string
        addresses:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
                description: "Tipo de la dirección (ej.: `billing`, `shipping`)."
              street:
                type: string
              number:
                type: string
              complement:
                type:
                  - string
                  - "null"
              neighborhood:
                type: string
              city:
                type: string
              state:
                type: string
              postal_code:
                type: string
              country:
                type: string
                description: "Código ISO del país (ej.: `BR`)."
        phones:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        emails:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        websites:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        tags:
          type: array
          items:
            type: string
        custom_metadata:
          type: object
          additionalProperties: {}
        create_billing_account:
          type: boolean
          description: "Cuando es `false`, no crea cuenta de cobro automáticamente. Por defecto: `true`."
        billing_email:
          type: string
          description: "Correo para envío de facturas. Por defecto: usa `email` cuando se omite."
        payment_terms_days:
          type: integer
          minimum: 0
          description: "Plazo de pago en días. Por defecto: `10`."
        auto_collection:
          type: boolean
          description: "Cobro automático en la cuenta creada. Por defecto: `true`."
      required:
        - name
    CustomerUpdateRequest:
      type: object
      properties:
        name:
          type: string
        external_id:
          type: string
        nickname:
          type: string
        legal_name:
          type: string
        document_number:
          type: string
        kind:
          type: string
          enum:
            - natural
            - juridical
        birthday:
          type: string
        email:
          type: string
        phone:
          type: string
        notes:
          type: string
        addresses:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
                description: "Tipo de la dirección (ej.: `billing`, `shipping`)."
              street:
                type: string
              number:
                type: string
              complement:
                type:
                  - string
                  - "null"
              neighborhood:
                type: string
              city:
                type: string
              state:
                type: string
              postal_code:
                type: string
              country:
                type: string
                description: "Código ISO del país (ej.: `BR`)."
        phones:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        emails:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        websites:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
              value:
                type: string
            required:
              - value
        tags:
          type: array
          items:
            type: string
        custom_metadata:
          type: object
          additionalProperties: {}
    CustomerStatementItem:
      type: object
      properties:
        id:
          type: string
          format: uuid
        billing_account_id:
          type: string
          format: uuid
        kind:
          type: string
          description: "Tipo del movimiento (ej.: `invoice`, `payment`, `credit_application`)."
        occurred_at:
          type: string
          format: date-time
        description:
          type:
            - string
            - "null"
        amount_cents:
          type: integer
          description: Importe del movimiento en centavos (÷100 → BRL). Puede ser negativo.
        parent_id:
          type:
            - string
            - "null"
        parent_type:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time
      required:
        - id
        - billing_account_id
        - kind
        - occurred_at
        - description
        - amount_cents
        - parent_id
        - parent_type
        - created_at
    CustomerStatementListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CustomerStatementItem"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CustomerStatementSyncResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            customer_id:
              type: string
              format: uuid
            billing_account_id:
              type: string
              format: uuid
            invoices_created:
              type: integer
            payments_created:
              type: integer
            credit_applications_created:
              type: integer
            total_created:
              type: integer
          required:
            - customer_id
            - billing_account_id
            - invoices_created
            - payments_created
            - credit_applications_created
            - total_created
      required:
        - data
    CustomerStatementSyncRequest:
      type: object
      properties:
        billing_account_id:
          type: string
          format: uuid
          description: Necesario cuando el cliente tiene múltiples cuentas de cobro.
    CustomerRecalculateMrrResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            customer_id:
              type: string
              format: uuid
            total_mrr_cents:
              type: integer
              description: MRR consolidado de todas las cuentas, en centavos (÷100 → BRL).
            billing_accounts:
              type: array
              items:
                type: object
                properties:
                  billing_account_id:
                    type: string
                    format: uuid
                  mrr_amount_cents:
                    type: integer
                required:
                  - billing_account_id
                  - mrr_amount_cents
          required:
            - customer_id
            - total_mrr_cents
            - billing_accounts
      required:
        - data
    CustomerPortalUserV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
        status:
          type: string
          enum:
            - active
            - pending_verification
            - pending_invitation
            - suspended
            - locked
        sso_only:
          type: boolean
        external_id:
          type:
            - string
            - "null"
        email_verified_at:
          type:
            - string
            - "null"
          format: date-time
        last_login_at:
          type:
            - string
            - "null"
          format: date-time
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        name:
          type:
            - string
            - "null"
          description: Nombre del `Person` vinculado, cuando está cargado.
      required:
        - id
        - email
        - status
        - sso_only
        - external_id
        - email_verified_at
        - last_login_at
        - custom_metadata
        - created_at
        - updated_at
    CustomerPortalUserListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CustomerPortalUserV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CustomerPortalUserResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/CustomerPortalUserV1"
      required:
        - data
    CustomerPortalUserInviteRequest:
      type: object
      properties:
        email:
          type: string
          description: Correo del invitado (obligatorio).
      required:
        - email
    CustomerPortalUserUpdateRequest:
      type: object
      properties:
        email:
          type: string
        external_id:
          type:
            - string
            - "null"
        sso_only:
          type: boolean
        status:
          type: string
          enum:
            - active
            - suspended
          description: "Transiciones permitidas: `active` ↔ `suspended`."
    CustomerPortalUserEmailPreviewResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            type:
              type: string
              enum:
                - invitation
                - verification
            from:
              type: string
            to:
              type: string
            subject:
              type: string
            message:
              type: string
          required:
            - type
            - from
            - to
            - subject
            - message
      required:
        - data
    CustomerPortalUserResendResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            resent:
              type: boolean
            type:
              type: string
              enum:
                - invitation
                - verification
          required:
            - resent
            - type
      required:
        - data
    PlanItemV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        plan_id:
          type: string
          format: uuid
        product_id:
          type: string
          format: uuid
        price_id:
          type: string
          format: uuid
        quantity:
          type: integer
          description: Cantidad del producto incluida en el plan.
          example: 1
        included:
          type: boolean
          description: Cuando es `true`, el ítem está incluido en el precio del plan. Cuando es `false`, es un add-on cobrado por separado.
        created_at:
          type: string
          format: date-time
      required:
        - id
        - plan_id
        - product_id
        - price_id
        - quantity
        - included
        - created_at
    PlanV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type:
            - string
            - "null"
          format: uuid
        company_id:
          type:
            - string
            - "null"
          format: uuid
        plan_group_id:
          type:
            - string
            - "null"
          format: uuid
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Cuando está definido, el plan es específico de una cuenta de cobro (plan custom).
        name:
          type: string
        slug:
          type: string
          description: Identificador único por organización. Solo `[a-z0-9-]`.
        description:
          type:
            - string
            - "null"
        status:
          type: string
          enum:
            - draft
            - active
            - archived
          description: "Estado del plan: `draft`, `active` o `archived`."
        plan_type:
          type: string
          enum:
            - standard
            - custom
            - promotional
          description: "`standard`: plan público estándar. `custom`: plan específico de cliente. `promotional`: plan promocional temporal."
        visibility:
          type: string
          enum:
            - public
            - private
          description: "`public`: visible en el catálogo. `private`: oculto."
        features:
          type: array
          items:
            type: string
          description: Lista de features del plan (strings libres).
        limits:
          type: object
          properties:
            users:
              type: integer
            subaccounts:
              type: integer
            operationsPerMonth:
              type: integer
            apiRequestsPerDay:
              type: integer
          description: Límites del plan (usuarios, subcuentas, operaciones, etc.).
        trial_period_days:
          type:
            - integer
            - "null"
          description: Días de trial concedidos al crear una suscripción de este plan.
        default_billing_cycle:
          type: string
          enum:
            - monthly
            - quarterly
            - semiannual
            - annual
          description: Periodicidad de cobro por defecto.
        available_billing_cycles:
          type: array
          items:
            type: string
            enum:
              - monthly
              - quarterly
              - semiannual
              - annual
          description: Periodicidades de cobro permitidas para este plan.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        items:
          type: array
          items:
            $ref: "#/components/schemas/PlanItemV1"
          description: Ítems del plan. Presente cuando se incluye vía relations.
      required:
        - id
        - organization_id
        - company_id
        - plan_group_id
        - billing_account_id
        - name
        - slug
        - description
        - status
        - plan_type
        - visibility
        - features
        - limits
        - trial_period_days
        - default_billing_cycle
        - available_billing_cycles
        - custom_metadata
        - created_at
        - updated_at
    PlanListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PlanV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    PlanResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/PlanV1"
      required:
        - data
    PlanItemListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PlanItemV1"
      required:
        - data
    PlanItemInput:
      type: object
      properties:
        product_id:
          type: string
          format: uuid
        price_id:
          type: string
          format: uuid
        quantity:
          type: integer
          minimum: 1
          default: 1
        included:
          type: boolean
          default: true
      required:
        - product_id
        - price_id
    PlanCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
        plan_group_id:
          type:
            - string
            - "null"
          format: uuid
        name:
          type: string
          minLength: 1
          maxLength: 255
        slug:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9-]+$
          description: Solo letras minúsculas, dígitos y guiones.
        description:
          type:
            - string
            - "null"
        status:
          type: string
          enum:
            - draft
            - active
            - archived
        plan_type:
          type: string
          enum:
            - standard
            - custom
            - promotional
          description: "Por defecto: `standard`."
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
        visibility:
          type: string
          enum:
            - public
            - private
          description: "Por defecto: `public`."
        features:
          type: array
          items:
            type: string
        limits:
          type: object
          properties:
            users:
              type: integer
            subaccounts:
              type: integer
            operationsPerMonth:
              type: integer
            apiRequestsPerDay:
              type: integer
        trial_period_days:
          type: integer
          minimum: 0
          maximum: 90
        default_billing_cycle:
          type: string
          enum:
            - monthly
            - quarterly
            - semiannual
            - annual
        available_billing_cycles:
          type: array
          items:
            type: string
            enum:
              - monthly
              - quarterly
              - semiannual
              - annual
        items:
          type: array
          items:
            $ref: "#/components/schemas/PlanItemInput"
        custom_metadata:
          type: object
          additionalProperties: {}
      required:
        - name
        - slug
    PlanUpdateRequest:
      type: object
      properties:
        company_id:
          type:
            - string
            - "null"
          format: uuid
        plan_group_id:
          type:
            - string
            - "null"
          format: uuid
        name:
          type: string
          minLength: 1
          maxLength: 255
        slug:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9-]+$
          description: Solo letras minúsculas, dígitos y guiones.
        description:
          type:
            - string
            - "null"
        status:
          type: string
          enum:
            - draft
            - active
            - archived
        plan_type:
          type: string
          enum:
            - standard
            - custom
            - promotional
          description: "Por defecto: `standard`."
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
        visibility:
          type: string
          enum:
            - public
            - private
          description: "Por defecto: `public`."
        features:
          type: array
          items:
            type: string
        limits:
          type: object
          properties:
            users:
              type: integer
            subaccounts:
              type: integer
            operationsPerMonth:
              type: integer
            apiRequestsPerDay:
              type: integer
        trial_period_days:
          type: integer
          minimum: 0
          maximum: 90
        default_billing_cycle:
          type: string
          enum:
            - monthly
            - quarterly
            - semiannual
            - annual
        available_billing_cycles:
          type: array
          items:
            type: string
            enum:
              - monthly
              - quarterly
              - semiannual
              - annual
        items:
          type: array
          items:
            $ref: "#/components/schemas/PlanItemInput"
        custom_metadata:
          type: object
          additionalProperties: {}
    PlanItemsReplaceRequest:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/PlanItemInput"
      required:
        - items
    PlanItemsReplaceResponse:
      type: array
      items:
        $ref: "#/components/schemas/PlanItemV1"
    PlanBulkIdsRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: Lista de UUIDs de planes. Mínimo 1.
      required:
        - ids
    PlanBulkMoveGroupRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
        plan_group_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del grupo destino. Usa `null` para quitar la agrupación.
      required:
        - ids
        - plan_group_id
    PlanBulkArchiveResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            archived_count:
              type: integer
            skipped_count:
              type: integer
            skipped_ids:
              type: array
              items:
                type: string
                format: uuid
          required:
            - archived_count
            - skipped_count
            - skipped_ids
      required:
        - data
    PlanBulkDeleteResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted_count:
              type: integer
            skipped_count:
              type: integer
            skipped_ids:
              type: array
              items:
                type: string
                format: uuid
          required:
            - deleted_count
            - skipped_count
            - skipped_ids
      required:
        - data
    PlanBulkMoveGroupResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            moved_count:
              type: integer
          required:
            - moved_count
      required:
        - data
    PlanGroupV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
        company_id:
          type:
            - string
            - "null"
          format: uuid
        name:
          type: string
          description: Nombre del grupo de planes.
        slug:
          type:
            - string
            - "null"
          description: Identificador legible usado en URLs públicas.
        display_order:
          type: integer
          description: Posición de visualización (el valor menor aparece primero).
        archived_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de archivado. `null` cuando está activo.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - company_id
        - name
        - slug
        - display_order
        - archived_at
        - created_at
        - updated_at
    PlanGroupListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PlanGroupV1"
      required:
        - data
    PlanGroupResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/PlanGroupV1"
      required:
        - data
    PlanGroupCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre del grupo de planes.
        slug:
          type: string
          minLength: 1
          maxLength: 100
          description: Identificador legible (opcional). Generado automáticamente si se omite.
      required:
        - name
    PlanGroupUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        slug:
          type: string
          minLength: 1
          maxLength: 100
    PlanGroupReorderRequest:
      type: object
      properties:
        group_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: Lista de UUIDs de los grupos en el nuevo orden de visualización. Todos deben pertenecer a la organización actual.
      required:
        - group_ids
    PlanGroupReorderResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            success:
              type: boolean
              example: true
          required:
            - success
      required:
        - data
    ProductV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type:
            - string
            - "null"
          format: uuid
        company_id:
          type:
            - string
            - "null"
          format: uuid
        parent_product_id:
          type:
            - string
            - "null"
          format: uuid
        product_group_id:
          type:
            - string
            - "null"
          format: uuid
        service_item_id:
          type:
            - string
            - "null"
          format: uuid
        name:
          type: string
          description: Nombre de visualización del producto.
        slug:
          type: string
          description: Identificador legible único dentro del grupo de productos. Acepta solo `[a-z0-9_-]+`.
        description:
          type:
            - string
            - "null"
        status:
          type: string
          enum:
            - draft
            - active
            - archived
          description: "Estado del producto: `draft`, `active` o `archived`."
        visibility:
          type: string
          enum:
            - public
            - private
          description: "Visibilidad en el portal del cliente. `public`: visible para clientes; `private`: solo operadores."
        product_type:
          type: string
          enum:
            - base
            - addon_quantity
            - addon_fixed
            - metered
            - one_time_fixed
            - one_time_quantity
          description: "Tipo del producto. `base`: plan principal; `addon_quantity`/`addon_fixed`: complementos; `metered`: cobro por uso; `one_time_fixed`/`one_time_quantity`: cobro único."
        unit_label:
          type:
            - string
            - "null"
          description: 'Etiqueta de la unidad (ej.: "usuario", "GB"). Usada en facturas y portal.'
        increment:
          type: integer
          description: Incremento mínimo de cantidad al cambiar el ítem.
        website_url:
          type:
            - string
            - "null"
        display_order:
          type: integer
          description: Orden de visualización (menor primero).
        is_required:
          type: boolean
          description: Cuando es `true`, el producto es obligatorio en el plan en el que se incluye.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - company_id
        - parent_product_id
        - product_group_id
        - service_item_id
        - name
        - slug
        - description
        - status
        - visibility
        - product_type
        - unit_label
        - increment
        - website_url
        - display_order
        - is_required
        - custom_metadata
        - created_at
        - updated_at
    ProductListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProductV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ProductResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/ProductV1"
      required:
        - data
    ProductCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre del producto.
        slug:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9_-]+$
          description: Slug único dentro del grupo. Acepta solo `[a-z0-9_-]+`.
        description:
          type: string
        productType:
          type: string
          enum:
            - base
            - addon_quantity
            - addon_fixed
            - metered
            - one_time_fixed
            - one_time_quantity
          description: "Tipo del producto. Por defecto: `base`."
        visibility:
          type: string
          enum:
            - public
            - private
          description: "Visibilidad en el portal. Por defecto: `public`."
        unitLabel:
          type: string
        increment:
          type: integer
          exclusiveMinimum: 0
          description: "Incremento mínimo. Por defecto: `1`."
        websiteUrl:
          type: string
          format: uri
        isRequired:
          type: boolean
        parentProductId:
          type: string
          format: uuid
        productGroupId:
          type: string
          format: uuid
        companyId:
          type: string
          format: uuid
        customMetadata:
          type: object
          additionalProperties: {}
      required:
        - name
        - slug
    ProductUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        slug:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9_-]+$
        description:
          type: string
        status:
          type: string
          enum:
            - draft
            - active
            - archived
        productType:
          type: string
          enum:
            - base
            - addon_quantity
            - addon_fixed
            - metered
            - one_time_fixed
            - one_time_quantity
        visibility:
          type: string
          enum:
            - public
            - private
        unitLabel:
          type: string
        increment:
          type: integer
          exclusiveMinimum: 0
        websiteUrl:
          type: string
          format: uri
        isRequired:
          type: boolean
        parentProductId:
          type:
            - string
            - "null"
          format: uuid
        productGroupId:
          type:
            - string
            - "null"
          format: uuid
        companyId:
          type:
            - string
            - "null"
          format: uuid
        customMetadata:
          type: object
          additionalProperties: {}
    ProductReorderRequest:
      type: object
      properties:
        product_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: IDs de productos en el nuevo orden deseado. El índice 0 pasa a ser el primero de la lista.
      required:
        - product_ids
    ProductBulkArchiveRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: IDs de productos a archivar.
      required:
        - ids
    ProductBulkDeleteRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: IDs de productos a eliminar permanentemente.
      required:
        - ids
    ProductBulkVisibilityRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
        visibility:
          type: string
          enum:
            - public
            - private
          description: Nueva visibilidad a aplicar en todos los productos.
      required:
        - ids
        - visibility
    ProductBulkMoveGroupRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
        product_group_id:
          type:
            - string
            - "null"
          format: uuid
          description: Grupo de destino. Usa `null` para quitar del grupo actual.
      required:
        - ids
        - product_group_id
    ProductBulkChangeCompanyRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: Empresa de destino. Usa `null` para limpiar el vínculo.
      required:
        - ids
        - company_id
    ProductBulkResultResponse:
      type: object
      properties:
        data: {}
    ProductGroupV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora asociada al grupo (opcional).
        proposal_template_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la plantilla de propuesta por defecto del grupo (opcional).
        service_item_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del ítem de servicio fiscal por defecto del grupo (opcional).
        name:
          type: string
          description: Nombre del grupo de productos.
        slug:
          type: string
          description: Identificador legible usado en URLs públicas. Único por organización.
        display_order:
          type: integer
          description: Orden de visualización del grupo (menor primero).
        archived_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de archivado. `null` cuando está activo.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - company_id
        - proposal_template_id
        - service_item_id
        - name
        - slug
        - display_order
        - archived_at
        - created_at
        - updated_at
    ProductGroupListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProductGroupV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ProductGroupResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/ProductGroupV1"
      required:
        - data
    ProductGroupCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre del grupo.
        slug:
          type: string
          description: Slug del grupo. Generado a partir del nombre cuando se omite.
        company_id:
          type:
            - string
            - "null"
          format: uuid
        proposal_template_id:
          type:
            - string
            - "null"
          format: uuid
        service_item_id:
          type:
            - string
            - "null"
          format: uuid
      required:
        - name
    ProductGroupUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        slug:
          type: string
        company_id:
          type:
            - string
            - "null"
          format: uuid
        proposal_template_id:
          type:
            - string
            - "null"
          format: uuid
        service_item_id:
          type:
            - string
            - "null"
          format: uuid
    ProductGroupReorderRequest:
      type: object
      properties:
        group_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: Lista de UUIDs en el nuevo orden deseado. Todos los IDs deben pertenecer a la organización.
      required:
        - group_ids
    ProductGroupReorderResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            success:
              type: boolean
              example: true
          required:
            - success
      required:
        - data
    ProductGroupDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    OrganizationPersonV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        external_code:
          type:
            - string
            - "null"
        name:
          type: string
          description: Razón social o nombre de visualización.
        nickname:
          type:
            - string
            - "null"
        legal_name:
          type:
            - string
            - "null"
        document_type:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
          description: "Tipo de documento: `cpf` o `cnpj`."
        document_number:
          type:
            - string
            - "null"
          description: Número del documento (solo dígitos).
        kind:
          type:
            - string
            - "null"
          enum:
            - individual
            - company
        birthday:
          type:
            - string
            - "null"
        notes:
          type:
            - string
            - "null"
        addresses:
          description: Lista de direcciones.
        phones:
          description: Lista de teléfonos de contacto.
        emails:
          description: Lista de correos de contacto.
        websites: {}
        tags:
          type:
            - array
            - "null"
          items:
            type: string
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        type:
          type: string
        parent_id:
          type:
            - string
            - "null"
          format: uuid
        external_id:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - name
    OrganizationV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        external_id:
          type:
            - string
            - "null"
          description: Identificador externo de la organización en el sistema de Kobana.
        status:
          type: string
          description: "Estado de la organización (ej.: `active`)."
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos arbitrarios. Las claves internas (`apiKeys`, `encryptionKey`, `secrets`) se eliminan antes de la respuesta.
        person:
          allOf:
            - $ref: "#/components/schemas/OrganizationPersonV1"
            - description: Datos registrales de la organización (nombre, documento, contactos, direcciones). Siempre incluido.
      required:
        - id
        - external_id
        - status
        - created_at
        - updated_at
        - person
    OrganizationResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/OrganizationV1"
      required:
        - data
    OrganizationUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de visualización de la organización.
        email:
          type: string
          format: email
          description: Correo de contacto principal de la organización.
        phone:
          type: string
          description: Teléfono de contacto principal de la organización.
        addresses:
          type: array
          items: {}
          description: Lista de direcciones de la organización.
    CompanyPersonV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        external_code:
          type:
            - string
            - "null"
          description: Código externo del registro `Person` en el sistema del cliente.
        name:
          type: string
        nickname:
          type:
            - string
            - "null"
        legal_name:
          type:
            - string
            - "null"
          description: Razón social.
        document_type:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
          description: "Tipo de documento: `cpf` o `cnpj`."
        document_number:
          type:
            - string
            - "null"
          description: Número del documento (CPF o CNPJ), almacenado solo con dígitos.
        kind:
          type:
            - string
            - "null"
        birthday:
          type:
            - string
            - "null"
        notes:
          type:
            - string
            - "null"
        addresses: {}
        phones: {}
        emails: {}
        websites: {}
        tags:
          type:
            - array
            - "null"
          items:
            type: string
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        type:
          type: string
        parent_id:
          type:
            - string
            - "null"
        external_id:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - name
    CompanyV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        public_id:
          type:
            - string
            - "null"
          description: Identificador público corto de la empresa (estable y seguro para mostrar).
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización (tenant) propietaria de la empresa.
        person_id:
          type: string
          format: uuid
          description: UUID del registro `Person` (identidad jurídica) asociado a la empresa.
        parent_company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la matriz cuando la empresa es una sucursal; `null` para matrices.
        is_default:
          type: boolean
          description: Indica si es la empresa predeterminada de la organización. Solo las matrices pueden ser predeterminadas.
        status:
          type: string
          enum:
            - active
            - inactive
          description: "Estado de la empresa: `active` o `inactive`."
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos internos de la empresa (clave/valor libre).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizados definidos por el cliente (clave/valor libre).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        person:
          allOf:
            - $ref: "#/components/schemas/CompanyPersonV1"
            - description: Identidad jurídica de la empresa (nombre, razón social, documento, contactos).
      required:
        - id
        - public_id
        - organization_id
        - person_id
        - parent_company_id
        - is_default
        - status
        - metadata
        - custom_metadata
        - created_at
        - updated_at
    CompanyListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CompanyV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CompanyResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/CompanyV1"
      required:
        - data
    CompanyMatrizListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CompanyV1"
      required:
        - data
    CompanyDeleteResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    CompanyCreateRequest:
      type: object
      properties:
        parent_company_id:
          type: string
          format: uuid
          description: UUID de la matriz al crear una sucursal. Omite para crear una matriz.
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre comercial de la empresa.
        legal_name:
          type: string
          maxLength: 255
          description: Razón social.
        document_type:
          type: string
          enum:
            - cpf
            - cnpj
          description: "Tipo de documento: `cpf` o `cnpj`. Solo `cnpj` puede tener sucursales."
        document_number:
          type: string
          minLength: 11
          maxLength: 18
          description: CPF o CNPJ (con o sin máscara — se normalizará a solo dígitos).
        email:
          type: string
          format: email
          description: Correo principal de la empresa.
        phone:
          type: string
          description: Teléfono principal de la empresa. Cuando se informa, se añade al array `phones`.
        addresses:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Lista de direcciones de la empresa.
        emails:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Lista de correos adicionales. Si se omite, se usa el `email` principal.
        phones:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Lista de teléfonos de la empresa.
        websites:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Lista de sitios web de la empresa.
        is_default:
          type: boolean
          description: Cuando es `true`, marca esta empresa como predeterminada de la organización. Solo las matrices pueden ser predeterminadas.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Metadatos personalizados (clave/valor libre).
      required:
        - name
        - document_type
        - document_number
        - email
    CompanyUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nuevo nombre comercial.
        legal_name:
          type: string
          maxLength: 255
          description: Nueva razón social.
        email:
          type: string
          format: email
          description: Nuevo correo principal. Actualiza la primera entrada del array `emails`.
        phone:
          type: string
          description: Nuevo teléfono principal. Actualiza la primera entrada del array `phones`.
        addresses:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Sustituye la lista de direcciones.
        emails:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Sustituye la lista de correos.
        phones:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Sustituye la lista de teléfonos.
        websites:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Sustituye la lista de sitios web.
        is_default:
          type: boolean
          description: Cuando es `true`, marca esta empresa como predeterminada. Usa el endpoint `set-default` para la operación dedicada.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Sustituye los metadatos personalizados.
    CompanyFiscalProfileUpsertRequest:
      type: object
      properties:
        taxation_regime:
          type: string
          enum:
            - simple_national
            - presumed_profit
            - real_profit
            - mei
          description: "Régimen tributario: `simple_national`, `presumed_profit`, `real_profit` o `mei`."
        simples_annex:
          type:
            - string
            - "null"
          enum:
            - annex_iii
            - annex_iv
            - annex_v
          description: "Anexo del Simples Nacional: `annex_iii`, `annex_iv` o `annex_v`."
        rbt12_cents:
          type:
            - integer
            - "null"
          minimum: 0
          description: Ingreso bruto acumulado en los últimos 12 meses, en centavos (÷100 → BRL).
        factor_r:
          type:
            - number
            - "null"
          description: Factor R (nómina ÷ ingreso), usado en la clasificación del Simples Nacional.
        cpp_inside_das:
          type: boolean
          description: Indica si la CPP (contribución previsional patronal) está incluida en el DAS.
        presumption_rate:
          type:
            - number
            - "null"
          description: "Alícuota de presunción (lucro presunto), en decimal (ej.: 0.32)."
        iss_municipality:
          type:
            - string
            - "null"
          description: Municipio donde se aplica el ISS.
        iss_rate:
          type:
            - number
            - "null"
          description: Alícuota del ISS, en decimal.
        pis_rate:
          type:
            - number
            - "null"
          description: Alícuota del PIS, en decimal.
        cofins_rate:
          type:
            - number
            - "null"
          description: Alícuota de COFINS, en decimal.
        csll_rate:
          type:
            - number
            - "null"
          description: Alícuota de CSLL, en decimal.
        irpj_rate:
          type:
            - number
            - "null"
          description: Alícuota de IRPJ, en decimal.
        cbs_ibs_enabled:
          type: boolean
          description: Habilita los campos CBS/IBS (Reforma Tributaria brasileña).
        cbs_rate:
          type:
            - number
            - "null"
          description: Alícuota de la CBS, en decimal.
        ibs_rate:
          type:
            - number
            - "null"
          description: Alícuota del IBS, en decimal.
        cbs_ibs_mode:
          type: string
          enum:
            - informative
            - compositive
          description: "Modo de cálculo CBS/IBS: `informative` (solo demostrativo) o `compositive` (compone el precio)."
      required:
        - taxation_regime
    CompanyFiscalProfileResponse:
      type: object
      properties:
        data: {}
    CertificatePersonV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: UUID del person.
        name:
          type: string
          description: Nombre del person.
        document_number:
          type:
            - string
            - "null"
          description: CPF o CNPJ del person.
      required:
        - id
        - name
        - document_number
    CertificateV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        person_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del `person` asociado (cuando el documento coincide con un registro existente).
        name:
          type: string
          description: Nombre amigable del certificado.
        status:
          type: string
          enum:
            - active
            - expired
            - revoked
          description: "Estado del certificado: `active`, `expired` o `revoked`."
        format:
          type: string
          enum:
            - pfx
            - pem
          description: "Formato del certificado almacenado: `pfx` o `pem`."
        document:
          type:
            - string
            - "null"
          description: CPF o CNPJ extraído del certificado (solo dígitos).
        common_name:
          type:
            - string
            - "null"
          description: Nombre común (`CN`) del subject del certificado.
        organization_name:
          type:
            - string
            - "null"
          description: Nombre de la organización (`O`) del subject.
        organizational_unit:
          type:
            - string
            - "null"
          description: Unidad organizativa (`OU`) del subject.
        issuer_common_name:
          type:
            - string
            - "null"
          description: Nombre común (`CN`) del emisor (AC).
        issuer_organization:
          type:
            - string
            - "null"
          description: Organización (`O`) del emisor (AC).
        serial_number:
          type:
            - string
            - "null"
          description: Número de serie del certificado.
        thumbprint:
          type:
            - string
            - "null"
          description: Thumbprint SHA-1 del certificado (hex en mayúsculas). Único por certificado.
        not_before:
          type:
            - string
            - "null"
          format: date-time
          description: Inicio de la validez del certificado (UTC).
        not_after:
          type:
            - string
            - "null"
          format: date-time
          description: Fin de la validez del certificado (UTC).
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos internos del certificado.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos arbitrarios definidos por el cliente.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        person:
          allOf:
            - $ref: "#/components/schemas/CertificatePersonV1"
            - type:
                - object
                - "null"
              description: Resumen del `person` asociado (id, name, document_number), cuando se carga.
      required:
        - id
        - person_id
        - name
        - status
        - format
        - document
        - common_name
        - organization_name
        - organizational_unit
        - issuer_common_name
        - issuer_organization
        - serial_number
        - thumbprint
        - not_before
        - not_after
        - created_at
        - updated_at
    CertificateListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CertificateV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CertificateResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/CertificateV1"
      required:
        - data
    CertificateUploadRequest:
      type: object
      properties:
        type:
          type: string
          enum:
            - pfx
            - cert_key
            - combined_pem
            - cert_key_password
          description: "Formato del envío: `pfx`, `cert_key`, `combined_pem` o `cert_key_password`."
        name:
          type: string
          description: Nombre amigable del certificado. Cuando se omite, se deriva de `common_name` o del documento.
        file:
          type: string
          description: Archivo binario (PFX/P12) cuando `type=pfx`, o PEM combinado cuando `type=combined_pem`.
          format: binary
        cert_file:
          type: string
          description: Archivo PEM del certificado (solo para `type=cert_key` o `type=cert_key_password`).
          format: binary
        key_file:
          type: string
          description: Archivo PEM de la clave privada (solo para `type=cert_key` o `type=cert_key_password`).
          format: binary
        password:
          type: string
          description: Contraseña del PFX (`type=pfx`) o de la clave privada (`type=cert_key_password`). Se almacena cifrada y nunca se devuelve.
      required:
        - type
    CouponTargetV1:
      type: object
      properties:
        planIds:
          type: array
          items:
            type: string
            format: uuid
          description: Lista de UUIDs de planes.
        productIds:
          type: array
          items:
            type: string
            format: uuid
          description: Lista de UUIDs de productos.
    CouponV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        code:
          type: string
          description: Código público del cupón — usado por los clientes al aplicar el descuento. Único por organización.
        name:
          type: string
          description: Nombre interno del cupón (mostrado en el panel).
        description:
          type:
            - string
            - "null"
          description: Descripción opcional del cupón.
        discount_type:
          type: string
          enum:
            - percentage
            - fixed_amount
          description: "Tipo de descuento: `percentage` (en puntos porcentuales) o `fixed_amount` (en centavos, ÷100 → BRL)."
        discount_value:
          type: integer
          description: Valor del descuento. Cuando `discount_type=percentage`, un entero entre `0` y `100`. Cuando `discount_type=fixed_amount`, el importe en centavos (÷100 → BRL).
          example: 10
        currency:
          type: string
          description: "Moneda ISO 4217. Por defecto: `BRL`."
          example: BRL
        min_purchase_cents:
          type:
            - integer
            - "null"
          description: Importe mínimo de la factura (en centavos, ÷100 → BRL) para que el cupón pueda aplicarse.
        max_discount_cents:
          type:
            - integer
            - "null"
          description: Tope absoluto del descuento (en centavos, ÷100 → BRL). Se aplica principalmente a cupones porcentuales.
        usage_limit:
          type:
            - integer
            - "null"
          description: Número total de veces que el cupón puede canjearse en toda la organización. `null` = ilimitado.
        usage_limit_per_user:
          type:
            - integer
            - "null"
          description: Número máximo de canjes por cuenta de cobro. `null` = ilimitado.
        usage_count:
          type: integer
          description: Cantidad de canjes ya registrados para el cupón.
          example: 0
        valid_from:
          type:
            - string
            - "null"
          format: date-time
          description: Inicio del periodo de validez (ISO 8601). `null` = sin inicio definido.
        valid_until:
          type:
            - string
            - "null"
          format: date-time
          description: Fin del periodo de validez (ISO 8601). `null` = sin expiración.
        applies_to:
          allOf:
            - $ref: "#/components/schemas/CouponTargetV1"
            - type:
                - object
                - "null"
              description: Restringe el cupón a planes y/o productos específicos. Cuando es `null`, se aplica a cualquier ítem.
        excludes:
          allOf:
            - $ref: "#/components/schemas/CouponTargetV1"
            - type:
                - object
                - "null"
              description: Lista de planes y/o productos donde el cupón **no** puede aplicarse.
        is_active:
          type: boolean
          description: Cuando es `false`, el cupón ya no acepta nuevos canjes.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos arbitrarios definidos por el cliente (clave/valor).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - code
        - name
        - description
        - discount_type
        - discount_value
        - currency
        - min_purchase_cents
        - max_discount_cents
        - usage_limit
        - usage_limit_per_user
        - usage_count
        - valid_from
        - valid_until
        - applies_to
        - excludes
        - is_active
        - custom_metadata
        - created_at
        - updated_at
    CouponListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CouponV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CouponResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/CouponV1"
      required:
        - data
    CouponCreateRequest:
      type: object
      properties:
        code:
          type: string
          minLength: 1
          description: Código público del cupón — único por organización.
        name:
          type: string
          minLength: 1
          description: Nombre interno del cupón.
        description:
          type: string
          description: Descripción opcional.
        discount_type:
          type: string
          enum:
            - percentage
            - fixed_amount
          description: "Tipo de descuento: `percentage` o `fixed_amount`."
        discount_value:
          type: integer
          minimum: 0
          description: Valor del descuento. Para `percentage`, entero entre `0` y `100`. Para `fixed_amount`, importe en centavos (÷100 → BRL).
        currency:
          type: string
          description: "Moneda ISO 4217. Por defecto: `BRL`."
        min_purchase_cents:
          type: integer
          minimum: 0
          description: Importe mínimo de la factura, en centavos (÷100 → BRL), para que el cupón pueda aplicarse.
        max_discount_cents:
          type: integer
          minimum: 0
          description: Tope absoluto del descuento, en centavos (÷100 → BRL).
        usage_limit:
          type: integer
          exclusiveMinimum: 0
          description: Máximo total de canjes en la organización. Omite para ilimitado.
        usage_limit_per_user:
          type: integer
          exclusiveMinimum: 0
          description: Máximo de canjes por cuenta de cobro. Omite para ilimitado.
        valid_from:
          type: string
          description: Inicio del periodo de validez (ISO 8601 — fecha o fecha/hora).
        valid_until:
          type: string
          description: Fin del periodo de validez (ISO 8601 — fecha o fecha/hora).
        applies_to:
          allOf:
            - $ref: "#/components/schemas/CouponTargetV1"
            - description: Restringe el cupón a planes y/o productos.
        excludes:
          allOf:
            - $ref: "#/components/schemas/CouponTargetV1"
            - description: Lista de planes y/o productos donde el cupón no puede aplicarse.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Metadatos arbitrarios definidos por el cliente (clave/valor).
      required:
        - code
        - name
        - discount_type
        - discount_value
    CouponUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          description: Nuevo nombre interno del cupón.
        description:
          type:
            - string
            - "null"
          description: Nueva descripción. Envía `null` para limpiar.
        usage_limit:
          type:
            - integer
            - "null"
          exclusiveMinimum: 0
          description: Nuevo límite total de canjes. `null` = ilimitado.
        usage_limit_per_user:
          type:
            - integer
            - "null"
          exclusiveMinimum: 0
          description: Nuevo límite por cuenta de cobro. `null` = ilimitado.
        valid_from:
          type:
            - string
            - "null"
          description: Nuevo inicio de validez (ISO 8601). `null` = quita la restricción.
        valid_until:
          type:
            - string
            - "null"
          description: Nuevo fin de validez (ISO 8601). `null` = quita la expiración.
        applies_to:
          allOf:
            - $ref: "#/components/schemas/CouponTargetV1"
            - type:
                - object
                - "null"
              description: Nueva restricción de planes/productos. `null` = quita la restricción.
        excludes:
          allOf:
            - $ref: "#/components/schemas/CouponTargetV1"
            - type:
                - object
                - "null"
              description: Nueva lista de exclusiones. `null` = quita las exclusiones.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Metadatos arbitrarios definidos por el cliente (clave/valor).
    CouponRedemptionV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        coupon_id:
          type: string
          format: uuid
          description: UUID del cupón canjeado.
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro que aplicó el cupón.
        invoice_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la factura donde se aplicó el descuento, cuando corresponde.
        discount_cents:
          type: integer
          description: Descuento efectivamente aplicado en este canje, en centavos (÷100 → BRL).
        created_at:
          type: string
          format: date-time
      required:
        - id
        - coupon_id
        - billing_account_id
        - invoice_id
        - discount_cents
        - created_at
    CouponRedemptionListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CouponRedemptionV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CreditV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro propietaria del crédito.
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización (tenant).
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID opcional de la `company` vinculada. Cuando está presente, el crédito solo puede aplicarse a facturas de la misma `company`.
        type:
          type: string
          enum:
            - promotional
            - adjustment
            - refund
            - manual
          description: "Tipo del crédito: `promotional`, `adjustment`, `refund` o `manual`."
        amount_cents:
          type: integer
          description: Monto original del crédito en centavos (÷100 → BRL).
          example: 10000
        amount_remaining_cents:
          type: integer
          description: Saldo restante del crédito en centavos (÷100 → BRL).
          example: 10000
        currency:
          type: string
          description: "Moneda ISO 4217 (ej.: `BRL`)."
          example: BRL
        description:
          type:
            - string
            - "null"
          description: Descripción libre — se muestra en el extracto y en el audit log.
        expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora de expiración (ISO8601). `null` cuando el crédito no expira.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos internos del crédito.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizados validados contra el esquema de la organización.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        applications:
          type: array
          items: {}
          description: Aplicaciones de este crédito en facturas, cuando la relación está cargada.
      required:
        - id
        - billing_account_id
        - organization_id
        - company_id
        - type
        - amount_cents
        - amount_remaining_cents
        - currency
        - description
        - expires_at
        - metadata
        - custom_metadata
        - created_at
        - updated_at
    CreditListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CreditV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    CreditResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/CreditV1"
      required:
        - data
    CreditCreateRequest:
      type: object
      properties:
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro que recibirá el crédito.
        company_id:
          type: string
          format: uuid
          description: UUID opcional de la `company` a la que está vinculado el crédito. Cuando se informa, restringe la aplicación del crédito a facturas de la misma `company`.
        type:
          type: string
          enum:
            - promotional
            - adjustment
            - refund
            - manual
          description: "Tipo de crédito: `promotional`, `adjustment`, `refund` o `manual`."
        amount_cents:
          type: integer
          exclusiveMinimum: 0
          description: Monto del crédito en centavos (÷100 → BRL). Debe ser un entero positivo.
          example: 10000
        description:
          type: string
          maxLength: 500
          description: Descripción libre (hasta 500 caracteres) — se muestra en el extracto y en el audit log.
        expires_at:
          type: string
          format: date-time
          description: Fecha y hora de expiración (ISO8601). Después de esa fecha el saldo restante se descarta.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Metadatos personalizados validados contra el esquema de la organización.
      required:
        - billing_account_id
        - type
        - amount_cents
    CreditUpdateRequest:
      type: object
      properties:
        type:
          type: string
          enum:
            - promotional
            - adjustment
            - refund
            - manual
          description: "Nuevo tipo del crédito: `promotional`, `adjustment`, `refund` o `manual`."
        amount_cents:
          type: integer
          exclusiveMinimum: 0
          description: Nuevo monto en centavos (÷100 → BRL). Entero positivo. Solo puede cambiar si el crédito aún no se ha usado.
        description:
          type:
            - string
            - "null"
          maxLength: 500
          description: Nueva descripción. Envía `null` para limpiar.
        expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: Nueva fecha de expiración (ISO8601). Envía `null` para eliminar la expiración.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Metadatos personalizados validados contra el esquema de la organización.
    CreditDeleteResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    InvoiceLineItemV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        invoice_id:
          type: string
          format: uuid
        subscription_item_id:
          type:
            - string
            - "null"
          format: uuid
        product_id:
          type:
            - string
            - "null"
          format: uuid
        price_id:
          type:
            - string
            - "null"
          format: uuid
        service_item_id:
          type:
            - string
            - "null"
          format: uuid
        type:
          type:
            - string
            - "null"
        description:
          type:
            - string
            - "null"
          description: Descripción del ítem.
        quantity:
          type: number
          description: Cantidad.
        unit_amount_subcents:
          type: integer
          description: Valor unitario en subcentavos (÷10000 → BRL).
        amount_cents:
          type: integer
          description: Subtotal del ítem en centavos (÷100 → BRL).
        discount_cents:
          type:
            - integer
            - "null"
        tax_cents:
          type:
            - integer
            - "null"
        period_start:
          type:
            - string
            - "null"
          format: date-time
        period_end:
          type:
            - string
            - "null"
          format: date-time
        proration:
          type:
            - boolean
            - "null"
        proration_details: {}
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
      required:
        - id
        - invoice_id
        - quantity
        - unit_amount_subcents
        - amount_cents
        - created_at
    InvoiceInstallmentV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        public_id:
          type:
            - string
            - "null"
        invoice_id:
          type: string
          format: uuid
        number:
          type: integer
          description: Número secuencial de la parcela (comienza en 1).
        amount_cents:
          type: integer
          description: Valor de la parcela en centavos (÷100 → BRL).
        due_date:
          type: string
          format: date-time
          description: Fecha de vencimiento de la parcela.
        status:
          type: string
          enum:
            - pending
            - paid
            - canceled
          description: "Estado de la parcela: `pending`, `paid` o `canceled`."
        payment_id:
          type:
            - string
            - "null"
          format: uuid
        paid_at:
          type:
            - string
            - "null"
          format: date-time
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - invoice_id
        - number
        - amount_cents
        - due_date
        - status
        - created_at
        - updated_at
    InvoiceV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        number:
          type:
            - string
            - "null"
          description: Número humano-legible de la factura (generado al finalizar).
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro destino.
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora (opcional — por defecto la `default_company` de la organización).
        subscription_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la suscripción de origen, cuando la factura proviene de una suscripción.
        product_group_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del grupo de productos, cuando la factura está restringida a un grupo.
        status:
          type: string
          enum:
            - draft
            - open
            - paid
            - void
            - uncollectible
          description: "Estado: `draft`, `open`, `paid`, `void` o `uncollectible`."
        collection_method:
          type: string
          enum:
            - charge_automatically
            - manual_charge
            - manual_invoice
          description: "Método de cobro: `charge_automatically`, `manual_charge` o `manual_invoice`."
        currency:
          type: string
          description: "Moneda ISO 4217 (ej.: `BRL`)."
          example: BRL
        subtotal_cents:
          type: integer
          description: Suma de los ítems de línea en centavos (÷100 → BRL).
        discount_cents:
          type: integer
          description: Descuento aplicado en centavos (÷100 → BRL).
        tax_cents:
          type: integer
          description: Impuestos calculados en centavos (÷100 → BRL).
        total_withheld_cents:
          type: integer
          description: Total de retenciones tributarias (IRRF/ISS/INSS/CSRF) en centavos.
        net_value_cents:
          type:
            - integer
            - "null"
          description: Valor neto tras retenciones, en centavos.
        total_cents:
          type: integer
          description: Total de la factura en centavos (÷100 → BRL).
        amount_due_cents:
          type: integer
          description: Valor debido (cobrado) en centavos.
        amount_paid_cents:
          type: integer
          description: Valor ya pagado en centavos.
        amount_remaining_cents:
          type: integer
          description: Valor restante a pagar en centavos.
        period_start:
          type:
            - string
            - "null"
          format: date-time
          description: Inicio del período de competencia (facturas de suscripción).
        period_end:
          type:
            - string
            - "null"
          format: date-time
          description: Fin del período de competencia (facturas de suscripción).
        due_date:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de vencimiento.
        paid_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora en que la factura fue saldada.
        voided_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora en que la factura fue cancelada (`void`).
        finalized_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora en que la factura salió del borrador (`draft` → `open`).
        billing_reason:
          type:
            - string
            - "null"
          description: "Motivo de generación: `subscription_cycle`, `subscription_create`, `manual`, etc."
        description:
          type:
            - string
            - "null"
          description: Descripción libre mostrada al inicio de la factura.
        footer:
          type:
            - string
            - "null"
          description: Pie libre mostrado al final de la factura.
        hosted_invoice_url:
          type:
            - string
            - "null"
          description: URL pública alojada de la factura.
        invoice_pdf_url:
          type:
            - string
            - "null"
          description: URL directa al PDF de la factura.
        next_payment_attempt:
          type:
            - string
            - "null"
          format: date-time
          description: Próximo intento de cobro automático (dunning).
        payment_attempts:
          type: integer
          description: Cantidad de intentos de cobro realizados.
        installment_count:
          type:
            - integer
            - "null"
          description: Cantidad de parcelas (cuando la factura está dividida).
        nfe_issuance_policy:
          type:
            - string
            - "null"
          enum:
            - disabled
            - on_finalization
            - on_full_payment
            - per_installment
          description: "Política de emisión de NF-e: `disabled`, `on_finalization`, `on_full_payment` o `per_installment`."
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Campos personalizados definidos por la organización.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        line_items:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceLineItemV1"
          description: Ítems de línea — presente cuando se incluye vía `include=line_items`.
        installments:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceInstallmentV1"
          description: Parcelas — presente cuando se incluye vía `include=installments`.
      required:
        - id
        - number
        - billing_account_id
        - company_id
        - subscription_id
        - product_group_id
        - status
        - collection_method
        - currency
        - subtotal_cents
        - discount_cents
        - tax_cents
        - total_withheld_cents
        - net_value_cents
        - total_cents
        - amount_due_cents
        - amount_paid_cents
        - amount_remaining_cents
        - period_start
        - period_end
        - due_date
        - paid_at
        - voided_at
        - finalized_at
        - billing_reason
        - description
        - footer
        - hosted_invoice_url
        - invoice_pdf_url
        - next_payment_attempt
        - payment_attempts
        - installment_count
        - nfe_issuance_policy
        - created_at
        - updated_at
    InvoiceListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    InvoiceResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/InvoiceV1"
      required:
        - data
    InvoiceLineItemCreateInput:
      type: object
      properties:
        description:
          type: string
          minLength: 1
          description: Descripción del ítem. **Obligatorio**.
        quantity:
          type: number
          minimum: 1
          description: "Cantidad. Por defecto: 1."
        unit_amount_cents:
          type: number
          minimum: 0
          description: Valor unitario en centavos.
        product_id:
          type: string
          format: uuid
          description: UUID del producto (opcional).
        price_id:
          type: string
          format: uuid
          description: UUID del precio (opcional).
        service_item_id:
          type: string
          format: uuid
          description: UUID del ítem de servicio (opcional).
      required:
        - description
        - unit_amount_cents
    InvoiceInstallmentCreateInput:
      type: object
      properties:
        amount_cents:
          type: integer
          minimum: 1
          description: Valor de la parcela en centavos.
        due_date:
          type: string
          description: Fecha de vencimiento de la parcela.
      required:
        - amount_cents
        - due_date
    InvoiceCreateRequest:
      type: object
      properties:
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro destino. **Obligatorio**.
        company_id:
          type: string
          format: uuid
          description: "UUID de la empresa emisora. Por defecto: empresa por defecto de la organización."
        subscription_id:
          type: string
          format: uuid
          description: UUID de la suscripción de origen.
        product_group_id:
          type: string
          format: uuid
          description: UUID del grupo de productos.
        collection_method:
          type: string
          enum:
            - charge_automatically
            - manual_charge
            - manual_invoice
          description: "Método de cobro. Por defecto: `manual_invoice`."
        due_date:
          type: string
          description: "Fecha de vencimiento. Por defecto: hoy + `payment_terms_days` de la cuenta."
        description:
          type: string
          description: Descripción libre mostrada al inicio de la factura.
        footer:
          type: string
          description: Pie libre mostrado al final de la factura.
        line_items:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceLineItemCreateInput"
          minItems: 1
          description: Ítems de línea. Mínimo 1.
        discount_cents:
          type: integer
          minimum: 0
          description: Descuento fijo en centavos.
        discount_description:
          type: string
          description: 'Descripción del descuento (ej.: "Descuento por pago anual (20%)").'
        installments:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceInstallmentCreateInput"
          minItems: 2
          description: Cronograma de parcelas (mínimo 2). Cada ítem tiene `amount_cents` y `due_date`.
        nfe_issuance_policy:
          type: string
          enum:
            - disabled
            - on_finalization
            - on_full_payment
            - per_installment
          description: Política de emisión de NF-e para facturas en cuotas.
        period_start:
          type: string
          description: Inicio del período de competencia (autocompletado en facturas de suscripción).
        period_end:
          type: string
          description: Fin del período de competencia (autocompletado en facturas de suscripción).
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Campos personalizados (validados contra el schema de la organización).
      required:
        - billing_account_id
        - line_items
    InvoiceLineItemUpdateInput:
      type: object
      properties:
        id:
          type: string
          format: uuid
        description:
          type: string
          minLength: 1
        quantity:
          type: number
          minimum: 1
        unit_amount_cents:
          type: number
          minimum: 0
      required:
        - description
        - unit_amount_cents
    InvoiceUpdateRequest:
      type: object
      properties:
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: Actualiza la empresa emisora. Usa `null` para limpiar.
        collection_method:
          type: string
          enum:
            - charge_automatically
            - manual_charge
            - manual_invoice
          description: Actualiza el método de cobro.
        due_date:
          type: string
          description: Actualiza la fecha de vencimiento.
        description:
          type: string
        footer:
          type: string
        line_items:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceLineItemUpdateInput"
          description: Sustituye los ítems de línea. Para preservar un ítem, envía su `id`.
        nfe_issuance_policy:
          type:
            - string
            - "null"
          enum:
            - disabled
            - on_finalization
            - on_full_payment
            - per_installment
          description: Actualiza la política de NF-e. Usa `null` para limpiar.
        custom_metadata:
          type: object
          additionalProperties: {}
    InvoiceVoidRequest:
      type: object
      properties:
        reason:
          type: string
          description: Motivo de la cancelación (registrado en el audit log).
    InvoiceUndoPaymentRequest:
      type: object
      properties:
        reason:
          type: string
          description: Motivo de la reversión (registrado en el audit log).
    InvoicePayRequest:
      type: object
      properties:
        payment_method:
          type: string
          enum:
            - pix
            - bank_slip
            - card
          description: "Cuando se informa, dispara el gateway: `pix`, `bank_slip` o `card`. Omite para marcar como pagada manualmente."
        payment_method_id:
          type: string
          format: uuid
          description: "UUID de un método de pago guardado (ej.: tarjeta tokenizada)."
        card:
          type: object
          properties:
            card_token:
              type: string
              minLength: 1
              description: Token de la tarjeta obtenido en el front-end vía el SDK del gateway.
            holder_name:
              type: string
              minLength: 3
              description: Nombre impreso en la tarjeta (mínimo 3 caracteres).
          required:
            - card_token
            - holder_name
          description: Datos de la tarjeta cuando `payment_method = card` y no hay `payment_method_id`.
        amount_cents:
          type: integer
          exclusiveMinimum: 0
          description: "Valor a cobrar en centavos. Por defecto: valor restante de la factura."
        installment_ids:
          type: array
          items:
            type: string
            format: uuid
          description: UUIDs de las parcelas que el pago cubre (facturas en cuotas).
    InvoicePayResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    InvoiceApplyCreditsRequest:
      type: object
      properties:
        credit_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: UUIDs de los créditos a aplicar (mínimo 1).
        amount_cents:
          type: integer
          exclusiveMinimum: 0
          description: "Valor a aplicar en centavos. Por defecto: cubre toda la factura."
      required:
        - credit_ids
    InvoiceCreditsResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    InvoiceNfeResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    InvoicePaymentMethodsResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            invoice_id:
              type: string
              format: uuid
            methods:
              type: array
              items:
                type: string
                enum:
                  - card
                  - bank_slip
                  - pix
                  - bank_transfer
            details:
              type: array
              items:
                type: object
                properties:
                  type:
                    type: string
                    enum:
                      - card
                      - bank_slip
                      - pix
                      - bank_transfer
                  gateway_id:
                    type: string
                    format: uuid
                  gateway_name:
                    type: string
                  is_default:
                    type: boolean
                required:
                  - type
                  - gateway_id
                  - gateway_name
                  - is_default
            pagarme_public_key:
              type: string
          required:
            - invoice_id
            - methods
            - details
      required:
        - data
    InvoiceInstallmentsListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceInstallmentV1"
      required:
        - data
    InvoiceInstallmentsCreateRequest:
      type: object
      properties:
        installments:
          type: array
          items:
            $ref: "#/components/schemas/InvoiceInstallmentCreateInput"
          minItems: 2
          description: Lista de parcelas. Mínimo 2.
      required:
        - installments
    InvoiceInstallmentResponse:
      type: object
      properties:
        data:
          allOf:
            - $ref: "#/components/schemas/InvoiceInstallmentV1"
            - type:
                - object
                - "null"
      required:
        - data
    InvoiceInstallmentPayRequest:
      type: object
      properties:
        payment_method:
          type: string
          enum:
            - pix
            - bank_slip
            - card
          description: "Cuando se informa, dispara el gateway: `pix`, `bank_slip` o `card`. Omite para marcar como pagada manualmente."
        payment_method_id:
          type: string
          format: uuid
          description: "UUID de un método de pago guardado (ej.: tarjeta tokenizada)."
        card:
          type: object
          properties:
            card_token:
              type: string
              minLength: 1
              description: Token de la tarjeta obtenido en el front-end vía el SDK del gateway.
            holder_name:
              type: string
              minLength: 3
              description: Nombre impreso en la tarjeta (mínimo 3 caracteres).
          required:
            - card_token
            - holder_name
          description: Datos de la tarjeta cuando `payment_method = card` y no hay `payment_method_id`.
      required:
        - payment_method
    InvoiceDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    PaymentV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        invoice_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la factura asociada, cuando exista.
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la cuenta de cobro.
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora.
        payment_method_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del medio de pago utilizado.
        gateway_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del gateway que procesó el pago.
        payment_method_type:
          type:
            - string
            - "null"
          enum:
            - card
            - bank_slip
            - pix
            - bank_transfer
          description: "Tipo del medio de pago: `card`, `bank_slip`, `pix` o `bank_transfer`."
        amount_cents:
          type: integer
          description: Monto cobrado en centavos (÷100 → BRL).
          example: 10000
        paid_amount_cents:
          type:
            - integer
            - "null"
          description: Monto efectivamente recibido en centavos (÷100 → BRL).
        currency:
          type: string
          description: "Moneda ISO 4217 (ej.: `BRL`)."
          example: BRL
        status:
          type: string
          enum:
            - pending
            - processing
            - succeeded
            - failed
            - canceled
            - partial
            - refunded
            - partially_refunded
          description: "Estado: `pending`, `processing`, `succeeded`, `failed`, `canceled`, `partial`, `refunded` o `partially_refunded`."
        payment_gateway:
          type:
            - string
            - "null"
          description: "Nombre del gateway upstream (ej.: `KOBANA`, `PAGARME`)."
        gateway_payment_id:
          type:
            - string
            - "null"
          description: Identificador del pago en el gateway upstream.
        failure_code:
          type:
            - string
            - "null"
          description: Código de falla devuelto por el gateway.
        failure_message:
          type:
            - string
            - "null"
          description: Mensaje de falla devuelto por el gateway.
        idempotency_key:
          type:
            - string
            - "null"
          description: Clave de idempotencia provista al crear el pago.
        paid_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora de la confirmación del pago.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - invoice_id
        - billing_account_id
        - company_id
        - payment_method_id
        - gateway_id
        - payment_method_type
        - amount_cents
        - paid_amount_cents
        - currency
        - status
        - payment_gateway
        - gateway_payment_id
        - failure_code
        - failure_message
        - idempotency_key
        - paid_at
        - custom_metadata
        - created_at
        - updated_at
    PaymentListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PaymentV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    PaymentResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/PaymentV1"
      required:
        - data
    PaymentCreateCard:
      type: object
      properties:
        cardToken:
          type: string
          minLength: 1
          description: Token de tarjeta emitido por el gateway (preferido).
        number:
          type: string
          minLength: 13
          maxLength: 19
          description: Número de la tarjeta (PAN). Use el token siempre que sea posible.
        expMonth:
          type: integer
          minimum: 1
          maximum: 12
          description: Mes de expiración (1–12).
        expYear:
          type: integer
          description: Año de expiración (≥ año actual).
        cvv:
          type: string
          minLength: 3
          maxLength: 4
          description: CVV/CVC de la tarjeta (3 a 4 dígitos).
        holderName:
          type: string
          minLength: 1
          description: Nombre del portador tal como aparece en la tarjeta.
      required:
        - holderName
    PaymentCreatePaymentMethod:
      type: object
      properties:
        type:
          type: string
          enum:
            - card
            - bank_slip
            - pix
          description: "Tipo del medio de pago: `card`, `bank_slip` o `pix`."
        card:
          allOf:
            - $ref: "#/components/schemas/PaymentCreateCard"
            - description: Datos de la tarjeta — token (preferido) **o** PAN/expiración/CVV.
      required:
        - type
    PaymentCreateRequest:
      type: object
      properties:
        invoiceId:
          type: string
          format: uuid
          description: UUID de la factura a pagar.
        paymentMethodId:
          type: string
          format: uuid
          description: UUID de un medio de pago previamente registrado para la cuenta.
        paymentMethod:
          allOf:
            - $ref: "#/components/schemas/PaymentCreatePaymentMethod"
            - description: Bloque con datos del medio de pago — usado cuando no hay `payment_method_id`.
        amountCents:
          type: integer
          minimum: 1
          description: "Monto a cobrar en centavos (÷100 → BRL). Por defecto: saldo abierto de la factura."
        customMetadata:
          type: object
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente para correlación interna.
      required:
        - invoiceId
    PaymentCancelRequest:
      type: object
      properties:
        reason:
          type: string
          description: Motivo de la cancelación (registrado en el audit log).
        cancel_on_gateway:
          type: boolean
          description: Cuando es `true` (por defecto), también cancela en el gateway upstream.
    PaymentRetryCard:
      type: object
      properties:
        cardToken:
          type: string
          minLength: 1
          description: Token de tarjeta emitido por el gateway.
        holderName:
          type: string
          minLength: 3
          description: Nombre del portador tal como aparece en la tarjeta.
      required:
        - cardToken
        - holderName
    PaymentRetryRequest:
      type: object
      properties:
        card:
          allOf:
            - $ref: "#/components/schemas/PaymentRetryCard"
            - description: Tarjeta alternativa (token y portador) para el reintento.
        payment_method_id:
          type: string
          format: uuid
          description: UUID de un medio de pago guardado para usar en el reintento.
    PaymentChangeCardRequest:
      type: object
      properties:
        credit_card_id:
          type: string
          format: uuid
          description: UUID de la tarjeta de crédito activa de la cuenta de cobro.
      required:
        - credit_card_id
    PaymentChangeDueDateRequest:
      type: object
      properties:
        due_date:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
          description: Nueva fecha de vencimiento en formato `YYYY-MM-DD`.
          example: 2026-08-15
      required:
        - due_date
    PaymentChangeDueDateResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            due_date:
              type: string
            updated:
              type: boolean
          required:
            - due_date
            - updated
      required:
        - data
    PaymentSendEmailRequest:
      type: object
      properties:
        email:
          type: string
          format: email
          description: Dirección de correo del destinatario.
        message:
          type: string
          description: Mensaje opcional incluido en el cuerpo del correo.
      required:
        - email
    PaymentSendEmailResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            sent:
              type: boolean
            email:
              type: string
              format: email
          required:
            - sent
            - email
      required:
        - data
    PaymentRefundRequest:
      type: object
      properties:
        amount_cents:
          type: integer
          minimum: 1
          description: "Monto a reembolsar en centavos (÷100 → BRL). Por defecto: monto total del pago."
        reason:
          type: string
          description: Motivo del reembolso (registrado en el audit log).
    PaymentBulkUpdateInvoiceRequest:
      type: object
      properties:
        payment_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: Lista de UUIDs de los pagos a actualizar.
        invoice_id:
          type: string
          format: uuid
          description: UUID de la factura de destino.
      required:
        - payment_ids
        - invoice_id
    PaymentBulkUpdateInvoiceResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            updated_count:
              type: integer
          required:
            - updated_count
      required:
        - data
    PaymentDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    PaymentMethodCreditCardV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: UUID del registro de tarjeta.
        status:
          type: string
          enum:
            - active
            - expired
            - removed
          description: "Estado de la tarjeta: `active`, `expired`, `removed`."
        brand:
          type:
            - string
            - "null"
          description: "Marca de la tarjeta (ej.: `visa`, `mastercard`)."
          example: visa
        last_four_digits:
          type:
            - string
            - "null"
          description: Últimos cuatro dígitos de la tarjeta.
          example: "4242"
        holder_name:
          type:
            - string
            - "null"
          description: Nombre del titular como aparece en la tarjeta.
        expiration_month:
          type:
            - integer
            - "null"
          description: Mes de vencimiento (1–12).
          example: 12
        expiration_year:
          type:
            - integer
            - "null"
          description: Año de vencimiento (4 dígitos).
          example: 2030
      required:
        - id
        - status
        - brand
        - last_four_digits
        - holder_name
        - expiration_month
        - expiration_year
    PaymentMethodBankAccountV1:
      type: object
      properties:
        bank_code:
          type:
            - string
            - "null"
          description: Código de compensación del banco (FEBRABAN, 3 dígitos).
          example: "341"
        bank_name:
          type:
            - string
            - "null"
          description: Nombre del banco, resuelto a partir del `bank_code`.
          example: Itaú
        agency:
          type:
            - string
            - "null"
          description: Agencia bancaria.
        account:
          type:
            - string
            - "null"
          description: Número de la cuenta bancaria.
        account_type:
          type:
            - string
            - "null"
          enum:
            - checking
            - savings
          description: "Tipo de cuenta: `checking` o `savings`."
        holder_name:
          type:
            - string
            - "null"
          description: Nombre del titular de la cuenta.
      required:
        - bank_code
        - bank_name
        - agency
        - account
        - account_type
        - holder_name
    PaymentMethodV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro propietaria.
        credit_card_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del `credit_card` asociado, cuando `type=card`.
        type:
          type: string
          enum:
            - card
            - bank_slip
            - pix
            - bank_transfer
          description: "Tipo del método: `card`, `bank_slip`, `pix` o `bank_transfer`."
        status:
          type: string
          enum:
            - active
            - expired
            - failed
            - removed
          description: "Estado del método: `active`, `expired`, `failed` o `removed`."
        is_default:
          type: boolean
          description: Indica si este es el método predeterminado de la cuenta de cobro.
        is_backup:
          type: boolean
          description: Indica si el método se usa como respaldo del predeterminado.
        billing_details:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Datos de cobro opcionales (nombre, correo, teléfono, dirección) enviados al gateway.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos personalizados en formato libre (`object`).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        bank_account:
          allOf:
            - $ref: "#/components/schemas/PaymentMethodBankAccountV1"
            - description: Resumen público de la cuenta bancaria, presente cuando `type=bank_transfer`. No incluye el documento del titular.
        credit_card:
          allOf:
            - $ref: "#/components/schemas/PaymentMethodCreditCardV1"
            - description: Resumen público de la tarjeta, presente cuando `type=card`. No incluye tokens del gateway ni fingerprint.
      required:
        - id
        - billing_account_id
        - credit_card_id
        - type
        - status
        - is_default
        - is_backup
        - created_at
        - updated_at
    PaymentMethodListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PaymentMethodV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    PaymentMethodResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/PaymentMethodV1"
      required:
        - data
    PaymentMethodCardInput:
      type: object
      properties:
        cardToken:
          type: string
          minLength: 1
          description: Token de la tarjeta generado por la tokenización del lado del cliente del gateway.
        holderName:
          type: string
          minLength: 1
          description: Nombre del titular como aparece en la tarjeta.
      required:
        - cardToken
        - holderName
    PaymentMethodBankAccountInput:
      type: object
      properties:
        bankCode:
          type: string
          minLength: 3
          maxLength: 3
          description: Código de compensación del banco (FEBRABAN, 3 dígitos).
          example: "341"
        agency:
          type: string
          minLength: 1
          description: Agencia bancaria.
        account:
          type: string
          minLength: 1
          description: Número de la cuenta bancaria.
        accountType:
          type: string
          enum:
            - checking
            - savings
          description: "Tipo de cuenta: `checking` o `savings`."
        holderName:
          type: string
          minLength: 1
          description: Nombre del titular de la cuenta.
        holderDocument:
          type: string
          minLength: 11
          maxLength: 14
          description: CPF o CNPJ del titular (11 a 14 dígitos).
      required:
        - bankCode
        - agency
        - account
        - accountType
        - holderName
        - holderDocument
    PaymentMethodBillingAddress:
      type: object
      properties:
        street:
          type: string
        number:
          type: string
        complement:
          type: string
        neighborhood:
          type: string
        city:
          type: string
        state:
          type: string
          minLength: 2
          maxLength: 2
        zipCode:
          type: string
        country:
          type: string
      required:
        - street
        - number
        - neighborhood
        - city
        - state
        - zipCode
    PaymentMethodBillingDetailsInput:
      type: object
      properties:
        name:
          type: string
          description: Nombre para los datos de cobro.
        email:
          type: string
          format: email
          description: Correo para los datos de cobro.
        phone:
          type: string
          description: Teléfono para los datos de cobro.
        address:
          allOf:
            - $ref: "#/components/schemas/PaymentMethodBillingAddress"
            - description: Dirección de cobro.
    PaymentMethodCreateRequest:
      type: object
      properties:
        billingAccountId:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro propietaria del método de pago.
        type:
          type: string
          enum:
            - card
            - bank_slip
            - pix
            - bank_transfer
          description: "Tipo del método: `card`, `bank_slip`, `pix` o `bank_transfer`."
        isDefault:
          type: boolean
          description: Cuando es `true`, marca este método como predeterminado de la cuenta (desmarca los anteriores). El primer método creado para una cuenta también se vuelve predeterminado automáticamente.
        card:
          allOf:
            - $ref: "#/components/schemas/PaymentMethodCardInput"
            - description: Bloque de tarjeta. Obligatorio cuando `type=card`.
        bankAccount:
          allOf:
            - $ref: "#/components/schemas/PaymentMethodBankAccountInput"
            - description: Bloque de cuenta bancaria. Obligatorio cuando `type=bank_transfer`.
        billingDetails:
          allOf:
            - $ref: "#/components/schemas/PaymentMethodBillingDetailsInput"
            - description: Datos de cobro opcionales (nombre, correo, teléfono, dirección) usados por el gateway.
      required:
        - billingAccountId
        - type
    PaymentMethodUpdateRequest:
      type: object
      properties:
        exp_month:
          type: integer
          minimum: 1
          maximum: 12
          description: Mes de vencimiento de la tarjeta (1–12).
        exp_year:
          type: integer
          description: Año de vencimiento de la tarjeta (>= año actual).
      required:
        - exp_month
        - exp_year
    NfeV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        invoice_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la factura vinculada (cuando la NF-e se emitió para una factura).
        transaction_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la transacción de origen, cuando corresponda.
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro (tomador) de la NF-e.
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización emisora.
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora (prestador). Opcional cuando la organización tiene empresa por defecto.
        person_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la persona (tomador) asociada a la NF-e.
        status:
          type: string
          enum:
            - draft
            - pending
            - processing
            - issued
            - pending_cancel
            - canceling
            - canceled
            - error
          description: "Estado: `draft`, `pending`, `processing`, `issued`, `pending_cancel`, `canceling`, `canceled` o `error`."
        nfe_number:
          type:
            - integer
            - "null"
          description: Número de la NF-e asignado por la municipalidad (establecido tras la emisión).
        nfe_series:
          type:
            - string
            - "null"
          description: Serie de la NF-e.
        nfe_key:
          type:
            - string
            - "null"
          description: Clave de acceso única de la NF-e (44 o 50 dígitos, según el municipio).
        verification_code:
          type:
            - string
            - "null"
          description: Código de verificación/autenticidad impreso en el DANFSE.
        issue_date:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora de emisión por la municipalidad.
        cancelled_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora de la cancelación (cuando corresponda).
        amount_cents:
          type: integer
          description: Monto de la NF-e en centavos (÷100 → BRL).
        service_code:
          type:
            - string
            - "null"
          description: Código del servicio según la tabla municipal.
        service_description:
          type:
            - string
            - "null"
          description: Descripción libre del servicio prestado.
        lc116_code:
          type:
            - string
            - "null"
          description: Código LC 116 (lista nacional de servicios).
        cnae_code:
          type:
            - string
            - "null"
          description: Código CNAE de la actividad.
        nbs_code:
          type:
            - string
            - "null"
          description: Código NBS (Nomenclatura Brasileña de Servicios).
        ncm_code:
          type:
            - string
            - "null"
          description: Código NCM (Nomenclatura Común del Mercosur), cuando corresponda.
        customer_snapshot:
          type: object
          additionalProperties: {}
          description: Instantánea de los datos del tomador al momento de la emisión (nombre, documento, dirección).
        taxes:
          type: object
          additionalProperties: {}
          description: Alícuotas y montos de impuestos (ISS, PIS, COFINS, CSLL, IR, INSS).
        xml_url:
          type:
            - string
            - "null"
          description: URL pública para el XML alojado por el proveedor.
        pdf_url:
          type:
            - string
            - "null"
          description: URL pública para el PDF (DANFE/DANFSE) alojado por el proveedor.
        provider_company_id:
          type:
            - string
            - "null"
          description: Identificador de la empresa en el proveedor externo.
        rps_number:
          type:
            - integer
            - "null"
          description: Número del RPS (Recibo Provisorio de Servicios).
        rps_serial_number:
          type:
            - string
            - "null"
          description: Serie del RPS.
        issue_attempts:
          type: integer
          description: Cantidad de intentos de emisión realizados.
        cancel_attempts:
          type: integer
          description: Cantidad de intentos de cancelación realizados.
        next_retry_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora del próximo reintento automático (emisión o cancelación).
        error_message:
          type:
            - string
            - "null"
          description: Mensaje de error devuelto por el proveedor en el último intento.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Campos personalizados definidos por la organización.
        pdf_downloaded_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha en que el PDF fue descargado y persistido en la base de datos.
        xml_downloaded_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha en que el XML fue descargado y persistido en la base de datos.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - invoice_id
        - transaction_id
        - billing_account_id
        - organization_id
        - company_id
        - person_id
        - status
        - nfe_number
        - nfe_series
        - nfe_key
        - verification_code
        - issue_date
        - cancelled_at
        - amount_cents
        - service_code
        - service_description
        - lc116_code
        - cnae_code
        - nbs_code
        - ncm_code
        - customer_snapshot
        - taxes
        - xml_url
        - pdf_url
        - provider_company_id
        - rps_number
        - rps_serial_number
        - issue_attempts
        - cancel_attempts
        - next_retry_at
        - error_message
        - pdf_downloaded_at
        - xml_downloaded_at
        - created_at
        - updated_at
    NfeListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/NfeV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    NfeResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/NfeV1"
      required:
        - data
    NfeCustomerAddressInput:
      type: object
      properties:
        street:
          type: string
          description: Calle.
        number:
          type: string
          description: Número de la dirección.
        complement:
          type: string
          description: Complemento (opcional).
        neighborhood:
          type: string
          description: Barrio.
        city:
          type: string
          description: Ciudad.
        city_code:
          type: string
          description: Código IBGE de la ciudad (opcional).
        state:
          type: string
          description: Estado (sigla de dos letras).
        zip_code:
          type: string
          description: Código postal.
        country:
          type: string
          description: País (opcional, por defecto `BR`).
      required:
        - street
        - number
        - neighborhood
        - city
        - state
        - zip_code
      description: Dirección del tomador.
    NfeCustomerInput:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          description: Nombre o razón social del tomador.
        email:
          type: string
          format: email
          description: Correo del tomador.
        document_type:
          type: string
          enum:
            - cpf
            - cnpj
          description: "Tipo de documento: `cpf` o `cnpj`."
        document_number:
          type: string
          minLength: 1
          description: Número del documento (solo dígitos o con formato).
        phone:
          type: string
          description: Teléfono del tomador.
        address:
          $ref: "#/components/schemas/NfeCustomerAddressInput"
      required:
        - name
        - email
        - document_type
        - document_number
      description: Datos del tomador al momento de la emisión. **Obligatorio**.
    NfeTaxesInput:
      type: object
      properties:
        iss_rate:
          type: number
          description: Alícuota de ISS (decimal).
        pis_rate:
          type: number
          description: Alícuota de PIS (decimal).
        cofins_rate:
          type: number
          description: Alícuota de COFINS (decimal).
        csll_rate:
          type: number
          description: Alícuota de CSLL (decimal).
        ir_rate:
          type: number
          description: Alícuota de IR (decimal).
        inss_rate:
          type: number
          description: Alícuota de INSS (decimal).
      description: "Alícuotas de impuestos (en decimal, ej.: `0.05` para 5%). Aplica los defaults de la empresa cuando se omite."
    NfeCreateRequest:
      type: object
      properties:
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro (tomador). **Obligatorio**.
        company_id:
          type: string
          format: uuid
          description: "UUID de la empresa emisora (prestador). Por defecto: empresa por defecto de la organización."
        invoice_id:
          type: string
          format: uuid
          description: UUID de la factura vinculada (opcional).
        transaction_id:
          type: string
          format: uuid
          description: UUID de la transacción de origen (opcional).
        amount_cents:
          type: integer
          exclusiveMinimum: 0
          description: Monto de la NF-e en centavos (÷100 → BRL). **Obligatorio**, debe ser positivo.
        service_code:
          type: string
          description: Código del servicio según la tabla municipal.
        service_description:
          type: string
          description: Descripción libre del servicio prestado.
        lc116_code:
          type: string
          description: Código LC 116.
        cnae_code:
          type: string
          description: Código CNAE.
        nbs_code:
          type: string
          description: Código NBS.
        rps_number:
          type: integer
          description: Número del RPS (cuando la numeración es gestionada por la aplicación).
        rps_serial_number:
          type: string
          description: Serie del RPS.
        customer:
          $ref: "#/components/schemas/NfeCustomerInput"
        taxes:
          $ref: "#/components/schemas/NfeTaxesInput"
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Campos personalizados (validados contra el schema de la organización).
      required:
        - billing_account_id
        - amount_cents
        - customer
    NfeUpdateRequest:
      type: object
      properties:
        service_code:
          type: string
          description: Actualiza el código del servicio.
        service_description:
          type: string
          description: Actualiza la descripción del servicio.
        amount_cents:
          type: integer
          exclusiveMinimum: 0
          description: Actualiza el monto de la NF-e en centavos.
        lc116_code:
          type:
            - string
            - "null"
          description: Actualiza el código LC 116. Usa `null` para limpiar.
        cnae_code:
          type:
            - string
            - "null"
          description: Actualiza el código CNAE. Usa `null` para limpiar.
        nbs_code:
          type:
            - string
            - "null"
          description: Actualiza el código NBS. Usa `null` para limpiar.
        ncm_code:
          type:
            - string
            - "null"
          description: Actualiza el código NCM. Usa `null` para limpiar.
        rps_number:
          type:
            - integer
            - "null"
          description: Actualiza el número del RPS. Usa `null` para limpiar.
        rps_serial_number:
          type:
            - string
            - "null"
          description: Actualiza la serie del RPS. Usa `null` para limpiar.
        customer_snapshot:
          type: object
          additionalProperties: {}
          description: Sustituye el snapshot del tomador.
        taxes:
          type: object
          additionalProperties: {}
          description: Sustituye las alícuotas y montos de impuestos.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Actualiza los campos personalizados.
    NfeCancelRequest:
      type: object
      properties:
        reason:
          type: string
          minLength: 15
          description: Motivo de la cancelación. **Mínimo 15 caracteres**. Registrado en el audit log y enviado al proveedor.
      required:
        - reason
    NfeDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    NfeBulkUpdateRequest:
      type: object
      properties:
        nfe_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: UUIDs de las NF-e a actualizar. Mínimo 1.
        invoice_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la factura a vincular. Envía `null` para desvincular.
      required:
        - nfe_ids
        - invoice_id
    NfeBulkUpdateResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            updated:
              type: integer
          required:
            - updated
      required:
        - data
    NfeBulkDeleteRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          maxItems: 100
          description: UUIDs de las NF-e a eliminar. Mínimo 1, máximo 100.
      required:
        - ids
    NfeBulkDeleteResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted_count:
              type: integer
          required:
            - deleted_count
      required:
        - data
    NfeSyncOrgResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            job_id:
              type: string
            status:
              type: string
          required:
            - job_id
            - status
      required:
        - data
    NfeRequestTryV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        nfe_id:
          type: string
          format: uuid
        action:
          type: string
          description: "Acción intentada: `issue`, `cancel` o `query`."
        endpoint:
          type: string
          description: Endpoint del proveedor llamado.
        request_body:
          type:
            - string
            - "null"
        response_body:
          type:
            - string
            - "null"
        http_status:
          type:
            - string
            - "null"
          description: Estado HTTP devuelto por el proveedor.
        success:
          type: boolean
        error_message:
          type:
            - string
            - "null"
        duration_ms:
          type:
            - integer
            - "null"
          description: Duración de la solicitud en milisegundos.
        created_at:
          type: string
          format: date-time
      required:
        - id
        - nfe_id
        - action
        - endpoint
        - request_body
        - response_body
        - http_status
        - success
        - error_message
        - duration_ms
        - created_at
    NfeRequestTryListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/NfeRequestTryV1"
      required:
        - data
    NfeRequestTryPaginatedResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/NfeRequestTryV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    NfeExternalRequestSummaryV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        nfe_id:
          type:
            - string
            - "null"
          format: uuid
        invoice_id:
          type:
            - string
            - "null"
          format: uuid
        direction:
          type: string
          enum:
            - outbound
            - inbound
          description: "Dirección de la solicitud: `outbound` (llamada saliente) o `inbound` (webhook recibido)."
        service:
          type:
            - string
            - "null"
          description: "Proveedor/servicio externo (ej.: `nfeio`, `barueri`)."
        method:
          type: string
        endpoint:
          type: string
        http_status:
          type:
            - integer
            - "null"
        success:
          type: boolean
        error_message:
          type:
            - string
            - "null"
        duration_ms:
          type:
            - integer
            - "null"
        created_at:
          type: string
          format: date-time
      required:
        - id
        - nfe_id
        - invoice_id
        - direction
        - service
        - method
        - endpoint
        - http_status
        - success
        - error_message
        - duration_ms
        - created_at
    NfeExternalRequestV1:
      allOf:
        - $ref: "#/components/schemas/NfeExternalRequestSummaryV1"
        - type: object
          properties:
            request_headers:
              type:
                - object
                - "null"
              additionalProperties: {}
            request_body:
              type:
                - string
                - "null"
            response_headers:
              type:
                - object
                - "null"
              additionalProperties: {}
            response_body:
              type:
                - string
                - "null"
            metadata:
              type:
                - object
                - "null"
              additionalProperties: {}
          required:
            - request_headers
            - request_body
            - response_headers
            - response_body
            - metadata
    NfeExternalRequestListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/NfeExternalRequestSummaryV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    NfeExternalRequestResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/NfeExternalRequestV1"
      required:
        - data
    NfeFetchBarueriRequest:
      type: object
      properties:
        codigo_autenticidade:
          type: string
          minLength: 1
          description: Código de autenticidad impreso en el DANFSE de la NFS-e.
        cnpj_tomador:
          type: string
          minLength: 1
          description: CPF o CNPJ del tomador de la NFS-e (solo dígitos o con formato).
        ambiente:
          type: string
          enum:
            - production
            - homologation
          description: "Ambiente municipal: `production` o `homologation`. Por defecto: `production`."
      required:
        - codigo_autenticidade
        - cnpj_tomador
    NfeFetchBarueriResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    NfeImportBarueriRequest:
      type: object
      properties:
        xml:
          type: string
          description: XML crudo devuelto por `POST /nfes/fetch-barueri`. Opcional — cuando se omite, la NFS-e se vuelve a buscar con `codigo_autenticidade` + `cnpj_tomador`.
        codigo_autenticidade:
          type: string
          minLength: 1
          description: Código de autenticidad impreso en el DANFSE de la NFS-e.
        cnpj_tomador:
          type: string
          minLength: 1
          description: CPF o CNPJ del tomador de la NFS-e (solo dígitos o con formato).
        ambiente:
          type: string
          enum:
            - production
            - homologation
          description: "Ambiente municipal: `production` o `homologation`. Por defecto: `production`."
      required:
        - codigo_autenticidade
        - cnpj_tomador
    NfeImportBarueriResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    NfeValidateBarueriResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    NfeFetchFromSefazRequest:
      type: object
      properties:
        access_key:
          type: string
          minLength: 1
          description: Clave de acceso de la NFS-e (50 dígitos).
        certificate_id:
          type: string
          minLength: 1
          description: UUID del certificado digital registrado en la organización (usado para autenticación mTLS).
        ambiente:
          anyOf:
            - type: number
              enum:
                - 1
            - type: number
              enum:
                - 2
          description: "Ambiente SEFAZ: `1` (producción) o `2` (homologación). Por defecto: `2`."
      required:
        - access_key
        - certificate_id
    NfeFetchFromSefazResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    ProposalItemV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        proposal_id:
          type: string
          format: uuid
        product_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del producto.
        price_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del precio.
        quantity:
          type: number
          description: Cantidad.
        unit_amount_subcents:
          type: integer
          description: Valor unitario en subcentavos (÷10000 → BRL).
        setup_amount_subcents:
          type:
            - integer
            - "null"
          description: Setup unitario en subcentavos (÷10000 → BRL).
        excess_amount_subcents:
          type:
            - integer
            - "null"
          description: Valor adicional por excedente en subcentavos (÷10000 → BRL).
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - proposal_id
        - product_id
        - price_id
        - quantity
        - unit_amount_subcents
        - created_at
        - updated_at
    ProposalV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        public_id:
          type: string
          format: uuid
          description: UUID público — expuesto en URLs del portal (`/p/{code}`).
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora (opcional).
        code:
          type: string
          description: "Código corto único por organización (ej.: `PROP-0001`)."
        title:
          type: string
          description: 'Título de la propuesta (por defecto: "Proposta Comercial").'
        description:
          type:
            - string
            - "null"
          description: Descripción libre mostrada en la propuesta.
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la cuenta de cobro destino, cuando la propuesta es para un cliente existente.
        prospect_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del prospect (persona jurídica en prospección), cuando aún no hay `billing_account_id`.
        contact_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del contacto (persona física responsable dentro del prospect).
        coupon_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del cupón aplicado a la propuesta.
        template_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del template usado para generar el PDF.
        document_google_id:
          type:
            - string
            - "null"
          description: ID del Google Doc generado por el App Script.
        document_url:
          type:
            - string
            - "null"
          description: URL del documento Google Doc.
        pdf_url:
          type:
            - string
            - "null"
          description: URL pública del PDF de la propuesta.
        generated_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora de la última generación del PDF.
        pdf_outdated_at:
          type:
            - string
            - "null"
          format: date-time
          description: Marca cuándo el PDF quedó desactualizado respecto a los ítems/precios actuales.
        expires_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha de expiración de la propuesta.
        status:
          type: string
          enum:
            - draft
            - sent
            - viewed
            - accepted
            - rejected
            - expired
            - canceled
          description: "Estado: `draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired` o `canceled`."
        rejection_reason:
          type:
            - string
            - "null"
          description: Motivo de rechazo informado por el prospect.
        setup_amount_cents:
          type: integer
          description: Valor de setup (one-time) en centavos (÷100 → BRL).
        monthly_amount_cents:
          type: integer
          description: Valor mensual recurrente en centavos (÷100 → BRL).
        notes:
          type:
            - string
            - "null"
          description: Notas internas (no mostradas al cliente).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Campos personalizados definidos por la organización.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        sent_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora del envío.
        viewed_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora de la primera visualización por el destinatario.
        accepted_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora de la aceptación.
        rejected_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha y hora del rechazo.
        items:
          type: array
          items:
            $ref: "#/components/schemas/ProposalItemV1"
          description: Ítems de la propuesta — presente cuando se incluye vía `include=items`.
      required:
        - id
        - public_id
        - company_id
        - code
        - title
        - description
        - billing_account_id
        - prospect_id
        - contact_id
        - coupon_id
        - template_id
        - document_google_id
        - document_url
        - pdf_url
        - generated_at
        - pdf_outdated_at
        - expires_at
        - status
        - rejection_reason
        - setup_amount_cents
        - monthly_amount_cents
        - notes
        - created_at
        - updated_at
        - sent_at
        - viewed_at
        - accepted_at
        - rejected_at
    ProposalListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProposalV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ProposalResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/ProposalV1"
      required:
        - data
    ProposalItemInput:
      type: object
      properties:
        product_id:
          type: string
          format: uuid
          description: UUID del producto. **Obligatorio**.
        price_id:
          type: string
          format: uuid
          description: UUID del precio. **Obligatorio**.
        quantity:
          type: number
          minimum: 1
          description: "Cantidad. Por defecto: 1."
        unit_amount_subcents:
          type: number
          minimum: 0
          description: Valor unitario en subcentavos (÷10000 → BRL).
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Campos personalizados del ítem.
      required:
        - product_id
        - price_id
        - unit_amount_subcents
    ProspectDataInput:
      type: object
      properties:
        document:
          type: string
          description: CNPJ del prospect (solo dígitos o con formato).
        name:
          type: string
          minLength: 1
          description: Razón social del prospect.
        legal_name:
          type: string
          description: Razón social alternativa.
        nickname:
          type: string
          description: Nombre fantasía.
        email:
          type: string
          format: email
          description: Correo principal del prospect.
        phone:
          type: string
          description: Teléfono principal del prospect.
      required:
        - name
      description: Datos para crear un prospect inline (persona jurídica). Alternativa a `prospect_id`/`billing_account_id`.
    ContactDataInput:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          description: Nombre del contacto (persona responsable).
        email:
          type: string
          format: email
          description: Correo del contacto.
        phone:
          type: string
          description: Teléfono del contacto.
      required:
        - name
      description: Datos para crear un contacto inline (persona física responsable).
    ProposalCreateRequest:
      type: object
      properties:
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora. Usa `null` para limpiar.
        title:
          type: string
          minLength: 1
          maxLength: 255
          description: 'Título de la propuesta. Por defecto: "Proposta Comercial".'
        description:
          type:
            - string
            - "null"
          description: Descripción libre de la propuesta.
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la cuenta de cobro destino (cliente existente). Obligatorio si no se informan `prospect_id` ni `prospect_data`.
        prospect_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de un prospect existente. Alternativa a `billing_account_id`/`prospect_data`.
        contact_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de un contacto existente vinculado al prospect.
        prospect_data:
          $ref: "#/components/schemas/ProspectDataInput"
        contact_data:
          $ref: "#/components/schemas/ContactDataInput"
        coupon_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del cupón a aplicar.
        template_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del template para generar el PDF.
        expires_at:
          type: string
          description: Fecha de expiración.
        notes:
          type:
            - string
            - "null"
          description: Notas internas.
        items:
          type: array
          items:
            $ref: "#/components/schemas/ProposalItemInput"
          minItems: 1
          description: Ítems de la propuesta. Mínimo 1.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Campos personalizados (validados contra el schema de la organización).
    ProposalUpdateRequest:
      type: object
      properties:
        company_id:
          type:
            - string
            - "null"
          format: uuid
        title:
          type: string
          minLength: 1
          maxLength: 255
          description: Actualiza el título.
        description:
          type:
            - string
            - "null"
          description: Actualiza la descripción. Usa `null` para limpiar.
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
        prospect_id:
          type:
            - string
            - "null"
          format: uuid
        contact_id:
          type:
            - string
            - "null"
          format: uuid
        prospect_data:
          $ref: "#/components/schemas/ProspectDataInput"
        contact_data:
          $ref: "#/components/schemas/ContactDataInput"
        coupon_id:
          type:
            - string
            - "null"
          format: uuid
        template_id:
          type:
            - string
            - "null"
          format: uuid
        expires_at:
          type:
            - string
            - "null"
          description: Actualiza la fecha de expiración. Usa `null` para limpiar.
        notes:
          type:
            - string
            - "null"
          description: Actualiza las notas. Usa `null` para limpiar.
        items:
          type: array
          items:
            $ref: "#/components/schemas/ProposalItemInput"
          description: Sustituye la lista de ítems de la propuesta.
        custom_metadata:
          type: object
          additionalProperties: {}
    ProposalAcceptRequest:
      type: object
      properties:
        cycle:
          type: string
          enum:
            - monthly
            - quarterly
            - semiannual
            - annual
          description: "Ciclo de cobro de la suscripción creada al aceptar: `monthly`, `quarterly`, `semiannual` o `annual`."
      required:
        - cycle
    ProposalAcceptResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            proposal:
              $ref: "#/components/schemas/ProposalV1"
            subscription_id:
              type:
                - string
                - "null"
              format: uuid
            invoice_id:
              type:
                - string
                - "null"
              format: uuid
          required:
            - proposal
            - subscription_id
            - invoice_id
      required:
        - data
    ProposalSendRequest:
      type: object
      properties:
        subject:
          type: string
          description: "Asunto personalizado del correo. Por defecto: `Proposta {code}`."
        message:
          type: string
          description: Mensaje HTML personalizado del cuerpo del correo.
    ProposalGeneratePdfResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            proposal:
              $ref: "#/components/schemas/ProposalV1"
            pdf:
              type: object
              properties:
                document_id:
                  type:
                    - string
                    - "null"
                document_url:
                  type:
                    - string
                    - "null"
                pdf_url:
                  type:
                    - string
                    - "null"
              required:
                - document_id
                - document_url
                - pdf_url
          required:
            - proposal
            - pdf
      required:
        - data
    ProposalJsonResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            template_data: {}
            proposal:
              type: object
              properties:
                id:
                  type: string
                  format: uuid
                code:
                  type: string
                template_google_doc_id:
                  type:
                    - string
                    - "null"
                document_google_id:
                  type:
                    - string
                    - "null"
              required:
                - id
                - code
                - template_google_doc_id
                - document_google_id
          required:
            - proposal
      required:
        - data
    ProposalPricingResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties: {}
      required:
        - data
    ProposalItemsListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProposalItemV1"
      required:
        - data
    ProposalItemResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/ProposalItemV1"
      required:
        - data
    ProposalItemUpdateRequest:
      type: object
      properties:
        quantity:
          type: integer
          minimum: 1
          description: Actualiza la cantidad.
        unit_amount_subcents:
          type: number
          minimum: 0
          description: Actualiza el valor unitario en subcentavos (÷10000 → BRL).
        setup_amount_subcents:
          type: number
          minimum: 0
          description: Actualiza el setup unitario en subcentavos (÷10000 → BRL).
        custom_metadata:
          type: object
          additionalProperties: {}
          description: proposals.items.update.body.custom_metadata
    ProposalBulkRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          maxItems: 100
          description: Lista de UUIDs de las propuestas objetivo. Mínimo 1, máximo 100.
      required:
        - ids
    ProposalBulkResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            success_count:
              type: integer
            failed_count:
              type: integer
            errors:
              type: array
              items:
                type: object
                additionalProperties: {}
          required:
            - success_count
            - failed_count
            - errors
      required:
        - data
    ProposalStatsResponse:
      type: object
      properties:
        data:
          type: object
          additionalProperties:
            type: integer
      required:
        - data
    ProposalDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    ProposalTemplateV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        public_id:
          type: string
          format: uuid
          description: Identificador público estable de la plantilla.
        name:
          type: string
          description: Nombre de la plantilla (único por organización).
        description:
          type:
            - string
            - "null"
          description: Descripción libre de la plantilla.
        document_type:
          type: string
          enum:
            - google_doc
            - google_slides
          description: "Tipo de documento base: `google_doc` o `google_slides`."
        google_doc_id:
          type: string
          description: ID del Google Doc/Slides usado como base de la plantilla.
        app_script_url:
          type:
            - string
            - "null"
          description: URL del Apps Script que renderiza la plantilla, cuando esté configurado.
        google_drive_folder_id:
          type:
            - string
            - "null"
          description: ID de la carpeta de Google Drive donde se guardan las copias generadas.
        is_default:
          type: boolean
          description: Indica si esta es la plantilla predeterminada de la organización.
        is_active:
          type: boolean
          description: Indica si la plantilla está disponible para usar en nuevas propuestas.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos internos de la plantilla (estructura libre).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - public_id
        - name
        - description
        - document_type
        - google_doc_id
        - app_script_url
        - google_drive_folder_id
        - is_default
        - is_active
        - metadata
        - created_at
        - updated_at
    ProposalTemplateListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProposalTemplateV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ProposalTemplateResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/ProposalTemplateV1"
      required:
        - data
    ProposalTemplateCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nombre de la plantilla (único por organización).
        description:
          type:
            - string
            - "null"
          description: Descripción opcional de la plantilla.
        documentType:
          type: string
          enum:
            - google_doc
            - google_slides
          description: "Tipo de documento base: `google_doc` o `google_slides`. Por defecto: `google_doc`."
        googleDocId:
          type: string
          minLength: 1
          description: ID del Google Doc/Slides usado como plantilla (único por organización).
        appScriptUrl:
          type:
            - string
            - "null"
          format: uri
          description: URL del Apps Script publicado que renderiza la plantilla, cuando aplique.
        googleDriveFolderId:
          type:
            - string
            - "null"
          description: ID de la carpeta de Google Drive donde se guardarán las copias generadas.
        isDefault:
          type: boolean
          description: Cuando es `true`, marca esta plantilla como predeterminada de la organización.
        isActive:
          type: boolean
          description: Cuando es `false`, crea la plantilla ya desactivada.
        customMetadata:
          type: object
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente para correlación interna.
      required:
        - name
        - googleDocId
    ProposalTemplateUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Nuevo nombre de la plantilla.
        description:
          type:
            - string
            - "null"
          description: Nueva descripción de la plantilla.
        documentType:
          type: string
          enum:
            - google_doc
            - google_slides
          description: "Nuevo tipo de documento base: `google_doc` o `google_slides`."
        googleDocId:
          type: string
          minLength: 1
          description: Nuevo ID del Google Doc/Slides usado como plantilla.
        appScriptUrl:
          type:
            - string
            - "null"
          format: uri
          description: Nueva URL del Apps Script publicado.
        googleDriveFolderId:
          type:
            - string
            - "null"
          description: Nuevo ID de la carpeta de Google Drive.
        isDefault:
          type: boolean
          description: Cuando es `true`, marca esta plantilla como predeterminada.
        isActive:
          type: boolean
          description: Habilita o deshabilita la plantilla.
        customMetadata:
          type: object
          additionalProperties: {}
          description: Nuevos metadatos libres definidos por el cliente.
    PlanChangeV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        subscription_id:
          type: string
          format: uuid
          description: UUID de la suscripción objetivo del cambio.
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro.
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización propietaria del recurso.
        from_plan_id:
          type: string
          format: uuid
          description: UUID del plan de origen.
        to_plan_id:
          type: string
          format: uuid
          description: UUID del plan de destino.
        change_type:
          type: string
          enum:
            - upgrade
            - downgrade
            - lateral
          description: "Tipo del cambio: `upgrade`, `downgrade` o `lateral`."
        status:
          type: string
          enum:
            - pending
            - scheduled
            - completed
            - canceled
            - failed
          description: "Estado: `pending`, `scheduled`, `completed`, `canceled` o `failed`."
        timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Cuándo se aplica/aplicó el cambio: `immediate` o `end_of_period`."
        proration_method:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo efectivamente usado.
        effective_date:
          type: string
          format: date-time
          description: Fecha en que el cambio entra en vigor.
        scheduled_date:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha agendada para aplicación (cuando `timing = end_of_period`).
        completed_at:
          type:
            - string
            - "null"
          format: date-time
          description: Cuándo se completó el cambio.
        canceled_at:
          type:
            - string
            - "null"
          format: date-time
          description: Cuándo se canceló el cambio.
        from_price_subcents:
          type: integer
          description: Precio del plan de origen en subcentavos (÷10000 → BRL).
          example: 1000000
        to_price_subcents:
          type: integer
          description: Precio del plan de destino en subcentavos (÷10000 → BRL).
          example: 2000000
        price_difference_subcents:
          type: integer
          description: Diferencia `to - from` en subcentavos (÷10000 → BRL).
        proration_credit_subcents:
          type: integer
          description: Crédito generado por prorrateo en subcentavos (÷10000 → BRL).
        proration_charge_subcents:
          type: integer
          description: Cargo adicional por prorrateo en subcentavos (÷10000 → BRL).
        days_remaining:
          type: integer
          description: Días restantes en el período actual al momento del cambio.
        total_days_in_period:
          type: integer
          description: Total de días en el período de cobro.
        invoice_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la factura de prorrateo emitida (si existe).
        credit_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del crédito generado (si existe).
        reason:
          type:
            - string
            - "null"
          description: Motivo registrado por el iniciador del cambio.
        initiated_by:
          type:
            - string
            - "null"
          description: Identificador del iniciador (email del usuario, nombre de la API key o `api`).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - subscription_id
        - billing_account_id
        - organization_id
        - from_plan_id
        - to_plan_id
        - change_type
        - status
        - timing
        - proration_method
        - effective_date
        - scheduled_date
        - completed_at
        - canceled_at
        - from_price_subcents
        - to_price_subcents
        - price_difference_subcents
        - proration_credit_subcents
        - proration_charge_subcents
        - days_remaining
        - total_days_in_period
        - invoice_id
        - credit_id
        - reason
        - initiated_by
        - created_at
        - updated_at
    PlanChangeListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PlanChangeV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    PlanChangeResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/PlanChangeV1"
      required:
        - data
    PlanChangeExecuteRequest:
      type: object
      properties:
        subscription_id:
          type: string
          format: uuid
          description: UUID de la suscripción que tendrá su plan cambiado.
        to_plan_id:
          type: string
          format: uuid
          description: UUID del plan de destino.
        timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Cuándo aplicar el cambio: `immediate` (por defecto para upgrades) o `end_of_period` (por defecto para downgrades)."
        reason:
          type: string
          description: Motivo del cambio (registrado en el audit log).
      required:
        - subscription_id
        - to_plan_id
    PlanChangeExecuteResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            plan_change_id:
              type: string
              format: uuid
              description: UUID del cambio de plan creado.
            invoice_id:
              type:
                - string
                - "null"
              format: uuid
              description: UUID de la factura de prorrateo emitida (si existe).
            credit_id:
              type:
                - string
                - "null"
              format: uuid
              description: UUID del crédito generado (si existe).
          required:
            - plan_change_id
            - invoice_id
            - credit_id
      required:
        - data
    PlanChangeStatsResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            total:
              type: integer
              description: Total de cambios en la ventana.
            by_type:
              type: object
              additionalProperties:
                type: integer
              description: "Conteo por tipo (claves: `upgrade`, `downgrade`, `lateral`)."
            by_status:
              type: object
              additionalProperties:
                type: integer
              description: Conteo por estado.
            upgrades:
              type: integer
              description: Cantidad de upgrades en la ventana.
            downgrades:
              type: integer
              description: Cantidad de downgrades en la ventana.
            total_proration_credits_subcents:
              type: integer
              description: Suma de los créditos de prorrateo emitidos en subcentavos (÷10000 → BRL).
            total_proration_charges_subcents:
              type: integer
              description: Suma de los cargos de prorrateo en subcentavos (÷10000 → BRL).
          required:
            - total
            - by_type
            - by_status
            - upgrades
            - downgrades
            - total_proration_credits_subcents
            - total_proration_charges_subcents
      required:
        - data
    PlanChangeConfigV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización propietaria de la configuración.
        allow_upgrade:
          type: boolean
          description: Los upgrades están habilitados.
        allow_downgrade:
          type: boolean
          description: Los downgrades están habilitados.
        upgrade_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto para upgrades.
        downgrade_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto para downgrades.
        upgrade_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Cuándo aplicar upgrades por defecto.
        downgrade_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Cuándo aplicar downgrades por defecto.
        refund_on_downgrade:
          type: boolean
          description: Reembolsa la diferencia al hacer downgrade.
        credit_on_downgrade:
          type: boolean
          description: Genera crédito al hacer downgrade.
        apply_discount_on_change:
          type: boolean
          description: Mantiene los descuentos activos al cambiar de plan.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - allow_upgrade
        - allow_downgrade
        - upgrade_proration
        - downgrade_proration
        - upgrade_timing
        - downgrade_timing
        - refund_on_downgrade
        - credit_on_downgrade
        - apply_discount_on_change
        - created_at
        - updated_at
    PlanChangeConfigResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/PlanChangeConfigV1"
      required:
        - data
    PlanChangeConfigUpdateRequest:
      type: object
      properties:
        allow_upgrade:
          type: boolean
          description: Habilita upgrades de plan para la organización.
        allow_downgrade:
          type: boolean
          description: Habilita downgrades de plan para la organización.
        upgrade_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: "Método de prorrateo por defecto para upgrades: `full_proration`, `no_proration` o `partial_proration`."
        downgrade_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto para downgrades.
        upgrade_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Cuándo aplicar upgrades: `immediate` o `end_of_period`."
        downgrade_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Cuándo aplicar downgrades: `immediate` o `end_of_period`."
        refund_on_downgrade:
          type: boolean
          description: Reembolsa la diferencia al hacer downgrade.
        credit_on_downgrade:
          type: boolean
          description: Genera crédito (en lugar de reembolso) al hacer downgrade.
        apply_discount_on_change:
          type: boolean
          description: Mantiene los descuentos activos al cambiar de plan.
    PlanChangeRuleV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        from_plan_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del plan de origen, o `null` para cualquiera.
        to_plan_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del plan de destino, o `null` para cualquiera.
        change_type:
          type:
            - string
            - "null"
          enum:
            - upgrade
            - downgrade
            - lateral
          description: "Tipo de cambio cubierto: `upgrade`, `downgrade` o `lateral` (o `null` para cualquiera)."
        allowed:
          type: boolean
          description: Cuando es `false`, la transición se bloquea.
        timing:
          type:
            - string
            - "null"
          enum:
            - immediate
            - end_of_period
          description: Timing sobrescrito por esta regla.
        proration_method:
          type:
            - string
            - "null"
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo sobrescrito por esta regla.
        discount_percent:
          type:
            - integer
            - "null"
          description: Descuento porcentual (0–100) aplicado.
        bonus_days:
          type:
            - integer
            - "null"
          description: Días bonus concedidos.
        message:
          type:
            - string
            - "null"
          description: Mensaje mostrado al cliente.
        is_active:
          type: boolean
          description: Si la regla está activa.
        priority:
          type: integer
          description: Prioridad para la resolución de conflictos (mayor gana).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - from_plan_id
        - to_plan_id
        - change_type
        - allowed
        - timing
        - proration_method
        - discount_percent
        - bonus_days
        - message
        - is_active
        - priority
        - created_at
        - updated_at
    PlanChangeRuleListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PlanChangeRuleV1"
      required:
        - data
    PlanChangeRuleResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/PlanChangeRuleV1"
      required:
        - data
    PlanChangeRuleCreateRequest:
      type: object
      properties:
        from_plan_id:
          type: string
          format: uuid
          description: UUID del plan de origen. Cuando se omite, la regla cubre cualquier plan de origen.
        to_plan_id:
          type: string
          format: uuid
          description: UUID del plan de destino. Cuando se omite, la regla cubre cualquier plan de destino.
        change_type:
          type: string
          enum:
            - upgrade
            - downgrade
            - lateral
          description: "Tipo de cambio: `upgrade`, `downgrade` o `lateral`."
        allowed:
          type: boolean
          description: Cuando es `false`, bloquea explícitamente la transición.
        timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Sobrescribe el timing del cambio: `immediate` o `end_of_period`."
        proration_method:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: "Sobrescribe el método de prorrateo: `full_proration`, `no_proration` o `partial_proration`."
        discount_percent:
          type: number
          minimum: 0
          maximum: 100
          description: Descuento porcentual aplicado cuando esta regla coincide (0 a 100).
        bonus_days:
          type: integer
          minimum: 0
          description: Días bonus añadidos al período actual cuando se aplica esta regla.
        message:
          type: string
          description: "Mensaje libre mostrado en el portal/checkout (ej.: motivo del bloqueo)."
        priority:
          type: integer
          description: Prioridad para la resolución de conflictos cuando múltiples reglas coinciden (mayor gana).
    PlanChangeRuleUpdateRequest:
      type: object
      properties:
        allowed:
          type: boolean
          description: Cuando es `false`, bloquea explícitamente la transición.
        timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Sobrescribe el timing del cambio: `immediate` o `end_of_period`."
        proration_method:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: "Sobrescribe el método de prorrateo: `full_proration`, `no_proration` o `partial_proration`."
        discount_percent:
          type: number
          minimum: 0
          maximum: 100
          description: Descuento porcentual aplicado cuando esta regla coincide (0 a 100).
        bonus_days:
          type: integer
          minimum: 0
          description: Días bonus añadidos al período actual cuando se aplica esta regla.
        message:
          type: string
          description: "Mensaje libre mostrado en el portal/checkout (ej.: motivo del bloqueo)."
        priority:
          type: integer
          description: Prioridad para la resolución de conflictos cuando múltiples reglas coinciden (mayor gana).
        is_active:
          type: boolean
          description: Desactiva la regla sin eliminarla.
    PlanChangeRuleDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    SubscriptionChangeConfigV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organizationId:
          type: string
          format: uuid
          description: UUID de la organización dueña de la configuración.
        allowItemAdd:
          type: boolean
          description: Si es `true`, permite agregar nuevos ítems a la suscripción.
        allowItemRemove:
          type: boolean
          description: Si es `true`, permite eliminar ítems de la suscripción.
        allowQuantityChange:
          type: boolean
          description: Si es `true`, permite cambiar la cantidad de ítems existentes.
        allowPriceSwap:
          type: boolean
          description: Si es `true`, permite cambiar el precio (variante) de un ítem sin eliminarlo.
        addProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al agregar ítems.
        removeProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al eliminar ítems.
        quantityUpProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al aumentar cantidad.
        quantityDownProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al reducir cantidad.
        addTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al agregar ítems.
        removeTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al eliminar ítems.
        quantityUpTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al aumentar cantidad.
        quantityDownTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al reducir cantidad.
        refundOnRemove:
          type: boolean
          description: Si es `true`, emite reembolso al eliminar ítems. Requiere `creditOnRemove`.
        creditOnRemove:
          type: boolean
          description: Si es `true`, emite crédito para el cliente al eliminar ítems.
        portalSelfService:
          type: boolean
          description: Si es `true`, habilita el self-service del cliente desde el portal.
        portalRequireConfirmation:
          type: boolean
          description: Si es `true`, exige confirmación explícita del cliente en el portal.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos libres asociados a la configuración.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organizationId
        - allowItemAdd
        - allowItemRemove
        - allowQuantityChange
        - allowPriceSwap
        - addProration
        - removeProration
        - quantityUpProration
        - quantityDownProration
        - addTiming
        - removeTiming
        - quantityUpTiming
        - quantityDownTiming
        - refundOnRemove
        - creditOnRemove
        - portalSelfService
        - portalRequireConfirmation
        - metadata
        - created_at
        - updated_at
    SubscriptionChangeConfigResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organizationId:
          type: string
          format: uuid
          description: UUID de la organización dueña de la configuración.
        allowItemAdd:
          type: boolean
          description: Si es `true`, permite agregar nuevos ítems a la suscripción.
        allowItemRemove:
          type: boolean
          description: Si es `true`, permite eliminar ítems de la suscripción.
        allowQuantityChange:
          type: boolean
          description: Si es `true`, permite cambiar la cantidad de ítems existentes.
        allowPriceSwap:
          type: boolean
          description: Si es `true`, permite cambiar el precio (variante) de un ítem sin eliminarlo.
        addProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al agregar ítems.
        removeProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al eliminar ítems.
        quantityUpProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al aumentar cantidad.
        quantityDownProration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo por defecto al reducir cantidad.
        addTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al agregar ítems.
        removeTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al eliminar ítems.
        quantityUpTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al aumentar cantidad.
        quantityDownTiming:
          type: string
          enum:
            - immediate
            - end_of_period
          description: Momento de aplicación por defecto al reducir cantidad.
        refundOnRemove:
          type: boolean
          description: Si es `true`, emite reembolso al eliminar ítems. Requiere `creditOnRemove`.
        creditOnRemove:
          type: boolean
          description: Si es `true`, emite crédito para el cliente al eliminar ítems.
        portalSelfService:
          type: boolean
          description: Si es `true`, habilita el self-service del cliente desde el portal.
        portalRequireConfirmation:
          type: boolean
          description: Si es `true`, exige confirmación explícita del cliente en el portal.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos libres asociados a la configuración.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organizationId
        - allowItemAdd
        - allowItemRemove
        - allowQuantityChange
        - allowPriceSwap
        - addProration
        - removeProration
        - quantityUpProration
        - quantityDownProration
        - addTiming
        - removeTiming
        - quantityUpTiming
        - quantityDownTiming
        - refundOnRemove
        - creditOnRemove
        - portalSelfService
        - portalRequireConfirmation
        - metadata
        - created_at
        - updated_at
    SubscriptionChangeConfigUpdateRequest:
      type: object
      properties:
        allow_item_add:
          type: boolean
          description: Permite agregar nuevos ítems a una suscripción existente.
        allow_item_remove:
          type: boolean
          description: Permite eliminar ítems de una suscripción existente.
        allow_quantity_change:
          type: boolean
          description: Permite cambiar la cantidad de ítems ya presentes en la suscripción.
        allow_price_swap:
          type: boolean
          description: Permite cambiar el precio (variante) de un ítem existente sin eliminarlo.
        add_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: "Método de prorrateo al agregar ítems: `full_proration`, `no_proration` o `partial_proration`."
        remove_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: "Método de prorrateo al eliminar ítems: `full_proration`, `no_proration` o `partial_proration`."
        quantity_up_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo al aumentar la cantidad de un ítem.
        quantity_down_proration:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: Método de prorrateo al reducir la cantidad de un ítem.
        add_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Momento de aplicación al agregar ítems: `immediate` o `end_of_period`."
        remove_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Momento de aplicación al eliminar ítems: `immediate` o `end_of_period`."
        quantity_up_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Momento de aplicación al aumentar cantidad: `immediate` o `end_of_period`."
        quantity_down_timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Momento de aplicación al reducir cantidad: `immediate` o `end_of_period`."
        refund_on_remove:
          type: boolean
          description: Cuando es `true`, emite un reembolso al cliente al eliminar ítems. Requiere `credit_on_remove` habilitado.
        credit_on_remove:
          type: boolean
          description: Cuando es `true`, emite un crédito a favor del cliente al eliminar ítems.
        portal_self_service:
          type: boolean
          description: Cuando es `true`, permite que el cliente cambie su propia suscripción desde el portal.
        portal_require_confirmation:
          type: boolean
          description: Cuando es `true`, exige confirmación explícita del cliente antes de aplicar el cambio en el portal.
    SubscriptionItemChangeV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        publicId:
          type: string
          format: uuid
          description: Identificador público (UUID) usado para integración externa.
        organizationId:
          type: string
          format: uuid
          description: UUID de la organización dueña del cambio.
        subscriptionId:
          type: string
          format: uuid
          description: UUID de la suscripción objetivo del cambio.
        billingAccountId:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro de la suscripción.
        status:
          type: string
          enum:
            - pending
            - scheduled
            - completed
            - canceled
            - failed
          description: "Estado: `pending`, `scheduled`, `completed`, `canceled` o `failed`."
        timing:
          type: string
          enum:
            - immediate
            - end_of_period
          description: "Momento de aplicación: `immediate` o `end_of_period`."
        initiatedByRole:
          type: string
          description: "Origen del cambio: `dashboard`, `portal` o `api`."
        initiatedBy:
          type:
            - string
            - "null"
          description: Identificador del usuario/integración que inició el cambio.
        prorationMethod:
          type: string
          enum:
            - full_proration
            - no_proration
            - partial_proration
          description: "Método de prorrateo efectivamente aplicado: `full_proration`, `no_proration` o `partial_proration`."
        prorationCreditSubcents:
          type: integer
          description: Crédito de prorrateo en subcents (÷10000 → BRL) emitido al cliente.
          example: 1000000
        prorationChargeSubcents:
          type: integer
          description: Cargo de prorrateo en subcents (÷10000 → BRL) emitido al cliente.
          example: 0
        daysRemaining:
          type: integer
          description: Días restantes del período de cobro al momento de la aplicación.
        totalDaysInPeriod:
          type: integer
          description: Total de días del período de cobro usado en el cálculo del prorrateo.
        changes:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: 'Snapshot del lote de operaciones aplicado. Cada entrada tiene la forma `{ op: "add"|"remove"|"update_qty"|"update_price", productId, priceId, quantity, prevQuantity?, prevPriceId?, prevUnitAmountSubcents?, unitAmountSubcents }`.'
        effectiveDate:
          type: string
          format: date-time
          description: Fecha efectiva del cambio (cuando entra en vigor).
        scheduledDate:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha agendada para la aplicación, cuando `timing = end_of_period`.
        completedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora en que el cambio fue efectivamente aplicado.
        canceledAt:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora en que el cambio fue cancelado, cuando corresponde.
        invoiceId:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la factura generada por el cambio, cuando exista.
        creditId:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del crédito generado por el cambio, cuando exista.
        reason:
          type:
            - string
            - "null"
          description: Motivo libre informado por el operador al registrar el cambio.
        idempotencyKey:
          type:
            - string
            - "null"
          description: Clave de idempotencia por organización — permite reejecutar con seguridad.
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - publicId
        - organizationId
        - subscriptionId
        - billingAccountId
        - status
        - timing
        - initiatedByRole
        - initiatedBy
        - prorationMethod
        - prorationCreditSubcents
        - prorationChargeSubcents
        - daysRemaining
        - totalDaysInPeriod
        - changes
        - effectiveDate
        - scheduledDate
        - completedAt
        - canceledAt
        - invoiceId
        - creditId
        - reason
        - idempotencyKey
        - metadata
        - created_at
        - updated_at
    SubscriptionItemChangeListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/SubscriptionItemChangeV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    SubscriptionItemChangeResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/SubscriptionItemChangeV1"
      required:
        - data
    UsageRecordV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        subscription_item_id:
          type: string
          format: uuid
          description: UUID del ítem de suscripción al que pertenece el uso.
        quantity:
          type: number
          description: Cantidad reportada en el registro.
          example: 5
        action:
          type: string
          enum:
            - increment
            - set
          description: "Tipo de acción: `increment` (suma) o `set` (reemplaza valores previos del período)."
        timestamp:
          type: string
          format: date-time
          description: Fecha/hora del uso (ISO 8601).
        idempotency_key:
          type:
            - string
            - "null"
          description: Clave de idempotencia usada al crear, si la hubo.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente.
        created_at:
          type: string
          format: date-time
      required:
        - id
        - subscription_item_id
        - quantity
        - action
        - timestamp
        - idempotency_key
        - metadata
        - created_at
    UsageListMeta:
      type: object
      properties:
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer
        total_quantity:
          type: number
        product_slug:
          type: string
        customer_id:
          type: string
      required:
        - total
        - limit
        - offset
        - total_quantity
    UsageListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/UsageRecordV1"
        meta:
          $ref: "#/components/schemas/UsageListMeta"
      required:
        - data
        - meta
    UsageRecordCreateInput:
      type: object
      properties:
        subscription_item_id:
          type: string
          format: uuid
          description: UUID del ítem de suscripción (opción 1 — alternativa a `product_slug` + `customer_id`).
        product_slug:
          type: string
          description: Slug del producto (opción 2). Combina con `customer_id` para ubicar la suscripción activa.
        customer_id:
          type: string
          description: Identificador externo del cliente (`external_id` del `billing_account`). Combina con `product_slug`.
        quantity:
          type: number
          minimum: 0
          description: "Cantidad a registrar — número no negativo. Ej.: `5`, `0`, `1500`."
          example: 5
        action:
          type: string
          enum:
            - increment
            - set
          description: "`increment` (por defecto) suma a la cantidad del período; `set` reemplaza cualquier valor previo del período hasta el momento."
        timestamp:
          type: string
          format: date-time
          description: "Fecha/hora del uso en ISO 8601. Por defecto: ahora."
        idempotency_key:
          type: string
          description: "Clave de idempotencia opcional. Si ya existe un registro con la misma clave para el ítem, devuelve el existente con `duplicate: true`."
        custom_metadata:
          type: object
          additionalProperties: {}
          description: usage.create.body.custom_metadata
      required:
        - quantity
    UsageRecordBatchCreateInput:
      type: object
      properties:
        records:
          type: array
          items:
            $ref: "#/components/schemas/UsageRecordCreateInput"
          minItems: 1
          maxItems: 100
          description: Lote de registros (1 a 100). Cuando está presente, procesa el lote e ignora el registro de nivel superior.
      required:
        - records
    UsageRecordCreateRequest:
      anyOf:
        - $ref: "#/components/schemas/UsageRecordCreateInput"
        - $ref: "#/components/schemas/UsageRecordBatchCreateInput"
    UsageRecordBatchResult:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: UUID del registro creado o ya existente.
        subscription_item_id:
          type: string
          format: uuid
        product_slug:
          type: string
        customer_id:
          type: string
        success:
          type: boolean
          description: Indica si el registro fue procesado con éxito.
        duplicate:
          type: boolean
          description: Cuando es `true`, indica que el registro ya existía con la misma `idempotency_key`.
        error:
          type: string
          description: Mensaje de error cuando `success` es `false`.
      required:
        - success
    UsageRecordCreateResponse:
      type: object
      properties:
        success:
          type: boolean
        processed:
          type: integer
        failed:
          type: integer
        partial:
          type: boolean
        results:
          anyOf:
            - $ref: "#/components/schemas/UsageRecordBatchResult"
            - type: array
              items:
                $ref: "#/components/schemas/UsageRecordBatchResult"
      required:
        - success
        - processed
        - results
    UsageSummaryProduct:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: UUID del producto asociado al ítem de suscripción.
        name:
          type: string
          description: Nombre del producto.
        slug:
          type: string
          description: Slug del producto.
      required:
        - id
        - name
        - slug
    UsageSummaryPeriod:
      type: object
      properties:
        start:
          type: string
          format: date-time
          description: Inicio del período usado por la agregación (ISO 8601).
        end:
          type: string
          format: date-time
          description: Fin del período usado por la agregación (ISO 8601).
      required:
        - start
        - end
    UsageSummaryUsage:
      type: object
      properties:
        total_quantity:
          type: number
          description: Cantidad total consolidada en el período.
        record_count:
          type: integer
          description: Número de registros considerados.
        first_record_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora del primer registro en el período (ISO 8601).
        last_record_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora del último registro en el período (ISO 8601).
      required:
        - total_quantity
        - record_count
        - first_record_at
        - last_record_at
    UsageSummaryBilling:
      type: object
      properties:
        estimated_cost_cents:
          type:
            - integer
            - "null"
          description: Costo estimado en centavos (÷100 → BRL). `null` cuando el precio no permite el cálculo.
        price_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del precio usado para la estimación.
        usage_type:
          type:
            - string
            - "null"
          description: "Tipo de uso configurado en el precio (ej.: `metered`, `licensed`)."
      required:
        - estimated_cost_cents
        - price_id
        - usage_type
    UsageSummaryResponse:
      type: object
      properties:
        subscription_item_id:
          type: string
          format: uuid
          description: UUID del ítem de suscripción al que pertenece el uso.
        subscription_id:
          type: string
          format: uuid
          description: UUID de la suscripción.
        subscription_status:
          type: string
          description: "Estado actual de la suscripción (ej.: `active`, `trialing`)."
        product:
          allOf:
            - $ref: "#/components/schemas/UsageSummaryProduct"
            - type:
                - object
                - "null"
        customer_id:
          type: string
        period:
          $ref: "#/components/schemas/UsageSummaryPeriod"
        usage:
          $ref: "#/components/schemas/UsageSummaryUsage"
        billing:
          $ref: "#/components/schemas/UsageSummaryBilling"
      required:
        - subscription_item_id
        - subscription_id
        - subscription_status
        - product
        - period
        - usage
        - billing
    UsageDailySnapshotV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización dueña del snapshot.
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro asociada al snapshot.
        product_group_id:
          type: string
          format: uuid
          description: UUID del grupo de productos.
        product_id:
          type: string
          format: uuid
          description: UUID del producto.
        snapshot_date:
          type: string
          format: date-time
          description: Fecha del snapshot (ISO 8601, granularidad diaria).
        quantity:
          type: number
          description: Cantidad reportada en el registro.
        idempotency_key:
          type: string
          description: Clave de idempotencia usada al crear, si la hubo.
        calculated_at:
          type: string
          format: date-time
          description: Fecha/hora en que el snapshot fue calculado (ISO 8601).
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - billing_account_id
        - product_group_id
        - product_id
        - snapshot_date
        - quantity
        - idempotency_key
        - calculated_at
        - metadata
        - created_at
        - updated_at
    UsageSnapshotListMeta:
      type: object
      properties:
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer
      required:
        - total
        - limit
        - offset
    UsageDailySnapshotListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/UsageDailySnapshotV1"
        meta:
          $ref: "#/components/schemas/UsageSnapshotListMeta"
      required:
        - data
        - meta
    UsageDailySnapshotCreateInput:
      type: object
      properties:
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro. Alternativa a `billing_account_public_id`.
        billing_account_public_id:
          type: string
          description: Identificador público de la cuenta de cobro. Alternativa a `billing_account_id`.
        product_group_slug:
          type: string
          description: Slug del grupo de productos — obligatorio. Acota la búsqueda de `product_slug`/`product_id`.
        product_slug:
          type: string
          description: Slug del producto dentro del grupo. Alternativa a `product_id`.
        product_id:
          type: string
          format: uuid
          description: UUID del producto dentro del grupo. Alternativa a `product_slug`.
        snapshot_date:
          type: string
          description: Fecha del snapshot en formato `YYYY-MM-DD` (ISO 8601).
          example: 2026-03-15
        quantity:
          type: number
          minimum: 0
          description: Cantidad consolidada del día — número no negativo.
        idempotency_key:
          type: string
          minLength: 1
          description: Clave de idempotencia **obligatoria**. Debe ser única entre snapshots.
        calculated_at:
          type: string
          format: date-time
          description: "Fecha/hora en que la cantidad fue calculada (ISO 8601). Por defecto: ahora."
        custom_metadata:
          type: object
          additionalProperties: {}
          description: usage.daily_snapshots.create.body.custom_metadata
      required:
        - product_group_slug
        - snapshot_date
        - quantity
        - idempotency_key
    UsageDailySnapshotBatchCreateInput:
      type: object
      properties:
        records:
          type: array
          items:
            $ref: "#/components/schemas/UsageDailySnapshotCreateInput"
          minItems: 1
          maxItems: 100
          description: Lote de snapshots diarios (1 a 100).
      required:
        - records
    UsageDailySnapshotCreateRequest:
      anyOf:
        - $ref: "#/components/schemas/UsageDailySnapshotCreateInput"
        - $ref: "#/components/schemas/UsageDailySnapshotBatchCreateInput"
    UsageDailySnapshotBatchResult:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: UUID del registro creado o ya existente.
        billing_account_id:
          type: string
          format: uuid
        product_id:
          type: string
          format: uuid
        success:
          type: boolean
          description: Indica si el registro fue procesado con éxito.
        overwritten:
          type: boolean
          description: Cuando es `true`, indica que un snapshot preexistente fue sobrescrito.
        error:
          type: string
          description: Mensaje de error cuando `success` es `false`.
        data:
          $ref: "#/components/schemas/UsageDailySnapshotV1"
      required:
        - success
    UsageDailySnapshotCreateResponse:
      type: object
      properties:
        success:
          type: boolean
        processed:
          type: integer
        failed:
          type: integer
        partial:
          type: boolean
        results:
          anyOf:
            - $ref: "#/components/schemas/UsageDailySnapshotBatchResult"
            - type: array
              items:
                $ref: "#/components/schemas/UsageDailySnapshotBatchResult"
      required:
        - success
        - processed
        - results
    UsageMonthlySnapshotV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización dueña del snapshot.
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro asociada al snapshot.
        product_group_id:
          type: string
          format: uuid
          description: UUID del grupo de productos.
        product_id:
          type: string
          format: uuid
          description: UUID del producto.
        month:
          type: string
          format: date-time
          description: Mes de referencia (ISO 8601 — primer día del mes).
        total_quantity:
          type: number
          description: Cantidad total acumulada del mes.
        avg_daily_quantity:
          type: number
          description: Promedio diario de uso del mes.
        days_with_usage:
          type: integer
          description: Número de días con uso registrado en el mes.
        plan_limit:
          type:
            - number
            - "null"
          description: Límite contratado del plan para el período. `null` cuando es ilimitado.
        excess_quantity:
          type: number
          description: Cantidad excedente sobre el `plan_limit`.
        idempotency_key:
          type: string
          description: Clave de idempotencia usada al crear, si la hubo.
        calculated_at:
          type: string
          format: date-time
          description: Fecha/hora en que el snapshot fue calculado (ISO 8601).
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - billing_account_id
        - product_group_id
        - product_id
        - month
        - total_quantity
        - avg_daily_quantity
        - days_with_usage
        - plan_limit
        - excess_quantity
        - idempotency_key
        - calculated_at
        - metadata
        - created_at
        - updated_at
    UsageMonthlySnapshotListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/UsageMonthlySnapshotV1"
        meta:
          $ref: "#/components/schemas/UsageSnapshotListMeta"
      required:
        - data
        - meta
    UsageMonthlySnapshotCreateInput:
      type: object
      properties:
        billing_account_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro. Alternativa a `billing_account_public_id`.
        billing_account_public_id:
          type: string
          description: Identificador público de la cuenta de cobro. Alternativa a `billing_account_id`.
        product_group_slug:
          type: string
          description: Slug del grupo de productos — obligatorio.
        product_slug:
          type: string
          description: Slug del producto dentro del grupo. Alternativa a `product_id`.
        product_id:
          type: string
          format: uuid
          description: UUID del producto dentro del grupo. Alternativa a `product_slug`.
        month:
          type: string
          description: "Mes de referencia en formato `YYYY-MM-DD` (usa el primer día del mes, ej.: `2026-03-01`)."
          example: 2026-03-01
        total_quantity:
          type: number
          minimum: 0
          description: Cantidad total acumulada del mes — número no negativo.
        avg_daily_quantity:
          type: number
          minimum: 0
          description: Promedio diario de uso del mes — número no negativo.
        days_with_usage:
          type: integer
          minimum: 0
          description: Número de días con uso registrado (entero no negativo).
        plan_limit:
          type:
            - number
            - "null"
          description: Límite contratado del plan para el período (opcional). Usa `null` cuando es ilimitado.
        excess_quantity:
          type: number
          minimum: 0
          description: Cantidad excedente sobre el `plan_limit` — número no negativo.
        idempotency_key:
          type: string
          minLength: 1
          description: Clave de idempotencia **obligatoria**. Debe ser única entre snapshots.
        calculated_at:
          type: string
          format: date-time
          description: "Fecha/hora en que los números fueron calculados (ISO 8601). Por defecto: ahora."
        custom_metadata:
          type: object
          additionalProperties: {}
          description: usage.monthly_snapshots.create.body.custom_metadata
      required:
        - product_group_slug
        - month
        - total_quantity
        - avg_daily_quantity
        - days_with_usage
        - excess_quantity
        - idempotency_key
    UsageMonthlySnapshotBatchCreateInput:
      type: object
      properties:
        records:
          type: array
          items:
            $ref: "#/components/schemas/UsageMonthlySnapshotCreateInput"
          minItems: 1
          maxItems: 100
          description: Lote de snapshots mensuales (1 a 100).
      required:
        - records
    UsageMonthlySnapshotCreateRequest:
      anyOf:
        - $ref: "#/components/schemas/UsageMonthlySnapshotCreateInput"
        - $ref: "#/components/schemas/UsageMonthlySnapshotBatchCreateInput"
    UsageMonthlySnapshotBatchResult:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: UUID del registro creado o ya existente.
        billing_account_id:
          type: string
          format: uuid
        product_id:
          type: string
          format: uuid
        success:
          type: boolean
          description: Indica si el registro fue procesado con éxito.
        overwritten:
          type: boolean
          description: Cuando es `true`, indica que un snapshot preexistente fue sobrescrito.
        error:
          type: string
          description: Mensaje de error cuando `success` es `false`.
        data:
          $ref: "#/components/schemas/UsageMonthlySnapshotV1"
      required:
        - success
    UsageMonthlySnapshotCreateResponse:
      type: object
      properties:
        success:
          type: boolean
        processed:
          type: integer
        failed:
          type: integer
        partial:
          type: boolean
        results:
          anyOf:
            - $ref: "#/components/schemas/UsageMonthlySnapshotBatchResult"
            - type: array
              items:
                $ref: "#/components/schemas/UsageMonthlySnapshotBatchResult"
      required:
        - success
        - processed
        - results
    TaxRuleItemV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tax_rule_id:
          type: string
          format: uuid
          description: UUID de la regla a la que pertenece el ítem.
        tax_type:
          type: string
          enum:
            - iss
            - pis
            - cofins
            - csll
            - irpj
            - irrf
            - csrf
            - inss
            - cbs
            - ibs
          description: "Tributo: `iss`, `pis`, `cofins`, `csll`, `irpj`, `irrf`, `csrf`, `inss`, `cbs` o `ibs`."
        rate:
          type: number
          description: "Alícuota porcentual (0–100), hasta 2 decimales. Ej.: `5.00` = 5%."
          example: 5
        is_withheld:
          type: boolean
          description: Cuando es `true`, el tributo es retenido en la fuente.
        base_type:
          type: string
          enum:
            - service_value
            - service_value_minus_iss
            - presumed_base
          description: "Base de cálculo: `service_value`, `service_value_minus_iss` o `presumed_base`."
        is_exempt:
          type: boolean
          description: Cuando es `true`, indica exención del tributo.
        exemption_reason:
          type:
            - string
            - "null"
          description: Justificación de la exención, cuando `is_exempt = true`.
        created_at:
          type: string
          format: date-time
      required:
        - id
        - tax_rule_id
        - tax_type
        - rate
        - is_withheld
        - base_type
        - is_exempt
        - exemption_reason
        - created_at
    TaxRuleV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        scope:
          type: string
          enum:
            - global
            - organization
            - company
            - service
            - customer
            - municipality
          description: "Alcance de la regla: `global`, `organization`, `company`, `service`, `customer` o `municipality`."
        priority:
          type: integer
          description: Prioridad. Mayor valor sobrescribe reglas de menor prioridad.
          example: 0
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa cuando `scope = company`.
        service_item_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del ítem de servicio cuando `scope = service`.
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la cuenta de cobro cuando `scope = customer`.
        customer_tax_type:
          type:
            - string
            - "null"
          enum:
            - pessoa_fisica
            - pessoa_juridica
            - mei
            - simples_optante
            - entidade_publica
          description: Tipo tributario del cliente que la regla cubre.
        municipality:
          type:
            - string
            - "null"
          description: Código IBGE del municipio (7 dígitos) cuando `scope = municipality`.
        operation_type:
          type:
            - string
            - "null"
          enum:
            - interno
            - externo
          description: "Tipo de operación: `interno` o `externo`."
        name:
          type: string
          description: Nombre legible de la regla.
        description:
          type:
            - string
            - "null"
          description: Descripción de la regla.
        is_active:
          type: boolean
          description: Indica si la regla está activa.
        effective_from:
          type: string
          format: date-time
          description: Fecha/hora ISO 8601 a partir de la cual la regla rige.
        effective_to:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora ISO 8601 en que la regla deja de regir. `null` significa vigencia indeterminada.
        items:
          type: array
          items:
            $ref: "#/components/schemas/TaxRuleItemV1"
          description: Lista de ítems de la regla — una entrada por tributo.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - scope
        - priority
        - company_id
        - service_item_id
        - billing_account_id
        - customer_tax_type
        - municipality
        - operation_type
        - name
        - description
        - is_active
        - effective_from
        - effective_to
        - items
        - created_at
        - updated_at
    TaxRuleListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/TaxRuleV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    TaxRuleResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/TaxRuleV1"
      required:
        - data
    TaxRuleItemInput:
      type: object
      properties:
        tax_type:
          type: string
          enum:
            - iss
            - pis
            - cofins
            - csll
            - irpj
            - irrf
            - csrf
            - inss
            - cbs
            - ibs
          description: "Tributo: `iss`, `pis`, `cofins`, `csll`, `irpj`, `irrf`, `csrf`, `inss`, `cbs` o `ibs`."
        rate:
          type: number
          minimum: 0
          maximum: 100
          description: "Alícuota porcentual (0–100), hasta 2 decimales. Ej.: `5.00` = 5%."
          example: 5
        is_withheld:
          type: boolean
          description: Cuando es `true`, el tributo es retenido en la fuente.
        base_type:
          type: string
          enum:
            - service_value
            - service_value_minus_iss
            - presumed_base
          description: "Base de cálculo: `service_value`, `service_value_minus_iss` o `presumed_base`."
        is_exempt:
          type: boolean
          description: Cuando es `true`, indica exención del tributo.
        exemption_reason:
          type:
            - string
            - "null"
          description: Justificación de la exención, cuando `is_exempt = true`.
      required:
        - tax_type
        - rate
    TaxRuleCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          description: Nombre legible de la regla.
        description:
          type:
            - string
            - "null"
          description: Descripción opcional de la regla.
        scope:
          type: string
          enum:
            - global
            - organization
            - company
            - service
            - customer
            - municipality
          description: "Alcance de la regla: `global`, `organization`, `company`, `service`, `customer` o `municipality`."
        priority:
          type: integer
          description: "Prioridad de la regla. Mayor valor sobrescribe reglas de menor prioridad. Por defecto: `0`."
          example: 0
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa cuando `scope = company`. La empresa debe pertenecer a la organización actual.
        service_item_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID del ítem de servicio cuando `scope = service`. El ítem debe pertenecer a la organización actual.
        billing_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la cuenta de cobro cuando `scope = customer`. La cuenta debe pertenecer a la organización actual.
        customer_tax_type:
          type:
            - string
            - "null"
          enum:
            - pessoa_fisica
            - pessoa_juridica
            - mei
            - simples_optante
            - entidade_publica
          description: "Tipo tributario del cliente: `pessoa_fisica`, `pessoa_juridica`, `mei`, `simples_optante` o `entidade_publica`."
        municipality:
          type:
            - string
            - "null"
          description: Código IBGE del municipio (7 dígitos) cuando `scope = municipality`.
        operation_type:
          type:
            - string
            - "null"
          enum:
            - interno
            - externo
          description: "Tipo de operación: `interno` (mismo municipio) o `externo` (municipio diferente)."
        effective_from:
          type: string
          format: date-time
          description: Fecha/hora ISO 8601 en que la regla entra en vigor.
        effective_to:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora ISO 8601 en que la regla deja de regir. `null` significa vigencia indeterminada.
        items:
          type: array
          items:
            $ref: "#/components/schemas/TaxRuleItemInput"
          minItems: 1
          description: Lista de ítems (alícuotas por tributo). Se requiere al menos 1 ítem.
      required:
        - name
        - scope
        - items
    TaxRuleUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          description: Nuevo nombre de la regla.
        description:
          type:
            - string
            - "null"
          description: Nueva descripción de la regla.
        priority:
          type: integer
          description: Nueva prioridad de la regla.
        is_active:
          type: boolean
          description: Cuando es `false`, desactiva la regla sin eliminarla.
        effective_to:
          type:
            - string
            - "null"
          format: date-time
          description: Nueva fecha/hora ISO 8601 de fin de vigencia (o `null` para indeterminada).
        items:
          type: array
          items:
            $ref: "#/components/schemas/TaxRuleItemInput"
          description: Cuando se informa, reemplaza por completo la lista de ítems de la regla.
    TaxRuleDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    TaxPeriodV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        company_id:
          type: string
          format: uuid
          description: UUID de la empresa emisora.
        period_type:
          type: string
          enum:
            - monthly
            - quarterly
          description: "Tipo del período: `monthly` (mensual) o `quarterly` (trimestral)."
        year:
          type: integer
          description: Año de competencia.
          example: 2026
        month:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 12
          description: Mes de competencia (1 a 12) — solo en períodos mensuales.
        quarter:
          type:
            - integer
            - "null"
          minimum: 1
          maximum: 4
          description: Trimestre de competencia (1 a 4) — solo en períodos trimestrales.
        status:
          type: string
          enum:
            - open
            - calculated
            - closed
          description: "Estado del período: `open`, `calculated` o `closed`."
        gross_revenue_cents:
          type: integer
          description: Ingreso bruto calculado en el período, en centavos (÷100 → BRL).
          example: 100000
        taxable_revenue_cents:
          type: integer
          description: Ingreso tributable en el período, en centavos (÷100 → BRL).
        iss_due_cents:
          type: integer
          description: ISS adeudado en el período, en centavos (÷100 → BRL).
        pis_due_cents:
          type: integer
          description: PIS adeudado en el período, en centavos (÷100 → BRL).
        cofins_due_cents:
          type: integer
          description: COFINS adeudado en el período, en centavos (÷100 → BRL).
        irpj_due_cents:
          type: integer
          description: IRPJ adeudado (estimación mensual), en centavos (÷100 → BRL).
        csll_due_cents:
          type: integer
          description: CSLL adeudado (estimación mensual), en centavos (÷100 → BRL).
        irrf_to_compensate_cents:
          type: integer
          description: IRRF retenido pendiente de compensación, en centavos (÷100 → BRL).
        csrf_to_compensate_cents:
          type: integer
          description: CSRF (PIS/COFINS/CSLL retenidos) a compensar, en centavos (÷100 → BRL).
        iss_to_compensate_cents:
          type: integer
          description: ISS retenido pendiente de compensación, en centavos (÷100 → BRL).
        das_estimated_cents:
          type:
            - integer
            - "null"
          description: DAS estimado (Simples Nacional), en centavos (÷100 → BRL).
        simples_effective_rate:
          type:
            - string
            - "null"
          description: Alícuota efectiva del Simples Nacional aplicada (porcentaje).
        simples_annex:
          type:
            - string
            - "null"
          description: "Anexo del Simples Nacional aplicable (ej.: `annex_iii`, `annex_v`)."
        cbs_due_cents:
          type: integer
          description: CBS adeudado (reforma tributaria 2026+), en centavos (÷100 → BRL).
        ibs_due_cents:
          type: integer
          description: IBS adeudado (reforma tributaria 2026+), en centavos (÷100 → BRL).
        calculation_log:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Registro JSON de la memoria de cálculo (régimen, NFes consideradas, alícuotas).
        closed_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora en que se cerró el período.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - company_id
        - period_type
        - year
        - month
        - quarter
        - status
        - gross_revenue_cents
        - taxable_revenue_cents
        - iss_due_cents
        - pis_due_cents
        - cofins_due_cents
        - irpj_due_cents
        - csll_due_cents
        - irrf_to_compensate_cents
        - csrf_to_compensate_cents
        - iss_to_compensate_cents
        - das_estimated_cents
        - simples_effective_rate
        - simples_annex
        - cbs_due_cents
        - ibs_due_cents
        - calculation_log
        - closed_at
        - created_at
        - updated_at
    TaxPeriodListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/TaxPeriodV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    TaxPeriodResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/TaxPeriodV1"
      required:
        - data
    TaxPeriodCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: UUID de la empresa emisora. Omita para usar la empresa por defecto de la organización.
        year:
          type: integer
          minimum: 2020
          maximum: 2100
          description: "Año de competencia (ej.: `2026`)."
          example: 2026
        month:
          type: integer
          minimum: 1
          maximum: 12
          description: Mes de competencia (1 a 12).
          example: 6
      required:
        - year
        - month
    TaxPeriodUpdateRequest:
      type: object
      properties:
        action:
          type: string
          enum:
            - recalculate
            - close
            - reopen
          description: "Acción a ejecutar: `recalculate`, `close` o `reopen`."
      required:
        - action
    TaxPeriodDeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            deleted:
              type: boolean
              example: true
          required:
            - deleted
      required:
        - data
    WithholdingV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización propietaria de la retención.
        invoice_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la factura asociada, cuando exista.
        nfe_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la NF-e asociada, cuando exista.
        tax_type:
          type: string
          enum:
            - iss
            - pis
            - cofins
            - csll
            - irpj
            - irrf
            - csrf
            - inss
            - cbs
            - ibs
          description: "Tipo del tributo retenido: `iss`, `pis`, `cofins`, `csll`, `irpj`, `irrf`, `csrf`, `inss`, `cbs` o `ibs`."
        base_cents:
          type: integer
          description: Base de cálculo en centavos (÷100 → BRL).
          example: 100000
        rate:
          type: number
          description: "Alícuota aplicada en porcentaje (ej.: `1.5` para 1,5%)."
          example: 1.5
        amount_cents:
          type: integer
          description: Monto retenido en centavos (÷100 → BRL).
          example: 1500
        withholder_id:
          type: string
          format: uuid
          description: UUID de la cuenta de cobro del retenedor (quien retuvo el tributo).
        status:
          type: string
          enum:
            - retained
            - compensated
            - partially_compensated
          description: "Estado de la retención: `retained`, `compensated` o `partially_compensated`."
        compensated_cents:
          type: integer
          description: Monto ya compensado/convertido en crédito, en centavos (÷100 → BRL).
          example: 0
        competence_month:
          type: integer
          minimum: 1
          maximum: 12
          description: Mes de competencia (1–12).
        competence_year:
          type: integer
          description: Año de competencia (2000–3000).
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres asociados a la retención.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - invoice_id
        - nfe_id
        - tax_type
        - base_cents
        - rate
        - amount_cents
        - withholder_id
        - status
        - compensated_cents
        - competence_month
        - competence_year
        - metadata
        - created_at
        - updated_at
    WithholdingListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WithholdingV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    WithholdingResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/WithholdingV1"
      required:
        - data
    WithholdingConvertToCreditRequest:
      type: object
      properties:
        amount_cents:
          type: integer
          exclusiveMinimum: 0
          description: "Monto a convertir en centavos (÷100 → BRL). Por defecto: saldo pendiente de la retención."
    WithholdingConvertToCreditResponse:
      type: object
      properties:
        credit:
          allOf:
            - $ref: "#/components/schemas/CreditV1"
            - description: Crédito generado por la conversión de la retención.
      required:
        - credit
    WithholdingBulkConvertRequest:
      type: object
      properties:
        tax_type:
          type: string
          enum:
            - iss
            - pis
            - cofins
            - csll
            - irpj
            - irrf
            - csrf
            - inss
            - cbs
            - ibs
          description: "Restringe la conversión a un tipo de tributo específico (ej.: `irrf`, `pis`, `cofins`)."
        competence_year:
          type: integer
          minimum: 2000
          maximum: 3000
          description: Restringe la conversión al año de competencia indicado.
        competence_month:
          type: integer
          minimum: 1
          maximum: 12
          description: Restringe la conversión al mes de competencia indicado (1–12).
    WithholdingBulkConvertResponse:
      type: object
      properties:
        credits:
          type: array
          items:
            $ref: "#/components/schemas/CreditV1"
          description: Lista de créditos generados por la conversión.
        total_converted:
          type: integer
          description: Cantidad total de retenciones convertidas en créditos.
        total_amount_cents:
          type: integer
          description: Suma de los montos convertidos en centavos (÷100 → BRL).
      required:
        - credits
        - total_converted
        - total_amount_cents
    ServiceItemV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora asociada al ítem. `null` cuando el ítem aplica a todas las empresas de la organización.
        name:
          type: string
          description: Nombre del ítem de servicio (mostrado en facturas y NFS-e).
        description:
          type:
            - string
            - "null"
          description: Descripción opcional del servicio.
        internal_code:
          type: string
          description: Código interno del ítem — inmutable después de la creación.
        lc116_code:
          type: string
          description: Código de servicio según la Ley Complementaria 116/2003 (p. ej. `01.05`).
          example: "01.05"
        municipal_code:
          type:
            - string
            - "null"
          description: Código municipal del servicio, cuando el municipio exige codificación propia.
        nbs_code:
          type:
            - string
            - "null"
          description: Código NBS — Nomenclatura Brasileña de Servicios.
        cnae_code:
          type:
            - string
            - "null"
          description: Código CNAE relacionado con el servicio prestado.
        simples_annex:
          type:
            - string
            - "null"
          enum:
            - annex_iii
            - annex_iv
            - annex_v
          description: "Anexo del Simples Nacional aplicable al servicio: `annex_iii`, `annex_iv` o `annex_v`."
        default_iss_rate:
          type:
            - string
            - "null"
          description: Alícuota predeterminada de ISS en puntos porcentuales (p. ej. `2.5` representa 2,5 %). Decimal con hasta dos posiciones.
          example: "2.50"
        subject_to_irrf:
          type: boolean
          description: Indica si el ítem está sujeto a retención de IRRF por defecto.
        subject_to_csrf:
          type: boolean
          description: Indica si el ítem está sujeto a retención de CSRF (PIS/COFINS/CSLL) por defecto.
        subject_to_inss:
          type: boolean
          description: Indica si el ítem está sujeto a retención de INSS por defecto.
        subject_to_iss_withholding:
          type: boolean
          description: Indica si el ítem está sujeto a retención de ISS por defecto.
        c_class_trib:
          type:
            - string
            - "null"
          description: Clasificación tributaria (CST IBS/CBS) aplicable al ítem.
        cbs_ibs_category:
          type:
            - string
            - "null"
          description: Categoría CBS/IBS del ítem, según la reforma tributaria.
        indop_code:
          type:
            - string
            - "null"
          description: Indicador de Operación (`indOp`) usado en la NFS-e SNNFSE.
        is_active:
          type: boolean
          description: Cuando es `false`, el ítem no puede seleccionarse en nuevas facturas o NFS-e.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos internos del ítem (uso de la plataforma).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos arbitrarios definidos por el cliente (clave/valor).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - organization_id
        - company_id
        - name
        - description
        - internal_code
        - lc116_code
        - municipal_code
        - nbs_code
        - cnae_code
        - simples_annex
        - default_iss_rate
        - subject_to_irrf
        - subject_to_csrf
        - subject_to_inss
        - subject_to_iss_withholding
        - c_class_trib
        - cbs_ibs_category
        - indop_code
        - is_active
        - metadata
        - custom_metadata
        - created_at
        - updated_at
    ServiceItemListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ServiceItemV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    ServiceItemResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/ServiceItemV1"
      required:
        - data
    ServiceItemCreateRequest:
      type: object
      properties:
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la empresa emisora asociada. Omite o envía `null` para un ítem aplicable a todas las empresas.
        name:
          type: string
          minLength: 1
          description: Nombre del ítem de servicio.
        description:
          type:
            - string
            - "null"
          description: Descripción opcional.
        internal_code:
          type: string
          minLength: 1
          description: Código interno del ítem — inmutable después de la creación.
        lc116_code:
          type: string
          minLength: 1
          description: Código LC 116/2003 del servicio (p. ej. `01.05`).
          example: "01.05"
        municipal_code:
          type:
            - string
            - "null"
          description: Código municipal del servicio, cuando corresponda.
        nbs_code:
          type:
            - string
            - "null"
          description: Código NBS.
        cnae_code:
          type:
            - string
            - "null"
          description: Código CNAE relacionado.
        simples_annex:
          type:
            - string
            - "null"
          enum:
            - annex_iii
            - annex_iv
            - annex_v
          description: "Anexo del Simples Nacional aplicable: `annex_iii`, `annex_iv` o `annex_v`."
        default_iss_rate:
          type:
            - number
            - "null"
          description: Alícuota predeterminada de ISS en puntos porcentuales (p. ej. `2.5`).
          example: 2.5
        subject_to_irrf:
          type: boolean
          description: "Indica si el ítem está sujeto a retención de IRRF. Por defecto: `true`."
        subject_to_csrf:
          type: boolean
          description: "Indica si el ítem está sujeto a retención de CSRF. Por defecto: `true`."
        subject_to_inss:
          type: boolean
          description: "Indica si el ítem está sujeto a retención de INSS. Por defecto: `false`."
        subject_to_iss_withholding:
          type: boolean
          description: "Indica si el ítem está sujeto a retención de ISS. Por defecto: `false`."
        c_class_trib:
          type:
            - string
            - "null"
          description: Clasificación tributaria CST IBS/CBS.
        cbs_ibs_category:
          type:
            - string
            - "null"
          description: Categoría CBS/IBS del ítem.
        indop_code:
          type:
            - string
            - "null"
          description: Indicador de Operación (`indOp`) de la NFS-e SNNFSE.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Metadatos arbitrarios definidos por el cliente (clave/valor).
      required:
        - name
        - internal_code
        - lc116_code
    ServiceItemUpdateRequest:
      type: object
      properties:
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: Nueva empresa emisora asociada. Envía `null` para desasociar.
        name:
          type: string
          minLength: 1
          description: Nuevo nombre del ítem.
        description:
          type:
            - string
            - "null"
          description: Nueva descripción. Envía `null` para limpiar.
        lc116_code:
          type: string
          minLength: 1
          description: Nuevo código LC 116/2003.
        municipal_code:
          type:
            - string
            - "null"
          description: Nuevo código municipal. Envía `null` para limpiar.
        nbs_code:
          type:
            - string
            - "null"
          description: Nuevo código NBS. Envía `null` para limpiar.
        cnae_code:
          type:
            - string
            - "null"
          description: Nuevo código CNAE. Envía `null` para limpiar.
        simples_annex:
          type:
            - string
            - "null"
          enum:
            - annex_iii
            - annex_iv
            - annex_v
          description: Nuevo anexo del Simples Nacional. Envía `null` para limpiar.
        default_iss_rate:
          type:
            - number
            - "null"
          description: Nueva alícuota predeterminada de ISS en puntos porcentuales. Envía `null` para limpiar.
        subject_to_irrf:
          type: boolean
          description: Actualiza la sujeción a IRRF.
        subject_to_csrf:
          type: boolean
          description: Actualiza la sujeción a CSRF.
        subject_to_inss:
          type: boolean
          description: Actualiza la sujeción a INSS.
        subject_to_iss_withholding:
          type: boolean
          description: Actualiza la sujeción a retención de ISS.
        c_class_trib:
          type:
            - string
            - "null"
          description: Nueva clasificación tributaria CST IBS/CBS. Envía `null` para limpiar.
        cbs_ibs_category:
          type:
            - string
            - "null"
          description: Nueva categoría CBS/IBS. Envía `null` para limpiar.
        indop_code:
          type:
            - string
            - "null"
          description: Nuevo Indicador de Operación (`indOp`). Envía `null` para limpiar.
        is_active:
          type: boolean
          description: Activa (`true`) o desactiva (`false`) el ítem de servicio.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Metadatos arbitrarios definidos por el cliente (clave/valor).
    DashboardUserRoleSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: UUID del rol.
        name:
          type: string
          description: Nombre del rol.
        slug:
          type: string
          description: Slug (identificador estable) del rol.
      required:
        - id
        - name
        - slug
    DashboardUserV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización a la que pertenece el usuario.
        person_id:
          type:
            - string
            - "null"
          format: uuid
          description: UUID de la persona (registro de identidad) asociada al usuario, cuando exista.
        external_id:
          type: string
          description: Identificador externo del usuario en Kobana OAuth.
        email:
          type: string
          format: email
          description: Correo del usuario.
        name:
          type:
            - string
            - "null"
          description: Nombre de visualización.
        picture:
          type:
            - string
            - "null"
          description: URL de la foto del usuario.
        status:
          type: string
          enum:
            - active
            - pending_invitation
            - suspended
          description: "Estado actual del usuario: `active`, `pending_invitation` o `suspended`."
        permissions:
          type: array
          items:
            type: string
          description: Permisos ad-hoc asignados directamente al usuario (además de los heredados de los roles).
        last_login_at:
          type:
            - string
            - "null"
          format: date-time
          description: Fecha/hora del último inicio de sesión.
        login_count:
          type: integer
          description: Cantidad total de inicios de sesión realizados.
        show_getting_started:
          type: boolean
          description: Indica si el panel "Primeros pasos" debe mostrarse al usuario.
        email_signature:
          type:
            - string
            - "null"
          description: Firma de correo configurada por el usuario.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos internos del sistema.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos libres definidos por el cliente.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        roles:
          type: array
          items:
            $ref: "#/components/schemas/DashboardUserRoleSummary"
          description: Roles asignados al usuario, expandidos como `{ id, name, slug }`.
      required:
        - id
        - organization_id
        - person_id
        - external_id
        - email
        - name
        - picture
        - status
        - permissions
        - last_login_at
        - login_count
        - show_getting_started
        - email_signature
        - metadata
        - custom_metadata
        - created_at
        - updated_at
    DashboardUserListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/DashboardUserV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    DashboardUserResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/DashboardUserV1"
      required:
        - data
    DashboardUserCreateRequest:
      type: object
      properties:
        email:
          type: string
          format: email
          description: Correo del usuario a invitar.
        name:
          type: string
          description: Nombre del usuario (opcional — el usuario puede completarlo al primer acceso).
        external_id:
          type: string
          description: Identificador externo del usuario en Kobana OAuth, cuando ya se conoce.
        role_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: Lista de UUIDs de roles (`team_roles`) a asignar al usuario. Debe contener al menos un ítem.
        permissions:
          type: array
          items:
            type: string
          description: Permisos ad-hoc adicionales (además de los heredados de los roles). Acepta los mismos slugs de permisos.
      required:
        - email
        - role_ids
    DashboardUserUpdateRequest:
      type: object
      properties:
        name:
          type: string
          description: Nuevo nombre de visualización.
        picture:
          type: string
          description: URL de la foto del usuario.
        status:
          type: string
          enum:
            - active
            - pending_invitation
            - suspended
          description: "Nuevo estado: `active`, `pending_invitation` o `suspended`."
        permissions:
          type: array
          items:
            type: string
          description: Lista completa de permisos ad-hoc (reemplaza la anterior).
        role_ids:
          type: array
          items:
            type: string
            format: uuid
          description: Lista completa de UUIDs de roles (reemplaza las asignaciones actuales).
    TeamRoleV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organization_id:
          type: string
          format: uuid
          description: UUID de la organización a la que pertenece el rol.
        name:
          type: string
          description: Nombre del rol.
        slug:
          type: string
          description: Slug estable del rol.
        description:
          type:
            - string
            - "null"
          description: Descripción del rol.
        permissions:
          type: array
          items:
            type: string
          description: Slugs de permisos concedidos por el rol.
        is_system:
          type: boolean
          description: Indica si el rol es gestionado por el sistema (no editable/removible).
        is_active:
          type: boolean
          description: Indica si el rol está activo y disponible para asignación.
        display_order:
          type: integer
          description: Orden de visualización del rol en las interfaces.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadatos internos del rol.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        users_count:
          type: integer
          description: Cantidad de usuarios actualmente vinculados al rol.
      required:
        - id
        - organization_id
        - name
        - slug
        - description
        - permissions
        - is_system
        - is_active
        - display_order
        - metadata
        - created_at
        - updated_at
    TeamRoleListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/TeamRoleV1"
        meta:
          $ref: "#/components/schemas/PaginationMeta"
      required:
        - data
        - meta
    TeamRoleResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/TeamRoleV1"
      required:
        - data
    TeamRoleCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          description: Nombre del rol (mostrado en la interfaz).
        slug:
          type: string
          pattern: ^[a-z][a-z0-9_]*$
          description: "Slug estable usado para referenciar el rol (ej.: `billing_manager`)."
          example: billing_manager
        description:
          type: string
          description: Descripción opcional de lo que concede el rol.
        permissions:
          type: array
          items:
            type: string
          description: Lista de slugs de permisos concedidos por el rol.
        is_active:
          type: boolean
          description: Indica si el rol está activo y puede ser asignado.
      required:
        - name
        - slug
        - permissions
    TeamRoleUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          description: Nuevo nombre del rol.
        slug:
          type: string
          pattern: ^[a-z][a-z0-9_]*$
          description: Nuevo slug del rol (mismas reglas que en la creación).
        description:
          type: string
          description: Nueva descripción.
        permissions:
          type: array
          items:
            type: string
          description: Lista completa de permisos (reemplaza la anterior).
        is_active:
          type: boolean
          description: Activa o desactiva el rol.
    ProposalItemCreateRequest:
      type: object
      properties:
        product_id:
          type: string
          format: uuid
          description: UUID del producto. **Obligatorio**.
        price_id:
          type: string
          format: uuid
          description: UUID del precio. **Obligatorio**.
        quantity:
          type: number
          minimum: 1
          description: "Cantidad. Por defecto: 1."
        unit_amount_subcents:
          type: number
          minimum: 0
          description: Valor unitario en subcentavos (÷10000 → BRL).
        metadata:
          type: object
          additionalProperties: {}
          description: Metadatos arbitrarios del ítem.
        custom_metadata:
          type: object
          additionalProperties: {}
          description: Campos personalizados del ítem.
      required:
        - product_id
        - price_id
        - unit_amount_subcents
  parameters: {}
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "Envía la cabecera `Authorization: Bearer <jwt>`. Formato del token: HS512."
paths:
  /v1/subscriptions:
    get:
      tags:
        - Assinaturas
      summary: Listar suscripciones
      description: Devuelve una lista paginada de las suscripciones de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - draft
              - trialing
              - active
              - past_due
              - canceled
              - paused
            description: Filtra por estado de la suscripción.
          required: false
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por plan.
          required: false
          name: plan_id
          in: query
        - schema:
            type: string
            enum:
              - monthly
              - quarterly
              - semiannual
              - annual
            description: Filtra por periodicidad de cobro.
          required: false
          name: billing_cycle
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por cuenta de cobro.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre.
          required: false
          name: search
          in: query
        - schema:
            type: string
            format: date-time
            description: Fecha inicial (ISO 8601).
          required: false
          name: date_from
          in: query
        - schema:
            type: string
            format: date-time
            description: Fecha final (ISO 8601).
          required: false
          name: date_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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de suscripciones.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-subscriptions
    post:
      tags:
        - Assinaturas
      summary: Crear suscripción
      description: Crea una suscripción. Es obligatorio enviar `plan_id` o `items`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscriptionCreateRequest"
      responses:
        "201":
          description: Suscripción creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions
      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.
          schema:
            type: string
            format: uuid
  /v1/subscriptions/{id}:
    get:
      tags:
        - Assinaturas
      summary: Obtener suscripción
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la suscripción.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-subscriptions-id
    patch:
      tags:
        - Assinaturas
      summary: Actualizar suscripción
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscriptionUpdateRequest"
      responses:
        "200":
          description: Suscripción actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-subscriptions-id
    delete:
      tags:
        - Assinaturas
      summary: Cancelar y eliminar suscripción (soft delete)
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Confirmación de soft delete.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-subscriptions-id
  /v1/subscriptions/{id}/pause:
    post:
      tags:
        - Assinaturas
      summary: Pausar suscripción
      description: Suspende el cobro manteniendo la suscripción abierta. `behavior` controla cómo se tratan las facturas generadas durante la pausa.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscriptionPauseRequest"
      responses:
        "200":
          description: Suscripción pausada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-pause
  /v1/subscriptions/{id}/resume:
    post:
      tags:
        - Assinaturas
      summary: Reanudar suscripción pausada
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Suscripción reanudada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-resume
  /v1/subscriptions/{id}/cancel:
    post:
      tags:
        - Assinaturas
      summary: Cancelar suscripción
      description: Cancela inmediatamente, o programa para el fin del período cuando `cancel_at_period_end=true`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscriptionCancelRequest"
      responses:
        "200":
          description: Suscripción cancelada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-cancel
  /v1/subscriptions/{id}/activate:
    post:
      tags:
        - Assinaturas
      summary: Activar suscripción en borrador/confirmada
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Suscripción activada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-activate
  /v1/subscriptions/{id}/change-plan:
    post:
      tags:
        - Assinaturas
      summary: Cambiar el plan de la suscripción
      description: Upgrade o downgrade. La prorrata sigue el `proration_behavior` de la suscripción.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscriptionChangePlanRequest"
      responses:
        "200":
          description: Suscripción actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-change-plan
  /v1/subscriptions/{id}/revert-to-confirmed:
    post:
      tags:
        - Assinaturas
      summary: Revertir suscripción al estado confirmado
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Suscripción revertida.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-revert-to-confirmed
  /v1/subscriptions/{id}/create-invoice:
    post:
      tags:
        - Assinaturas
      summary: Generar una factura puntual para la suscripción
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "201":
          description: Factura generada.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: {}
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-create-invoice
  /v1/subscriptions/{id}/items:
    get:
      tags:
        - Assinaturas
      summary: Listar ítems de la suscripción
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de ítems.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: {}
                required:
                  - data
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-subscriptions-id-items
  /v1/subscriptions/{id}/sync-plan-items:
    post:
      tags:
        - Assinaturas
      summary: Resincronizar ítems con el plan actual
      description: Realinea los ítems de la suscripción con la plantilla de plan-items (reconciliación manual).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la suscripción.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Suscripción sincronizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-subscriptions-id-sync-plan-items
  /v1/billing_accounts:
    get:
      tags:
        - Contas de Cobrança
      summary: Listar cuentas de cobro
      description: Devuelve una lista paginada de las cuentas de cobro de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - active
              - suspended
              - closed
            description: Filtra por estado de la cuenta de cobro.
          required: false
          name: status
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre.
          required: false
          name: search
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de cuentas de cobro.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-billing-accounts
    post:
      tags:
        - Contas de Cobrança
      summary: Crear cuenta de cobro
      description: Crea una cuenta de cobro asociada a un cliente. El bloque `customer` localiza un cliente existente por `id`, `external_id` o `document_number`; cuando no se encuentra, crea uno nuevo (requiere `name` y `document_number`).
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BillingAccountCreateRequest"
      responses:
        "201":
          description: Cuenta de cobro creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-billing-accounts
      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.
          schema:
            type: string
            format: uuid
  /v1/billing_accounts/{id}:
    get:
      tags:
        - Contas de Cobrança
      summary: Obtener cuenta de cobro
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la cuenta de cobro.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-billing-accounts-id
    patch:
      tags:
        - Contas de Cobrança
      summary: Actualizar cuenta de cobro
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BillingAccountUpdateRequest"
      responses:
        "200":
          description: Cuenta de cobro actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-billing-accounts-id
    delete:
      tags:
        - Contas de Cobrança
      summary: Cerrar cuenta de cobro
      description: Cierra la cuenta de cobro (equivalente a `POST /billing_accounts/{id}/close`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Motivo del cierre (registrado en el audit log).
          required: false
          name: reason
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Cuenta de cobro cerrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-billing-accounts-id
  /v1/billing_accounts/{id}/close:
    post:
      tags:
        - Contas de Cobrança
      summary: Cerrar cuenta de cobro (POST /close)
      description: Cierra la cuenta de cobro. El motivo (`reason`) se registra en el audit log.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BillingAccountReasonRequest"
      responses:
        "200":
          description: Cuenta de cobro cerrada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-billing-accounts-id-close
  /v1/billing_accounts/{id}/suspend:
    post:
      tags:
        - Contas de Cobrança
      summary: Suspender cuenta de cobro
      description: Suspende la cuenta. El motivo (`reason`) se registra en el audit log.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BillingAccountReasonRequest"
      responses:
        "200":
          description: Cuenta de cobro suspendida.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-billing-accounts-id-suspend
  /v1/billing_accounts/{id}/reactivate:
    post:
      tags:
        - Contas de Cobrança
      summary: Reactivar cuenta de cobro
      description: Reactiva una cuenta de cobro suspendida.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Cuenta de cobro reactivada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingAccountResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-billing-accounts-id-reactivate
  /v1/billing_accounts/{id}/invoices:
    get:
      tags:
        - Contas de Cobrança
      summary: Listar facturas de la cuenta
      description: Devuelve una lista paginada de las facturas de la cuenta de cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de facturas.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: {}
                required:
                  - data
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-billing-accounts-id-invoices
  /v1/billing_accounts/{id}/subscriptions:
    get:
      tags:
        - Contas de Cobrança
      summary: Listar suscripciones de la cuenta
      description: Devuelve una lista paginada de las suscripciones de la cuenta de cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de suscripciones.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: {}
                required:
                  - data
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-billing-accounts-id-subscriptions
  /v1/billing_accounts/{id}/credits:
    get:
      tags:
        - Contas de Cobrança
      summary: Listar créditos de la cuenta
      description: Devuelve una lista paginada de los créditos de la cuenta de cobro.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la cuenta de cobro.
          required: true
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Incluye créditos expirados cuando es `true`.
          required: false
          name: include_expired
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de créditos.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: {}
                required:
                  - data
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-billing-accounts-id-credits
  /v1/customers:
    get:
      tags:
        - Clientes
      summary: Listar clientes
      description: Devuelve una lista paginada de los clientes de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre por nombre, documento o correo.
          required: false
          name: search
          in: query
        - schema:
            type: string
            enum:
              - natural
              - juridical
            description: "Filtra por tipo de persona: `natural` (PF) o `juridical` (PJ)."
          required: false
          name: kind
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de clientes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-customers
    post:
      tags:
        - Clientes
      summary: Crear cliente
      description: Crea un cliente y, por defecto, una cuenta de cobro asociada. Usa `create_billing_account=false` para crear solo el cliente.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerCreateRequest"
      responses:
        "201":
          description: Cliente creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerCreateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-customers
      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.
          schema:
            type: string
            format: uuid
  /v1/customers/{id}:
    get:
      tags:
        - Clientes
      summary: Obtener cliente
      description: El parámetro `{id}` acepta el UUID del cliente o el `external_id`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del cliente.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-customers-id
    patch:
      tags:
        - Clientes
      summary: Actualizar cliente
      description: El parámetro `{id}` acepta el UUID del cliente o el `external_id`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerUpdateRequest"
      responses:
        "200":
          description: Cliente actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-customers-id
    delete:
      tags:
        - Clientes
      summary: Eliminar cliente (soft delete)
      description: Marca el cliente como eliminado sin borrar registros históricos.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Confirmación de soft delete.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-customers-id
  /v1/customers/{id}/statement:
    get:
      tags:
        - Clientes
      summary: Listar extracto financiero del cliente
      description: Devuelve los movimientos del extracto (facturas, pagos, aplicaciones de crédito) de la cuenta de cobro del cliente. Cuando el cliente tiene múltiples cuentas, informa `billing_account_id`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Necesario cuando el cliente tiene múltiples cuentas de cobro.
          required: false
          name: billing_account_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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de ítems del extracto.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerStatementListResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-customers-id-statement
  /v1/customers/{id}/statement/sync:
    post:
      tags:
        - Clientes
      summary: Reconstruir el extracto a partir del historial
      description: Recrea los ítems del extracto a partir de las facturas, pagos y aplicaciones de crédito existentes. Devuelve el resumen de lo creado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerStatementSyncRequest"
      responses:
        "200":
          description: Extracto reconstruido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerStatementSyncResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-customers-id-statement-sync
  /v1/customers/{id}/recalculate-mrr:
    post:
      tags:
        - Clientes
      summary: Recalcular el MRR del cliente
      description: Recalcula el MRR (Monthly Recurring Revenue) de las cuentas de cobro del cliente a partir de las suscripciones activas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: MRR recalculado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerRecalculateMrrResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-customers-id-recalculate-mrr
  /v1/customers/{id}/users:
    get:
      tags:
        - Clientes
      summary: Listar usuarios del portal del cliente
      description: Devuelve los usuarios con acceso al portal vinculados a la cuenta de cobro del cliente.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - active
              - pending_verification
              - pending_invitation
              - suspended
              - locked
            description: Filtra usuarios del portal por estado.
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de usuarios del portal.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerPortalUserListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-customers-id-users
    post:
      tags:
        - Clientes
      summary: Invitar usuario del portal
      description: Crea una invitación de acceso al portal para el correo informado. La invitación expira en 7 días.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerPortalUserInviteRequest"
      responses:
        "201":
          description: Invitación creada y correo encolado para envío.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerPortalUserResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: Ya existe un usuario o invitación para este correo.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-customers-id-users
  /v1/customers/{id}/users/invite:
    post:
      tags:
        - Clientes
      summary: Invitar usuario del portal (verbo)
      description: Equivalente a `POST /customers/{id}/users`. Disponible como ruta de verbo para SDKs que prefieren URLs descriptivas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerPortalUserInviteRequest"
      responses:
        "201":
          description: Invitación creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerPortalUserResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-customers-id-users-invite
  /v1/customers/{id}/users/{user_id}:
    get:
      tags:
        - Clientes
      summary: Obtener usuario del portal
      description: El parámetro `{user_id}` acepta el UUID o el `external_id` del usuario.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: UUID del usuario del portal o su `external_id`.
          required: true
          name: user_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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del usuario del portal.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerPortalUserResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-customers-id-users-user-id
    patch:
      tags:
        - Clientes
      summary: Actualizar usuario del portal
      description: "Actualiza `email`, `external_id`, `sso_only` y `status`. Transiciones de estado permitidas: `active` ↔ `suspended`."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: UUID del usuario del portal o su `external_id`.
          required: true
          name: user_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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerPortalUserUpdateRequest"
      responses:
        "200":
          description: Usuario actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerPortalUserResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-customers-id-users-user-id
    delete:
      tags:
        - Clientes
      summary: Eliminar usuario del portal
      description: Las invitaciones pendientes se eliminan definitivamente; los usuarios activos se suspenden.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: UUID del usuario del portal o su `external_id`.
          required: true
          name: user_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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Confirmación de eliminación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-customers-id-users-user-id
  /v1/customers/{id}/users/{user_id}/email-preview:
    get:
      tags:
        - Clientes
      summary: Previsualizar correo del usuario del portal
      description: Devuelve el asunto y el mensaje del correo de invitación o verificación que se enviaría al usuario, sin disparar el envío. Disponible solo para usuarios `pending_invitation` o `pending_verification`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: UUID del usuario del portal o su `external_id`.
          required: true
          name: user_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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Previsualización del correo.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerPortalUserEmailPreviewResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-customers-id-users-user-id-email-preview
  /v1/customers/{id}/users/{user_id}/resend:
    post:
      tags:
        - Clientes
      summary: Reenviar correo de invitación o verificación
      description: Reenvía el correo de invitación (para `pending_invitation`, con rotación de token) o de verificación (para `pending_verification`). Otros estados devuelven 400.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: UUID del cliente o `external_id`.
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: UUID del usuario del portal o su `external_id`.
          required: true
          name: user_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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Correo reenviado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerPortalUserResendResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-customers-id-users-user-id-resend
  /v1/plans:
    get:
      tags:
        - Planos
      summary: Listar planes
      description: Devuelve una lista paginada de los planes de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - draft
              - active
              - archived
            description: Filtra por estado del plan.
          required: false
          name: status
          in: query
        - schema:
            type: string
            enum:
              - standard
              - custom
              - promotional
            description: Filtra por tipo del plan.
          required: false
          name: plan_type
          in: query
        - schema:
            type: string
            enum:
              - public
              - private
            description: Filtra por visibilidad.
          required: false
          name: visibility
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por grupo de planes.
          required: false
          name: plan_group_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por empresa emisora.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra planes custom asociados a una cuenta de cobro.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            description: Búsqueda de texto libre.
          required: false
          name: search
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de planes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plans
    post:
      tags:
        - Planos
      summary: Crear plan
      description: Crea un plan para la organización autenticada.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanCreateRequest"
      responses:
        "201":
          description: Plan creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plans
      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.
          schema:
            type: string
            format: uuid
  /v1/plans/{id}:
    get:
      tags:
        - Planos
      summary: Obtener plan
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del plan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plans-id
    patch:
      tags:
        - Planos
      summary: Actualizar plan
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanUpdateRequest"
      responses:
        "200":
          description: Plan actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-plans-id
    delete:
      tags:
        - Planos
      summary: Archivar o eliminar plan
      description: Por defecto archiva el plan (soft delete). Usa `?hard=true` para eliminar permanentemente un plan en borrador.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del plan.
          required: true
          name: id
          in: path
        - schema:
            type:
              - boolean
              - "null"
            description: Cuando es `true`, elimina el plan permanentemente (solo borradores).
          required: false
          name: hard
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Confirmación de archivado o eliminación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-plans-id
  /v1/plans/{id}/duplicate:
    post:
      tags:
        - Planos
      summary: Duplicar plan
      description: Crea una copia del plan (incluyendo sus ítems) en estado de borrador.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "201":
          description: Plan duplicado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plans-id-duplicate
  /v1/plans/{id}/items:
    get:
      tags:
        - Planos
      summary: Listar ítems del plan
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de ítems del plan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanItemListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plans-id-items
    put:
      tags:
        - Planos
      summary: Reemplazar ítems del plan
      description: Reemplaza de forma atómica todos los ítems del plan. Solo se aceptan productos raíz y cada `product_id` debe ser único en la lista.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanItemsReplaceRequest"
      responses:
        "200":
          description: Ítems del plan reemplazados.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanItemsReplaceResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: put-plans-id-items
  /v1/plans/bulk-archive:
    post:
      tags:
        - Planos
      summary: Archivar planes en lote
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanBulkIdsRequest"
      responses:
        "200":
          description: Resumen del archivado en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanBulkArchiveResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plans-bulk-archive
      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.
          schema:
            type: string
            format: uuid
  /v1/plans/bulk-delete:
    post:
      tags:
        - Planos
      summary: Eliminar planes en lote
      description: Elimina permanentemente los planes informados. Los planes con suscripciones activas se omiten y se devuelven en `skipped_ids`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanBulkIdsRequest"
      responses:
        "200":
          description: Resumen de la eliminación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanBulkDeleteResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plans-bulk-delete
      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.
          schema:
            type: string
            format: uuid
  /v1/plans/bulk-move-group:
    post:
      tags:
        - Planos
      summary: Mover planes a otro grupo en lote
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanBulkMoveGroupRequest"
      responses:
        "200":
          description: Resumen del movimiento en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanBulkMoveGroupResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plans-bulk-move-group
      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.
          schema:
            type: string
            format: uuid
  /v1/plan_groups:
    get:
      tags:
        - Grupos de Plano
      summary: Listar grupos de planes
      description: Devuelve todos los grupos de planes de la organización actual ordenados por `display_order`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Búsqueda textual libre por nombre o slug.
          required: false
          name: search
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Cuando es `true`, incluye grupos archivados en la lista.
          required: false
          name: include_archived
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de grupos de planes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanGroupListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-groups
    post:
      tags:
        - Grupos de Plano
      summary: Crear grupo de planes
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanGroupCreateRequest"
      responses:
        "201":
          description: Grupo de planes creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanGroupResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plan-groups
      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.
          schema:
            type: string
            format: uuid
  /v1/plan_groups/{id}:
    get:
      tags:
        - Grupos de Plano
      summary: Obtener grupo de planes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de planes.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del grupo de planes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanGroupResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-groups-id
    patch:
      tags:
        - Grupos de Plano
      summary: Actualizar grupo de planes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de planes.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanGroupUpdateRequest"
      responses:
        "200":
          description: Grupo de planes actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanGroupResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-plan-groups-id
    delete:
      tags:
        - Grupos de Plano
      summary: Eliminar grupo de planes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de planes.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Confirmación de eliminación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-plan-groups-id
  /v1/plan_groups/reorder:
    post:
      tags:
        - Grupos de Plano
      summary: Reordenar grupos de planes
      description: Actualiza el `display_order` de los grupos según el orden del array `group_ids`. Todos los grupos deben pertenecer a la organización actual.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanGroupReorderRequest"
      responses:
        "200":
          description: Orden actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanGroupReorderResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plan-groups-reorder
      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.
          schema:
            type: string
            format: uuid
  /v1/plan_groups/{id}/archive:
    post:
      tags:
        - Grupos de Plano
      summary: Archivar grupo de planes
      description: Marca el grupo como archivado completando `archived_at`. Los grupos archivados se ocultan de los listados por defecto.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de planes.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Grupo de planes archivado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanGroupResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plan-groups-id-archive
  /v1/products:
    get:
      tags:
        - Produtos
      summary: Listar productos
      description: Devuelve una lista paginada de los productos de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - all
              - draft
              - active
              - archived
            description: Filtra por estado del producto.
          required: false
          name: status
          in: query
        - schema:
            type: string
            enum:
              - public
              - private
            description: Filtra por visibilidad.
          required: false
          name: visibility
          in: query
        - schema:
            type: string
            enum:
              - base
              - addon_quantity
              - addon_fixed
              - metered
              - one_time_fixed
              - one_time_quantity
            description: Filtra por tipo de producto.
          required: false
          name: productType
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por grupo de productos.
          required: false
          name: productGroupId
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por empresa.
          required: false
          name: companyId
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por producto padre.
          required: false
          name: parentProductId
          in: query
        - schema:
            type:
              - boolean
              - "null"
            description: Cuando es `true`, devuelve solo productos raíz (sin padre).
          required: false
          name: rootOnly
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre (nombre, slug).
          required: false
          name: search
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de productos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-products
    post:
      tags:
        - Produtos
      summary: Crear producto
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductCreateRequest"
      responses:
        "201":
          description: Producto creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products
      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.
          schema:
            type: string
            format: uuid
  /v1/products/{id}:
    get:
      tags:
        - Produtos
      summary: Obtener producto
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del producto.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del producto.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-products-id
    patch:
      tags:
        - Produtos
      summary: Actualizar producto
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del producto.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductUpdateRequest"
      responses:
        "200":
          description: Producto actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-products-id
    delete:
      tags:
        - Produtos
      summary: Archivar producto (soft delete)
      description: Archiva el producto. Devuelve 422 cuando el producto está en uso por suscripciones activas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del producto.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Confirmación de soft delete.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-products-id
  /v1/products/{id}/archive:
    post:
      tags:
        - Produtos
      summary: Archivar producto
      description: Archiva el producto. Devuelve 422 cuando el producto está en uso por suscripciones activas o ya está archivado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del producto.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Producto archivado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-id-archive
  /v1/products/{id}/publish:
    post:
      tags:
        - Produtos
      summary: Publicar producto
      description: Promueve un producto en borrador al estado activo.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del producto.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Producto publicado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-id-publish
  /v1/products/{id}/restore:
    post:
      tags:
        - Produtos
      summary: Restaurar producto archivado
      description: Restaura un producto archivado al estado activo.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del producto.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Producto restaurado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-id-restore
  /v1/products/reorder:
    post:
      tags:
        - Produtos
      summary: Reordenar productos
      description: Actualiza el orden de visualización de los productos. El índice 0 del array pasa a ser el primer ítem de la lista.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductReorderRequest"
      responses:
        "200":
          description: Orden actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductBulkResultResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-reorder
      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.
          schema:
            type: string
            format: uuid
    put:
      tags:
        - Produtos
      summary: Reordenar productos (PUT /reorder)
      description: Alias `PUT` para `/products/reorder`. Mismo comportamiento que la versión `POST`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductReorderRequest"
      responses:
        "200":
          description: Orden actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductBulkResultResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: put-products-reorder
      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.
          schema:
            type: string
            format: uuid
  /v1/products/bulk-archive:
    post:
      tags:
        - Produtos
      summary: Archivar productos en lote
      description: Archiva múltiples productos. Los IDs en uso por suscripciones activas o ya archivados se ignoran y se reportan en la respuesta.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductBulkArchiveRequest"
      responses:
        "200":
          description: Resultado de la operación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductBulkResultResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-bulk-archive
      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.
          schema:
            type: string
            format: uuid
  /v1/products/bulk-delete:
    post:
      tags:
        - Produtos
      summary: Eliminar productos en lote
      description: Elimina permanentemente múltiples productos. Los IDs en uso por suscripciones activas se ignoran y se reportan en la respuesta.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductBulkDeleteRequest"
      responses:
        "200":
          description: Resultado de la operación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductBulkResultResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-bulk-delete
      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.
          schema:
            type: string
            format: uuid
  /v1/products/bulk-visibility:
    post:
      tags:
        - Produtos
      summary: Cambiar visibilidad en lote
      description: Actualiza la visibilidad (`public` o `private`) de múltiples productos.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductBulkVisibilityRequest"
      responses:
        "200":
          description: Resultado de la operación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductBulkResultResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-bulk-visibility
      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.
          schema:
            type: string
            format: uuid
  /v1/products/bulk-move-group:
    post:
      tags:
        - Produtos
      summary: Mover productos a otro grupo
      description: "Mueve múltiples productos a un grupo de productos. Envía `product_group_id: null` para quitarlos del grupo actual."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductBulkMoveGroupRequest"
      responses:
        "200":
          description: Resultado de la operación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductBulkResultResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-bulk-move-group
      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.
          schema:
            type: string
            format: uuid
  /v1/products/bulk-change-company:
    post:
      tags:
        - Produtos
      summary: Cambiar empresa de productos en lote
      description: "Reasigna múltiples productos a otra empresa. Envía `company_id: null` para limpiar el vínculo."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductBulkChangeCompanyRequest"
      responses:
        "200":
          description: Resultado de la operación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductBulkResultResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-products-bulk-change-company
      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.
          schema:
            type: string
            format: uuid
  /v1/product_groups:
    get:
      tags:
        - Grupos de Produto
      summary: Listar grupos de productos
      description: Devuelve una lista paginada de los grupos de productos de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre por el nombre del grupo.
          required: false
          name: search
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por empresa emisora asociada.
          required: false
          name: company_id
          in: query
        - schema:
            type:
              - boolean
              - "null"
            description: "Cuando es `true`, incluye grupos archivados en el listado. Por defecto: `false`."
          required: false
          name: include_archived
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de grupos de productos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-product-groups
    post:
      tags:
        - Grupos de Produto
      summary: Crear grupo de productos
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductGroupCreateRequest"
      responses:
        "201":
          description: Grupo de productos creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-product-groups
      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.
          schema:
            type: string
            format: uuid
  /v1/product_groups/{id}:
    get:
      tags:
        - Grupos de Produto
      summary: Obtener grupo de productos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de productos.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del grupo de productos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-product-groups-id
    patch:
      tags:
        - Grupos de Produto
      summary: Actualizar grupo de productos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de productos.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductGroupUpdateRequest"
      responses:
        "200":
          description: Grupo de productos actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-product-groups-id
    delete:
      tags:
        - Grupos de Produto
      summary: Eliminar grupo de productos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de productos.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Confirmación de eliminación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-product-groups-id
  /v1/product_groups/{id}/archive:
    post:
      tags:
        - Grupos de Produto
      summary: Archivar grupo de productos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de productos.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Grupo de productos archivado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-product-groups-id-archive
    delete:
      tags:
        - Grupos de Produto
      summary: Desarchivar grupo de productos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del grupo de productos.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Grupo de productos desarchivado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-product-groups-id-archive
  /v1/product_groups/reorder:
    post:
      tags:
        - Grupos de Produto
      summary: Reordenar grupos de productos
      description: Actualiza el orden de visualización de los grupos de productos. El array `group_ids` define el nuevo orden — el índice 0 pasa a ser el primer grupo.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductGroupReorderRequest"
      responses:
        "200":
          description: Orden actualizado con éxito.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupReorderResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-product-groups-reorder
      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.
          schema:
            type: string
            format: uuid
    put:
      tags:
        - Grupos de Produto
      summary: Reordenar grupos de productos (alias PUT)
      description: Alias `PUT` para `POST /product_groups/reorder`. Mismo contrato.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProductGroupReorderRequest"
      responses:
        "200":
          description: Orden actualizado con éxito.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductGroupReorderResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: put-product-groups-reorder
      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.
          schema:
            type: string
            format: uuid
  /v1/organization:
    get:
      tags:
        - Organização
      summary: Obtener organización actual
      description: Devuelve los datos de la organización a la que pertenece el token autenticado. No hay parámetro `:id` — la organización siempre se resuelve a partir de la clave de la API.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Detalle de la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrganizationResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-organization
      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.
          schema:
            type: string
            format: uuid
    patch:
      tags:
        - Organização
      summary: Actualizar organización actual
      description: Actualiza el perfil de la organización autenticada. Solo los campos a continuación pueden modificarse — la identidad (`id`, `external_id`, `status`) no es modificable por este endpoint.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrganizationUpdateRequest"
      responses:
        "200":
          description: Organización actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrganizationResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-organization
      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.
          schema:
            type: string
            format: uuid
  /v1/companies:
    get:
      tags:
        - Empresas
      summary: Listar empresas
      description: Devuelve una lista paginada de las empresas (matrices y sucursales) de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - active
              - inactive
            description: "Filtra por estado: `active` o `inactive`."
          required: false
          name: status
          in: query
        - schema:
            type: string
            description: Búsqueda textual por nombre, razón social, correo o documento.
          required: false
          name: search
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filtra solo la empresa marcada como predeterminada de la organización.
          required: false
          name: is_default
          in: query
        - schema:
            type: string
            description: Filtra sucursales por matriz. Usa el UUID de la matriz para listar sus sucursales.
          required: false
          name: parent_company_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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de empresas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-companies
    post:
      tags:
        - Empresas
      summary: Crear empresa
      description: Crea una empresa (matriz o sucursal). Para crear una sucursal, informa `parent_company_id` — el CNPJ debe compartir el mismo prefijo que la matriz. Solo las empresas con CNPJ pueden tener sucursales. La primera empresa creada en la organización se marca como `is_default` automáticamente.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompanyCreateRequest"
      responses:
        "201":
          description: Empresa creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-companies
      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.
          schema:
            type: string
            format: uuid
  /v1/companies/matriz:
    get:
      tags:
        - Empresas
      summary: Listar matrices
      description: Lista las empresas matrices (sin `parent_company_id`) activas de la organización. Útil para seleccionar la matriz al crear una sucursal. Solo aparecen empresas con CNPJ.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Lista de empresas matrices.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyMatrizListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-companies-matriz
      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.
          schema:
            type: string
            format: uuid
  /v1/companies/{id}:
    get:
      tags:
        - Empresas
      summary: Obtener empresa
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la empresa.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-companies-id
    patch:
      tags:
        - Empresas
      summary: Actualizar empresa
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompanyUpdateRequest"
      responses:
        "200":
          description: Empresa actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-companies-id
    delete:
      tags:
        - Empresas
      summary: Desactivar empresa
      description: Desactiva la empresa (soft delete — equivalente a `POST /companies/{id}/deactivate`). No es posible desactivar la única empresa activa ni una empresa con suscripciones activas o en período de prueba.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Empresa desactivada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyDeleteResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-companies-id
  /v1/companies/{id}/set-default:
    post:
      tags:
        - Empresas
      summary: Definir empresa por defecto
      description: Marca la empresa como predeterminada de la organización. La empresa predeterminada anterior se desmarca en la misma transacción.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Empresa definida como predeterminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-companies-id-set-default
  /v1/companies/{id}/deactivate:
    post:
      tags:
        - Empresas
      summary: Desactivar empresa (POST /deactivate)
      description: Desactiva la empresa. No es posible desactivar la única empresa activa ni una empresa con suscripciones activas o en período de prueba. Si la empresa desactivada era la predeterminada, otra empresa activa se promueve automáticamente.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Empresa desactivada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-companies-id-deactivate
  /v1/companies/{id}/reactivate:
    post:
      tags:
        - Empresas
      summary: Reactivar empresa
      description: Reactiva una empresa previamente desactivada. Devuelve **422** cuando la empresa ya está activa.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Empresa reactivada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-companies-id-reactivate
  /v1/companies/{id}/fiscal:
    get:
      tags:
        - Empresas
      summary: Obtener perfil fiscal
      description: Devuelve el perfil fiscal de la empresa, o `null` cuando aún no se ha configurado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Perfil fiscal de la empresa (o `null`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyFiscalProfileResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-companies-id-fiscal
    patch:
      tags:
        - Empresas
      summary: Actualizar perfil fiscal
      description: Crea o actualiza (upsert) el perfil fiscal de la empresa. Incluye régimen tributario, alícuotas (ISS, PIS, COFINS, CSLL, IRPJ) y configuración de CBS/IBS.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompanyFiscalProfileUpsertRequest"
      responses:
        "200":
          description: Perfil fiscal actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyFiscalProfileResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-companies-id-fiscal
  /v1/companies/{id}/fiscal-profile:
    get:
      tags:
        - Empresas
      summary: Obtener perfil fiscal (alias)
      description: Alias de `GET /companies/{id}/fiscal` — misma representación y comportamiento.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Perfil fiscal de la empresa (o `null`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyFiscalProfileResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-companies-id-fiscal-profile
    patch:
      tags:
        - Empresas
      summary: Actualizar perfil fiscal (alias)
      description: Alias de `PATCH /companies/{id}/fiscal` — misma representación y comportamiento.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la empresa.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompanyFiscalProfileUpsertRequest"
      responses:
        "200":
          description: Perfil fiscal actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyFiscalProfileResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-companies-id-fiscal-profile
  /v1/certificates:
    get:
      tags:
        - Certificados
      summary: Listar certificados digitales
      description: Devuelve una lista paginada de los certificados digitales (A1) de la organización actual. Por seguridad, la respuesta **nunca** incluye la clave privada ni la contraseña del PFX — solo metadatos extraídos del certificado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - active
              - expired
              - revoked
              - expiring_soon
            description: Filtra por estado del certificado. `expiring_soon` devuelve certificados activos que vencen en los próximos 30 días.
          required: false
          name: status
          in: query
        - schema:
            type: string
            description: Búsqueda textual en `name`, `common_name`, `document` u `organization_name`.
          required: false
          name: search
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de certificados.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CertificateListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-certificates
    post:
      tags:
        - Certificados
      summary: Subir certificado digital
      description: |-
        Sube un certificado digital A1 vía `multipart/form-data`. La clave privada y la contraseña se cifran en reposo y **nunca** son devueltas por la API. El endpoint detecta duplicados por `thumbprint` y asocia automáticamente el certificado a un `person` por el CNPJ/CPF extraído.

        Valores aceptados para `type`:
        - `pfx` — enviar `file` (PFX/P12 binario) y `password`.
        - `cert_key` — enviar `cert_file` (PEM del certificado) y `key_file` (PEM de la clave).
        - `combined_pem` — enviar `file` (PEM con certificado + clave).
        - `cert_key_password` — enviar `cert_file`, `key_file` y `password` (clave protegida por contraseña).
      security:
        - bearerAuth: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/CertificateUploadRequest"
      responses:
        "201":
          description: Certificado subido y almacenado con éxito.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CertificateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-certificates
      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.
          schema:
            type: string
            format: uuid
  /v1/certificates/{id}:
    get:
      tags:
        - Certificados
      summary: Obtener certificado
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del certificado.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del certificado (sin datos sensibles).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CertificateResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-certificates-id
    delete:
      tags:
        - Certificados
      summary: Eliminar certificado
      description: Elimina el certificado mediante **soft delete**. Los datos cifrados permanecen en la base solo para fines de auditoría.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del certificado.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Certificado eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-certificates-id
  /v1/coupons:
    get:
      tags:
        - Cupons
      summary: Listar cupones
      description: Devuelve una lista paginada de los cupones de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: "Filtra por estado: `true` devuelve solo cupones activos; `false`, solo inactivos."
          required: false
          name: is_active
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre en `code` y `name`.
          required: false
          name: search
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de cupones.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-coupons
    post:
      tags:
        - Cupons
      summary: Crear cupón
      description: Crea un cupón de descuento. El `code` debe ser único por organización. Usa `discount_type=percentage` con `discount_value` entre `0` y `100`, o `discount_type=fixed_amount` con `discount_value` en centavos (÷100 → BRL).
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CouponCreateRequest"
      responses:
        "201":
          description: Cupón creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-coupons
      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.
          schema:
            type: string
            format: uuid
  /v1/coupons/{id}:
    get:
      tags:
        - Cupons
      summary: Obtener cupón
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del cupón.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del cupón.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-coupons-id
    patch:
      tags:
        - Cupons
      summary: Actualizar cupón
      description: Actualiza campos editables del cupón. `code`, `discount_type`, `discount_value` y `currency` son inmutables después de la creación.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del cupón.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CouponUpdateRequest"
      responses:
        "200":
          description: Cupón actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-coupons-id
    delete:
      tags:
        - Cupons
      summary: Eliminar cupón
      description: Elimina el cupón permanentemente. Solo se permite mientras el cupón **no tenga canjes registrados** — de lo contrario, usa `POST /coupons/{id}/deactivate` para preservar el historial.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del cupón.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Cupón eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-coupons-id
  /v1/coupons/{id}/deactivate:
    post:
      tags:
        - Cupons
      summary: Desactivar cupón
      description: "Desactiva el cupón: los nuevos canjes quedan bloqueados, pero los canjes anteriores se preservan."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del cupón.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Cupón desactivado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-coupons-id-deactivate
  /v1/coupons/{id}/redemptions:
    get:
      tags:
        - Cupons
      summary: Listar canjes del cupón
      description: Devuelve una lista paginada de los canjes registrados para el cupón.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del cupón.
          required: true
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de canjes del cupón.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CouponRedemptionListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-coupons-id-redemptions
  /v1/credits:
    get:
      tags:
        - Créditos
      summary: Listar créditos
      description: Devuelve una lista paginada de los créditos de la organización actual. Por defecto, los créditos expirados se omiten — usa `usage` o `include_expired` para incluirlos.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra créditos por cuenta de cobro.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra créditos por la `company` vinculada.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - promotional
              - adjustment
              - refund
              - manual
            description: "Filtra por tipo de crédito: `promotional`, `adjustment`, `refund` o `manual`."
          required: false
          name: type
          in: query
        - schema:
            type: string
            enum:
              - available
              - used
              - expired
            description: Filtra por estado de uso — `available` (saldo restante y no expirado), `used` (consumido por completo) o `expired` (vencido).
          required: false
          name: usage
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Incluye créditos expirados cuando es `true`. Se ignora si se informa `usage`.
          required: false
          name: include_expired
          in: query
        - schema:
            type: string
            format: date-time
            description: Límite inferior de `created_at` (ISO8601, inclusivo).
          required: false
          name: date_from
          in: query
        - schema:
            type: string
            format: date-time
            description: Límite superior de `created_at` (ISO8601, inclusivo).
          required: false
          name: date_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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de créditos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-credits
    post:
      tags:
        - Créditos
      summary: Agregar crédito
      description: Agrega un crédito a una cuenta de cobro. El monto se acredita en `balance_cents` de la `BillingAccount` y queda disponible para descontar facturas futuras. Cuando se informa `company_id`, el crédito solo puede aplicarse a facturas de la misma `company`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreditCreateRequest"
      responses:
        "201":
          description: Crédito creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-credits
      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.
          schema:
            type: string
            format: uuid
  /v1/credits/{id}:
    get:
      tags:
        - Créditos
      summary: Obtener crédito
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del crédito.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del crédito.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-credits-id
    patch:
      tags:
        - Créditos
      summary: Actualizar crédito
      description: Ajusta el crédito (tipo, monto, descripción o fecha de expiración). `amount_cents` solo puede cambiarse si el crédito no ha sido parcialmente utilizado — de lo contrario la API responde `400`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del crédito.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreditUpdateRequest"
      responses:
        "200":
          description: Crédito actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-credits-id
    delete:
      tags:
        - Créditos
      summary: Eliminar crédito
      description: Elimina el crédito y revierte el saldo correspondiente en la cuenta de cobro. Solo se permite si no se ha consumido ningún centavo del crédito — de lo contrario la API responde `400`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del crédito.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Crédito eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditDeleteResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-credits-id
  /v1/invoices:
    get:
      tags:
        - Faturas
      summary: Listar facturas
      description: Devuelve una lista paginada de las facturas de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - draft
              - open
              - paid
              - void
              - uncollectible
            description: "Filtra por estado: `draft`, `open`, `paid`, `void` o `uncollectible`."
          required: false
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `billing_account_id`.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `company_id` emisor.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `subscription_id` de origen.
          required: false
          name: subscription_id
          in: query
        - schema:
            type: string
            description: Filtra por `due_date` inicial (inclusive).
          required: false
          name: due_date_from
          in: query
        - schema:
            type: string
            description: Filtra por `due_date` final (inclusive).
          required: false
          name: due_date_to
          in: query
        - schema:
            type: string
            description: Filtra por `created_at` inicial (inclusive).
          required: false
          name: date_from
          in: query
        - schema:
            type: string
            description: Filtra por `created_at` final (inclusive).
          required: false
          name: date_to
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre.
          required: false
          name: search
          in: query
        - schema:
            type:
              - integer
              - "null"
            description: Filtra por valor total exacto (en centavos).
          required: false
          name: total_cents
          in: query
        - schema:
            type: string
            description: Filtra por CPF/CNPJ del cliente.
          required: false
          name: document_number
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Cuando es `true`, devuelve solo facturas sin pagos asociados.
          required: false
          name: without_payments
          in: query
        - schema:
            type: string
            enum:
              - all
              - with_plan
              - without_plan
            description: "Filtra por origen: `all`, `with_plan` (con suscripción) o `without_plan` (suelta)."
          required: false
          name: plan_filter
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de facturas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-invoices
    post:
      tags:
        - Faturas
      summary: Crear factura
      description: Crea una factura para una cuenta de cobro. Acepta ítems de línea, parcelas opcionales (`installments`, mínimo 2) y política de emisión de NF-e.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceCreateRequest"
      responses:
        "201":
          description: Factura creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices
      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.
          schema:
            type: string
            format: uuid
  /v1/invoices/pending-payment:
    get:
      tags:
        - Faturas
      summary: Listar facturas con pago pendiente
      description: Devuelve facturas finalizadas (`status = open`) cuya `due_date` ya pasó.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `billing_account_id`.
          required: false
          name: billing_account_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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de facturas con pago pendiente.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-invoices-pending-payment
  /v1/invoices/pending-nfe:
    get:
      tags:
        - Faturas
      summary: Listar facturas con NF-e pendiente
      description: Devuelve facturas pagadas pero con emisión de NF-e pendiente — sin NF-e, o con NF-e en estado `pending`, `processing` o `error`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `billing_account_id`.
          required: false
          name: billing_account_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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de facturas con NF-e pendiente.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-invoices-pending-nfe
  /v1/invoices/{id}:
    get:
      tags:
        - Faturas
      summary: Obtener factura
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la factura.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-invoices-id
    patch:
      tags:
        - Faturas
      summary: Actualizar factura
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceUpdateRequest"
      responses:
        "200":
          description: Factura actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-invoices-id
    delete:
      tags:
        - Faturas
      summary: Eliminar factura
      description: Elimina una factura en borrador (`draft`). Las facturas finalizadas deben cancelarse vía `POST /invoices/{id}/void`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Factura eliminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-invoices-id
  /v1/invoices/{id}/finalize:
    post:
      tags:
        - Faturas
      summary: Finalizar factura
      description: Finaliza una factura en borrador (`draft` → `open`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Factura finalizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-finalize
  /v1/invoices/{id}/void:
    post:
      tags:
        - Faturas
      summary: Cancelar (void) factura
      description: Cancela una factura abierta (`open` → `void`). El motivo (`reason`) se registra en el audit log.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceVoidRequest"
      responses:
        "200":
          description: Factura cancelada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-void
  /v1/invoices/{id}/undo-payment:
    post:
      tags:
        - Faturas
      summary: Deshacer pago de la factura
      description: Revierte el pago de la factura (`paid` → `open`). El motivo (`reason`) se registra en el audit log.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceUndoPaymentRequest"
      responses:
        "200":
          description: Pago deshecho.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-undo-payment
  /v1/invoices/{id}/pay:
    post:
      tags:
        - Faturas
      summary: Pagar factura
      description: "Dos modos: (a) cuando se informa `payment_method`, crea un pago vía gateway (`pix`, `bank_slip` o `card`) y devuelve los artefactos del método (QR code, código de barras o estado de la tarjeta); (b) cuando se omite, marca la factura como pagada manualmente (reconciliación)."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoicePayRequest"
      responses:
        "200":
          description: Resultado del pago o factura marcada como pagada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoicePayResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-pay
  /v1/invoices/{id}/send-reminder:
    post:
      tags:
        - Faturas
      summary: Enviar recordatorio de cobro
      description: Envía por correo un recordatorio de cobro de la factura al `billing_email` de la cuenta.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Recordatorio enviado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-send-reminder
  /v1/invoices/{id}/issue-nfe:
    post:
      tags:
        - Faturas
      summary: Emitir NF-e para la factura
      description: Dispara la emisión de la NF-e (Factura Electrónica) para la factura.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: NF-e en cola/emitida.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceNfeResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-issue-nfe
  /v1/invoices/{id}/apply-credits:
    post:
      tags:
        - Faturas
      summary: Aplicar créditos a la factura
      description: Aplica créditos de la cuenta de cobro en una factura abierta (`status = open`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceApplyCreditsRequest"
      responses:
        "200":
          description: Créditos aplicados.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceCreditsResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-apply-credits
  /v1/invoices/{id}/remove-credits:
    post:
      tags:
        - Faturas
      summary: Eliminar créditos aplicados
      description: Elimina todos los créditos aplicados a la factura, devolviéndolos a la cuenta de cobro. Solo funciona en facturas abiertas (`status = open`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Créditos eliminados.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceCreditsResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-remove-credits
  /v1/invoices/{id}/pdf:
    get:
      tags:
        - Faturas
      summary: Descargar PDF de la factura
      description: Genera y descarga el PDF de la factura como adjunto binario (`application/pdf`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: PDF de la factura.
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-invoices-id-pdf
  /v1/invoices/{id}/payment-methods:
    get:
      tags:
        - Faturas
      summary: Listar métodos de pago disponibles
      description: Lista los métodos de pago disponibles para pagar esta factura, según los gateways activos de la organización y la configuración del portal.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Métodos de pago disponibles.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoicePaymentMethodsResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-invoices-id-payment-methods
  /v1/invoices/{id}/installments:
    get:
      tags:
        - Faturas
      summary: Listar parcelas de la factura
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de parcelas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceInstallmentsListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-invoices-id-installments
    post:
      tags:
        - Faturas
      summary: Crear/actualizar cronograma de parcelas
      description: Crea o reemplaza el cronograma de parcelas de la factura. Mínimo de 2 parcelas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceInstallmentsCreateRequest"
      responses:
        "201":
          description: Cronograma de parcelas creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceInstallmentsListResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-installments
  /v1/invoices/{id}/installments/{installmentId}/mark-paid:
    post:
      tags:
        - Faturas
      summary: Marcar parcela como pagada
      description: Marca manualmente una parcela como pagada (reconciliación, sin llamada al gateway).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: UUID de la parcela.
          required: true
          name: installmentId
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Parcela marcada como pagada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceInstallmentResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-installments-installmentid-mark-paid
  /v1/invoices/{id}/installments/{installmentId}/pay:
    post:
      tags:
        - Faturas
      summary: Pagar parcela
      description: Procesa el pago de una parcela vía gateway. La parcela anterior debe estar pagada.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la factura.
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: UUID de la parcela.
          required: true
          name: installmentId
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceInstallmentPayRequest"
      responses:
        "200":
          description: Resultado del pago de la parcela.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoicePayResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-invoices-id-installments-installmentid-pay
  /v1/payments:
    get:
      tags:
        - Pagamentos
      summary: Listar pagos
      description: Devuelve una lista paginada de los pagos de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - pending
              - processing
              - succeeded
              - failed
              - canceled
              - partial
              - refunded
              - partially_refunded
            description: Filtra por estado del pago.
          required: false
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por los pagos de una factura específica.
          required: false
          name: invoiceId
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por los pagos de una cuenta de cobro.
          required: false
          name: billingAccountId
          in: query
        - schema:
            type: string
            description: Fecha inicial (creación) — formato ISO 8601.
          required: false
          name: dateFrom
          in: query
        - schema:
            type: string
            description: Fecha final (creación) — formato ISO 8601.
          required: false
          name: dateTo
          in: query
        - schema:
            type: string
            description: Fecha inicial de pago — formato ISO 8601.
          required: false
          name: paidDateFrom
          in: query
        - schema:
            type: string
            description: Fecha final de pago — formato ISO 8601.
          required: false
          name: paidDateTo
          in: query
        - schema:
            type: string
            enum:
              - all
              - with_invoice
              - without_invoice
            description: "Filtra por presencia de factura: `all`, `with_invoice` o `without_invoice`."
          required: false
          name: invoiceFilter
          in: query
        - schema:
            type: string
            enum:
              - card
              - bank_slip
              - pix
              - bank_transfer
            description: "Filtra por medio de pago: `card`, `bank_slip`, `pix` o `bank_transfer`."
          required: false
          name: paymentMethodType
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: Monto mínimo en centavos (÷100 → BRL).
          required: false
          name: amountMinCents
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: Monto máximo en centavos (÷100 → BRL).
          required: false
          name: amountMaxCents
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre.
          required: false
          name: search
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de pagos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-payments
    post:
      tags:
        - Pagamentos
      summary: Procesar pago
      description: Procesa un pago para una factura existente. Acepta un `payment_method_id` previamente registrado **o** un bloque `payment_method` con datos de tarjeta/PIX/boleto. Cuando se omite `amount_cents`, cobra el saldo abierto de la factura.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentCreateRequest"
      responses:
        "201":
          description: Pago creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments
      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.
          schema:
            type: string
            format: uuid
  /v1/payments/bulk-update-invoice:
    post:
      tags:
        - Pagamentos
      summary: Reasignar pagos a otra factura
      description: Actualiza en bloque la factura asociada a los pagos indicados. Útil para corregir vínculos cuando los pagos llegaron apuntando a la factura equivocada.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentBulkUpdateInvoiceRequest"
      responses:
        "200":
          description: Pagos reasignados.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentBulkUpdateInvoiceResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-bulk-update-invoice
      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.
          schema:
            type: string
            format: uuid
  /v1/payments/{id}:
    get:
      tags:
        - Pagamentos
      summary: Obtener pago
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del pago.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-payments-id
    delete:
      tags:
        - Pagamentos
      summary: Eliminar pago
      description: Elimina un pago. Los pagos ya confirmados no pueden eliminarse — use `cancel` o `refund`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Pago eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-payments-id
  /v1/payments/{id}/cancel:
    post:
      tags:
        - Pagamentos
      summary: Cancelar pago
      description: Cancela un pago abierto. Cuando `cancel_on_gateway` es `true` (por defecto), la cancelación se propaga al gateway.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentCancelRequest"
      responses:
        "200":
          description: Pago cancelado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-id-cancel
  /v1/payments/{id}/retry:
    post:
      tags:
        - Pagamentos
      summary: Reintentar pago
      description: Reprocesa un pago fallido. Opcionalmente cambia a una tarjeta nueva (vía `card`) o a un medio de pago guardado (`payment_method_id`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentRetryRequest"
      responses:
        "200":
          description: Pago reprocesado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-id-retry
  /v1/payments/{id}/change-card:
    post:
      tags:
        - Pagamentos
      summary: Cambiar tarjeta del pago
      description: Reemplaza la tarjeta de crédito de un pago fallido procesado por Pagar.me. La nueva tarjeta debe estar registrada previamente en `payment_methods`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentChangeCardRequest"
      responses:
        "200":
          description: Tarjeta cambiada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-id-change-card
  /v1/payments/{id}/change-due-date:
    post:
      tags:
        - Pagamentos
      summary: Cambiar vencimiento del boleto
      description: Cambia el vencimiento de un pago en boleto pendiente emitido vía gateway Kobana. La nueva fecha debe ser futura.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentChangeDueDateRequest"
      responses:
        "200":
          description: Vencimiento actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentChangeDueDateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-id-change-due-date
  /v1/payments/{id}/send-email:
    post:
      tags:
        - Pagamentos
      summary: Enviar boleto por correo
      description: Envía el enlace del boleto por correo al destinatario indicado. Disponible solo para pagos en boleto pendientes.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentSendEmailRequest"
      responses:
        "200":
          description: Correo enviado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentSendEmailResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-id-send-email
  /v1/payments/{id}/sync:
    post:
      tags:
        - Pagamentos
      summary: Sincronizar pago con el gateway
      description: Sincroniza el estado del pago con el gateway upstream (PIX/boleto Kobana o reembolso Pagar.me). Devuelve el pago actualizado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Pago sincronizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-id-sync
  /v1/payments/{id}/refund:
    post:
      tags:
        - Pagamentos
      summary: Reembolsar pago
      description: Reembolsa un pago exitoso. Sin `amount_cents`, reembolsa el monto total; con un monto parcial, emite un reembolso parcial.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentRefundRequest"
      responses:
        "200":
          description: Pago reembolsado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payments-id-refund
  /v1/payment_methods:
    get:
      tags:
        - Métodos de Pagamento
      summary: Listar métodos de pago
      description: "Devuelve una lista paginada de los métodos de pago de la organización actual. Filtros opcionales: `billing_account_id`, `type`, `status`."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por UUID de la cuenta de cobro.
          required: false
          name: billingAccountId
          in: query
        - schema:
            type: string
            enum:
              - card
              - bank_slip
              - pix
              - bank_transfer
            description: "Filtra por tipo: `card`, `bank_slip`, `pix` o `bank_transfer`."
          required: false
          name: type
          in: query
        - schema:
            type: string
            enum:
              - active
              - expired
              - failed
            description: "Filtra por estado: `active`, `expired` o `failed`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de métodos de pago.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentMethodListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-payment-methods
    post:
      tags:
        - Métodos de Pagamento
      summary: Crear método de pago
      description: Crea un método de pago asociado a una cuenta de cobro. Para `type=card`, envía el bloque `card` con el `cardToken` generado por la tokenización del lado del cliente. Para `type=bank_transfer`, envía el bloque `bankAccount`. Para `bank_slip` y `pix`, basta con el `type` — el gateway predeterminado se resuelve por la organización.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentMethodCreateRequest"
      responses:
        "201":
          description: Método de pago creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentMethodResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payment-methods
      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.
          schema:
            type: string
            format: uuid
  /v1/payment_methods/{id}:
    get:
      tags:
        - Métodos de Pagamento
      summary: Obtener método de pago
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del método de pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del método de pago.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentMethodResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-payment-methods-id
    patch:
      tags:
        - Métodos de Pagamento
      summary: Actualizar vencimiento de la tarjeta
      description: Actualiza solo el mes y año de vencimiento de la tarjeta asociada al método. Falla cuando `type` no es `card`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del método de pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentMethodUpdateRequest"
      responses:
        "200":
          description: Método de pago actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentMethodResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-payment-methods-id
    delete:
      tags:
        - Métodos de Pagamento
      summary: Eliminar método de pago
      description: Soft delete del método de pago (lo marca como `removed`). Falla cuando hay pagos pendientes o en procesamiento. Si era el predeterminado, otro método activo se promueve automáticamente.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del método de pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Método de pago eliminado.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                    required:
                      - deleted
                required:
                  - data
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-payment-methods-id
  /v1/payment_methods/{id}/set-default:
    post:
      tags:
        - Métodos de Pagamento
      summary: Definir método como predeterminado
      description: Marca el método como predeterminado de la cuenta de cobro y desmarca los anteriores. Falla cuando el método no está `active`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del método de pago.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Método de pago definido como predeterminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentMethodResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-payment-methods-id-set-default
  /v1/nfes:
    get:
      tags:
        - NF-e
      summary: Listar NF-e
      description: Devuelve una lista paginada de NF-e (facturas electrónicas) de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `billing_account_id`.
          required: false
          name: billingAccountId
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `company_id` emisor.
          required: false
          name: companyId
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `invoice_id` vinculado.
          required: false
          name: invoiceId
          in: query
        - schema:
            type: string
            enum:
              - draft
              - pending
              - processing
              - issued
              - pending_cancel
              - canceling
              - canceled
              - error
            description: "Filtra por estado: `draft`, `pending`, `processing`, `issued`, `pending_cancel`, `canceling`, `canceled` o `error`."
          required: false
          name: status
          in: query
        - schema:
            type: string
            enum:
              - with
              - without
            description: "Filtra por presencia de factura: `with` (solo con factura) o `without` (solo sueltas)."
          required: false
          name: invoiceFilter
          in: query
        - schema:
            type: string
            description: Filtra por `created_at` inicial (inclusive).
          required: false
          name: dateFrom
          in: query
        - schema:
            type: string
            description: Filtra por `created_at` final (inclusive).
          required: false
          name: dateTo
          in: query
        - schema:
            type: string
            description: Filtra por `issue_date` inicial (inclusive).
          required: false
          name: issueDateFrom
          in: query
        - schema:
            type: string
            description: Filtra por `issue_date` final (inclusive).
          required: false
          name: issueDateTo
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de NF-e.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes
    post:
      tags:
        - NF-e
      summary: Crear NF-e (borrador)
      description: Crea una NF-e en borrador (`draft`) para emisión posterior vía `POST /nfes/{id}/issue`. Acepta datos del tomador (`customer`) y alícuotas opcionales.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeCreateRequest"
      responses:
        "201":
          description: NF-e creada en borrador.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes
      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.
          schema:
            type: string
            format: uuid
  /v1/nfes/sync:
    post:
      tags:
        - NF-e
      summary: Sincronizar NF-e de la organización
      description: Dispara un job en background para sincronizar todas las NF-e de la organización con el proveedor (actualiza estado, descarga PDFs/XMLs pendientes). Devuelve el `job_id` para seguimiento. Rechaza si ya hay una sync activa.
      security:
        - bearerAuth: []
      responses:
        "201":
          description: Sync en cola.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeSyncOrgResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-sync
      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.
          schema:
            type: string
            format: uuid
  /v1/nfes/bulk:
    post:
      tags:
        - NF-e
      summary: Actualizar NF-e en lote
      description: Actualiza varias NF-e a la vez. Hoy soporta vincular o desvincular una factura (`invoice_id`) en hasta 100 NF-e por solicitud.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeBulkUpdateRequest"
      responses:
        "200":
          description: Cantidad de NF-e actualizadas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeBulkUpdateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-bulk
      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.
          schema:
            type: string
            format: uuid
  /v1/nfes/bulk-delete:
    post:
      tags:
        - NF-e
      summary: Eliminar NF-e en lote
      description: Elimina hasta 100 NF-e a la vez. Todas deben estar en estado `draft` y pertenecer a la organización actual.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeBulkDeleteRequest"
      responses:
        "200":
          description: Cantidad de NF-e eliminadas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeBulkDeleteResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-bulk-delete
      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.
          schema:
            type: string
            format: uuid
  /v1/nfes/request-tries:
    get:
      tags:
        - NF-e
      summary: Listar reintentos de solicitud (organización)
      description: Lista todos los intentos de solicitud al proveedor de NF-e de la organización, paginados y ordenados por fecha descendente.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de intentos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeRequestTryPaginatedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes-request-tries
  /v1/nfes/fetch-from-sefaz:
    post:
      tags:
        - NF-e
      summary: Buscar NFS-e en ADN (SEFAZ)
      description: Busca una NFS-e en el Ambiente de Dados Nacional (ADN) usando una clave de acceso de 50 dígitos y un certificado digital previamente registrado (autenticación mTLS).
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeFetchFromSefazRequest"
      responses:
        "200":
          description: Resultado de la búsqueda en el ADN.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeFetchFromSefazResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-fetch-from-sefaz
      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.
          schema:
            type: string
            format: uuid
  /v1/nfes/fetch-barueri:
    post:
      tags:
        - NF-e
      summary: Buscar NFS-e en Barueri
      description: Busca una NFS-e en el endpoint municipal de Barueri usando el `codigo_autenticidade` impreso en el DANFSE y el CPF/CNPJ del tomador. Devuelve el XML crudo y, cuando se encuentra, la NF-e ya existente en la organización.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeFetchBarueriRequest"
      responses:
        "200":
          description: Resultado de la búsqueda con XML y referencia a la NF-e existente, cuando exista.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeFetchBarueriResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-fetch-barueri
      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.
          schema:
            type: string
            format: uuid
  /v1/nfes/import-barueri:
    post:
      tags:
        - NF-e
      summary: Importar NFS-e Barueri
      description: Importa una NFS-e de Barueri en la base de datos. Acepta un XML crudo (devuelto por `POST /nfes/fetch-barueri`) o el `codigo_autenticidade` + `cnpj_tomador` para re-buscar antes de importar. Crea la `BillingAccount` del tomador cuando aún no existe.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeImportBarueriRequest"
      responses:
        "200":
          description: Resultado de la importación con flags `created`/`updated` y la NF-e persistida.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeImportBarueriResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-import-barueri
      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.
          schema:
            type: string
            format: uuid
  /v1/nfes/{id}:
    get:
      tags:
        - NF-e
      summary: Obtener NF-e
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la NF-e.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes-id
    patch:
      tags:
        - NF-e
      summary: Actualizar NF-e (borrador)
      description: Actualiza campos de la NF-e. Solo NF-e en `draft` aceptan cambios en montos y códigos fiscales.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeUpdateRequest"
      responses:
        "200":
          description: NF-e actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-nfes-id
    delete:
      tags:
        - NF-e
      summary: Eliminar NF-e
      description: Elimina una NF-e. Permitido solo para estados `draft`, `pending`, `processing`, `error` o `canceled`. NF-e `issued` deben cancelarse vía `POST /nfes/{id}/cancel`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: NF-e eliminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-nfes-id
  /v1/nfes/{id}/issue:
    post:
      tags:
        - NF-e
      summary: Emitir NF-e
      description: Emite una NF-e en borrador (`draft`) o reintenta una NF-e en `pending`/`error`. Envía la solicitud al proveedor configurado en la organización.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: NF-e en cola/emitida.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-id-issue
  /v1/nfes/{id}/cancel:
    post:
      tags:
        - NF-e
      summary: Cancelar NF-e
      description: Cancela una NF-e emitida. Encola la cancelación de forma asíncrona con el proveedor. El `reason` debe tener al menos 15 caracteres y se registra en el audit log.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NfeCancelRequest"
      responses:
        "200":
          description: Cancelación en cola.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-id-cancel
  /v1/nfes/{id}/retry-cancel:
    post:
      tags:
        - NF-e
      summary: Reintentar cancelación de la NF-e
      description: Reintenta la cancelación de una NF-e atascada en `pending_cancel`, `canceling` o `error`. Re-encola el job de cancelación.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Reintento de cancelación en cola.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-id-retry-cancel
  /v1/nfes/{id}/sync:
    post:
      tags:
        - NF-e
      summary: Sincronizar PDF/XML de la NF-e
      description: Descarga del proveedor el PDF y el XML de la NF-e y los persiste en la base de datos.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: PDF/XML sincronizados.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-id-sync
  /v1/nfes/{id}/pdf:
    get:
      tags:
        - NF-e
      summary: Descargar PDF de la NF-e
      description: Descarga el PDF de la NF-e (DANFE) como adjunto binario (`application/pdf`). Servido desde la base de datos cuando está disponible; en caso contrario, se busca en el proveedor.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: PDF de la NF-e.
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes-id-pdf
  /v1/nfes/{id}/xml:
    get:
      tags:
        - NF-e
      summary: Descargar XML de la NF-e
      description: Descarga el XML de la NF-e como adjunto binario (`application/xml`). Servido desde la base de datos cuando está disponible; en caso contrario, se busca en el proveedor.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: XML de la NF-e.
          content:
            application/xml:
              schema:
                type: string
                format: binary
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes-id-xml
  /v1/nfes/{id}/request-tries:
    get:
      tags:
        - NF-e
      summary: Listar reintentos de solicitud de la NF-e
      description: Lista los intentos (`NfeRequestTry`) realizados al proveedor para esta NF-e — una entrada por llamada, con endpoint, estado HTTP, duración y mensajes de error.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de intentos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeRequestTryListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes-id-request-tries
  /v1/nfes/{id}/requests:
    get:
      tags:
        - NF-e
      summary: Listar solicitudes externas de la NF-e
      description: Lista las solicitudes HTTP capturadas para esta NF-e (llamadas al proveedor y webhooks recibidos), con paginación. Usa `direction` para filtrar entre `outbound` e `inbound`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          name: id
          in: path
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - outbound
              - inbound
            description: "Filtra solicitudes por dirección: `outbound` (llamadas salientes) o `inbound` (webhooks recibidos)."
          required: false
          name: direction
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de solicitudes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeExternalRequestListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes-id-requests
  /v1/nfes/{id}/requests/{requestId}:
    get:
      tags:
        - NF-e
      summary: Obtener solicitud externa
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: UUID de la solicitud externa.
          required: true
          name: requestId
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la solicitud externa, incluyendo cabeceras y cuerpos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeExternalRequestResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-nfes-id-requests-requestid
  /v1/nfes/{id}/validate-barueri:
    post:
      tags:
        - NF-e
      summary: Validar NFS-e Barueri
      description: Re-busca la NFS-e en la municipalidad de Barueri y la compara con el registro local, devolviendo la lista de diferencias y el XML actualizado para reaplicación vía `POST /nfes/import-barueri`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la NF-e.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Resultado de la validación con diferencias y XML actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NfeValidateBarueriResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-nfes-id-validate-barueri
  /v1/proposals:
    get:
      tags:
        - Propostas
      summary: Listar propuestas
      description: Devuelve una lista paginada de las propuestas comerciales de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - draft
              - sent
              - viewed
              - accepted
              - rejected
              - expired
              - canceled
            description: "Filtra por estado: `draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired` o `canceled`."
          required: false
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `billing_account_id` vinculado.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `prospect_id`.
          required: false
          name: prospect_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `company_id` emisor.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por `template_id` usado para generar el PDF.
          required: false
          name: template_id
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre.
          required: false
          name: search
          in: query
        - schema:
            type: string
            description: Filtra propuestas con `expires_at` ≥ valor (ISO 8601).
          required: false
          name: expires_after
          in: query
        - schema:
            type: string
            description: Filtra propuestas con `expires_at` ≤ valor (ISO 8601).
          required: false
          name: expires_before
          in: query
        - schema:
            type: string
            description: Filtra por `created_at` inicial (inclusive).
          required: false
          name: date_from
          in: query
        - schema:
            type: string
            description: Filtra por `created_at` final (inclusive).
          required: false
          name: date_to
          in: query
        - schema:
            type: string
            description: Filtra por `accepted_at` inicial.
          required: false
          name: accepted_from
          in: query
        - schema:
            type: string
            description: Filtra por `accepted_at` final.
          required: false
          name: accepted_to
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: Filtra por `setup_amount_cents` mínimo (en centavos).
          required: false
          name: setup_amount_min
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: Filtra por `setup_amount_cents` máximo (en centavos).
          required: false
          name: setup_amount_max
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: Filtra por `monthly_amount_cents` mínimo (en centavos).
          required: false
          name: monthly_amount_min
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: Filtra por `monthly_amount_cents` máximo (en centavos).
          required: false
          name: monthly_amount_max
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de propuestas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposals
    post:
      tags:
        - Propostas
      summary: Crear propuesta
      description: Crea una propuesta comercial. Es obligatorio informar `billing_account_id`, `prospect_id` o `prospect_data` (creación inline del prospect).
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalCreateRequest"
      responses:
        "201":
          description: Propuesta creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals
      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.
          schema:
            type: string
            format: uuid
  /v1/proposals/stats:
    get:
      tags:
        - Propostas
      summary: Obtener estadísticas de propuestas
      description: Devuelve conteos de propuestas por estado (`draft`, `sent`, `viewed`, `accepted`, `rejected`, `expired`, `canceled`) para la organización actual.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Estadísticas de propuestas.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalStatsResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposals-stats
      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.
          schema:
            type: string
            format: uuid
  /v1/proposals/bulk-cancel:
    post:
      tags:
        - Propostas
      summary: Cancelar propuestas en lote
      description: Cancela varias propuestas a la vez (hasta 100). Devuelve conteos de éxito/fallo y errores por id.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalBulkRequest"
      responses:
        "200":
          description: Resultado de la cancelación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalBulkResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-bulk-cancel
      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.
          schema:
            type: string
            format: uuid
  /v1/proposals/bulk-delete:
    post:
      tags:
        - Propostas
      summary: Eliminar propuestas en lote
      description: Elimina varias propuestas a la vez (hasta 100). Devuelve conteos de éxito/fallo y errores por id.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalBulkRequest"
      responses:
        "200":
          description: Resultado de la eliminación en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalBulkResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-bulk-delete
      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.
          schema:
            type: string
            format: uuid
  /v1/proposals/bulk-send:
    post:
      tags:
        - Propostas
      summary: Enviar propuestas en lote
      description: Envía varias propuestas a la vez (hasta 100). Devuelve conteos de éxito/fallo y errores por id.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalBulkRequest"
      responses:
        "200":
          description: Resultado del envío en lote.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalBulkResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-bulk-send
      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.
          schema:
            type: string
            format: uuid
  /v1/proposals/{id}:
    get:
      tags:
        - Propostas
      summary: Obtener propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la propuesta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposals-id
    patch:
      tags:
        - Propostas
      summary: Actualizar propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalUpdateRequest"
      responses:
        "200":
          description: Propuesta actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-proposals-id
    delete:
      tags:
        - Propostas
      summary: Eliminar propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Propuesta eliminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-proposals-id
  /v1/proposals/{id}/accept:
    post:
      tags:
        - Propostas
      summary: Aceptar propuesta
      description: Acepta la propuesta y crea (o confirma) la suscripción correspondiente en el ciclo informado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalAcceptRequest"
      responses:
        "200":
          description: Propuesta aceptada — devuelve la propuesta, `subscription_id` e `invoice_id` cuando aplican.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalAcceptResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-accept
  /v1/proposals/{id}/cancel:
    post:
      tags:
        - Propostas
      summary: Cancelar propuesta
      description: Cancela la propuesta (`status → canceled`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Propuesta cancelada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-cancel
  /v1/proposals/{id}/duplicate:
    post:
      tags:
        - Propostas
      summary: Duplicar propuesta
      description: Crea una nueva propuesta a partir de la original (borrador), preservando ítems y configuración.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "201":
          description: Propuesta duplicada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-duplicate
  /v1/proposals/{id}/send:
    post:
      tags:
        - Propostas
      summary: Enviar propuesta por correo
      description: "Envía (o reenvía) la propuesta por correo al destinatario. Estados permitidos: `draft`, `sent` o `viewed`. Opcionalmente personaliza `subject` y `message` (HTML)."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalSendRequest"
      responses:
        "200":
          description: Propuesta enviada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-send
  /v1/proposals/{id}/publish:
    post:
      tags:
        - Propostas
      summary: Publicar propuesta
      description: Publica la propuesta (`draft → sent`) sin enviar el correo, dejándola disponible en el portal.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Propuesta publicada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-publish
  /v1/proposals/{id}/unpublish:
    post:
      tags:
        - Propostas
      summary: Despublicar propuesta
      description: Vuelve la propuesta a borrador (`sent`/`viewed` → `draft`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Propuesta despublicada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-unpublish
  /v1/proposals/{id}/undo-accept:
    post:
      tags:
        - Propostas
      summary: Deshacer aceptación de la propuesta
      description: "Deshace la aceptación: elimina la suscripción vinculada y vuelve el estado a `sent`."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Aceptación deshecha.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-undo-accept
  /v1/proposals/{id}/generate-pdf:
    post:
      tags:
        - Propostas
      summary: Generar PDF de la propuesta
      description: Genera (o regenera) el PDF de la propuesta vía Google App Scripts. Requiere un `template` con `app_script_url` configurado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: PDF generado — devuelve la propuesta actualizada y las URLs del documento/PDF.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalGeneratePdfResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-generate-pdf
  /v1/proposals/{id}/json:
    get:
      tags:
        - Propostas
      summary: Obtener snapshot JSON de la propuesta
      description: Devuelve el snapshot completo de los datos usados para renderizar el template del PDF (espeja lo que recibe AppScript).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Snapshot JSON de la propuesta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalJsonResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposals-id-json
  /v1/proposals/{id}/pricing:
    get:
      tags:
        - Propostas
      summary: Obtener cálculo de precio de la propuesta
      description: Devuelve el detalle de precios por ciclo de cobro (mensual, trimestral, semestral, anual), con setup y descuentos aplicados.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de precios.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalPricingResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposals-id-pricing
  /v1/proposals/{id}/items:
    get:
      tags:
        - Propostas
      summary: Listar ítems de la propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de ítems.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalItemsListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposals-id-items
    post:
      tags:
        - Propostas
      summary: Agregar ítem a la propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalItemCreateRequest"
      responses:
        "201":
          description: Ítem creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalItemResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposals-id-items
  /v1/proposals/{id}/items/{itemId}:
    get:
      tags:
        - Propostas
      summary: Obtener ítem de la propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de la propuesta.
          required: true
          name: itemId
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del ítem.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalItemResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposals-id-items-itemid
    patch:
      tags:
        - Propostas
      summary: Actualizar ítem de la propuesta
      description: Actualiza un ítem. Solo permitido en propuestas en borrador (`status = draft`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de la propuesta.
          required: true
          name: itemId
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalItemUpdateRequest"
      responses:
        "200":
          description: Ítem actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalItemResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-proposals-id-items-itemid
    delete:
      tags:
        - Propostas
      summary: Eliminar ítem de la propuesta
      description: Elimina un ítem. Solo permitido en propuestas en borrador (`status = draft`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la propuesta.
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de la propuesta.
          required: true
          name: itemId
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Ítem eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-proposals-id-items-itemid
  /v1/proposal_templates:
    get:
      tags:
        - Templates de Proposta
      summary: Listar plantillas de propuesta
      description: Devuelve una lista paginada de las plantillas de propuesta de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            description: Búsqueda textual por nombre o descripción.
          required: false
          name: search
          in: query
        - schema:
            type: boolean
            description: Filtra por plantillas activas (`true`) o desactivadas (`false`).
          required: false
          name: isActive
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de plantillas de propuesta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposal-templates
    post:
      tags:
        - Templates de Proposta
      summary: Crear plantilla de propuesta
      description: Crea una plantilla de propuesta vinculada a un Google Doc o Google Slides. El `google_doc_id` debe ser único por organización.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalTemplateCreateRequest"
      responses:
        "201":
          description: Plantilla de propuesta creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposal-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.
          schema:
            type: string
            format: uuid
  /v1/proposal_templates/{id}:
    get:
      tags:
        - Templates de Proposta
      summary: Obtener plantilla de propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la plantilla de propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la plantilla de propuesta.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-proposal-templates-id
    patch:
      tags:
        - Templates de Proposta
      summary: Actualizar plantilla de propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la plantilla de propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposalTemplateUpdateRequest"
      responses:
        "200":
          description: Plantilla de propuesta actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-proposal-templates-id
    delete:
      tags:
        - Templates de Proposta
      summary: Eliminar plantilla de propuesta
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la plantilla de propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Plantilla de propuesta eliminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-proposal-templates-id
  /v1/proposal_templates/{id}/activate:
    post:
      tags:
        - Templates de Proposta
      summary: Activar plantilla de propuesta
      description: Activa una plantilla de propuesta previamente desactivada, dejándola disponible para usar en nuevas propuestas.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la plantilla de propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Plantilla de propuesta activada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposal-templates-id-activate
  /v1/proposal_templates/{id}/deactivate:
    post:
      tags:
        - Templates de Proposta
      summary: Desactivar plantilla de propuesta
      description: Desactiva una plantilla de propuesta. Las plantillas desactivadas no pueden usarse en nuevas propuestas, pero las propuestas ya creadas permanecen sin cambios.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la plantilla de propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Plantilla de propuesta desactivada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposal-templates-id-deactivate
  /v1/proposal_templates/{id}/duplicate:
    post:
      tags:
        - Templates de Proposta
      summary: Duplicar plantilla de propuesta
      description: Crea una copia de la plantilla indicada, manteniendo la configuración y los metadatos. La nueva plantilla recibe un nombre derivado y nunca hereda la bandera `is_default`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la plantilla de propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "201":
          description: Plantilla de propuesta duplicada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposal-templates-id-duplicate
  /v1/proposal_templates/{id}/set-default:
    post:
      tags:
        - Templates de Proposta
      summary: Establecer plantilla como predeterminada
      description: Marca la plantilla indicada como predeterminada de la organización y quita la bandera de las demás. Solo una plantilla puede ser predeterminada a la vez.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la plantilla de propuesta.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Plantilla establecida como predeterminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProposalTemplateResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-proposal-templates-id-set-default
  /v1/plan_changes:
    get:
      tags:
        - Mudanças de Plano
      summary: Listar cambios de plan
      description: Devuelve el historial paginado de cambios de plan (upgrades, downgrades y laterales) de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por los cambios de una suscripción.
          required: false
          name: subscription_id
          in: query
        - schema:
            type: string
            enum:
              - upgrade
              - downgrade
              - lateral
            description: "Filtra por tipo: `upgrade`, `downgrade` o `lateral`."
          required: false
          name: change_type
          in: query
        - schema:
            type: string
            enum:
              - pending
              - scheduled
              - completed
              - canceled
              - failed
            description: "Filtra por estado: `pending`, `scheduled`, `completed`, `canceled` o `failed`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de cambios de plan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-changes
    post:
      tags:
        - Mudanças de Plano
      summary: Ejecutar cambio de plan
      description: Ejecuta un cambio de plan para una suscripción existente. `timing` controla si el cambio se aplica de inmediato (con prorrateo) o al final del período actual. En caso de éxito, devuelve los UUIDs del `plan_change`, la factura de prorrateo (si existe) y el crédito generado (si existe).
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanChangeExecuteRequest"
      responses:
        "201":
          description: Cambio de plan ejecutado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeExecuteResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plan-changes
      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.
          schema:
            type: string
            format: uuid
  /v1/plan_changes/stats:
    get:
      tags:
        - Mudanças de Plano
      summary: Estadísticas de cambios de plan
      description: Devuelve agregados de cambios de plan (totales por tipo, por estado, créditos y cargos de prorrateo) para una ventana en días.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Ventana en días para los agregados (por defecto: `30`)."
            example: 30
          required: false
          name: days
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Estadísticas agregadas de cambios de plan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeStatsResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-changes-stats
  /v1/plan_changes/config:
    get:
      tags:
        - Mudanças de Plano
      summary: Obtener configuración de cambio de plan
      description: Devuelve la configuración por defecto de la organización para upgrades y downgrades — comportamiento de prorrateo, timing y generación de crédito/refund.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Configuración actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeConfigResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-changes-config
      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.
          schema:
            type: string
            format: uuid
    patch:
      tags:
        - Mudanças de Plano
      summary: Actualizar configuración de cambio de plan
      description: Actualiza parcialmente (PATCH) la configuración de cambio de plan de la organización. Todos los campos son opcionales.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanChangeConfigUpdateRequest"
      responses:
        "200":
          description: Configuración actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeConfigResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-plan-changes-config
      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.
          schema:
            type: string
            format: uuid
  /v1/plan_changes/rules:
    get:
      tags:
        - Mudanças de Plano
      summary: Listar reglas de cambio de plan
      description: Lista las reglas personalizadas que sobrescriben el comportamiento por defecto para transiciones específicas entre planes.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Lista de reglas de cambio de plan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeRuleListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-changes-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.
          schema:
            type: string
            format: uuid
    post:
      tags:
        - Mudanças de Plano
      summary: Crear regla de cambio de plan
      description: Crea una nueva regla para una transición entre planes. Permite bloquear/permitir la transición, definir timing, prorrateo, descuentos y días bonus.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanChangeRuleCreateRequest"
      responses:
        "201":
          description: Regla creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeRuleResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-plan-changes-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.
          schema:
            type: string
            format: uuid
  /v1/plan_changes/rules/{id}:
    get:
      tags:
        - Mudanças de Plano
      summary: Obtener regla de cambio de plan
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la regla de cambio de plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la regla.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeRuleResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-changes-rules-id
    patch:
      tags:
        - Mudanças de Plano
      summary: Actualizar regla de cambio de plan
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la regla de cambio de plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanChangeRuleUpdateRequest"
      responses:
        "200":
          description: Regla actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeRuleResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-plan-changes-rules-id
    delete:
      tags:
        - Mudanças de Plano
      summary: Eliminar regla de cambio de plan
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la regla de cambio de plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Regla eliminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeRuleDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-plan-changes-rules-id
  /v1/plan_changes/{id}:
    get:
      tags:
        - Mudanças de Plano
      summary: Obtener cambio de plan
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del cambio de plan.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del cambio de plan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanChangeResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-plan-changes-id
  /v1/subscription_changes/config:
    get:
      tags:
        - Mudanças de Assinatura
      summary: Obtener configuración de cambios de suscripción
      description: Devuelve la configuración de la organización para cambios en ítems de suscripción — operaciones permitidas (agregar/eliminar/cambiar cantidad/cambiar precio), método de prorrateo por operación, momento de aplicación (`immediate` o `end_of_period`), reembolso/crédito al eliminar y self-service en el portal. Si aún no existe, se crea con los valores por defecto en la primera lectura.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: Configuración de la organización.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionChangeConfigResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-subscription-changes-config
      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.
          schema:
            type: string
            format: uuid
    put:
      tags:
        - Mudanças de Assinatura
      summary: Actualizar configuración de cambios de suscripción
      description: Actualiza parcialmente la configuración. Solo se modifican los campos enviados. `refund_on_remove` requiere que `credit_on_remove` también esté habilitado — el reembolso es una variante del crédito.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscriptionChangeConfigUpdateRequest"
      responses:
        "200":
          description: Configuración actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionChangeConfigResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: put-subscription-changes-config
      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.
          schema:
            type: string
            format: uuid
  /v1/subscription_item_changes:
    get:
      tags:
        - Mudanças de Assinatura
      summary: Listar cambios de ítems de suscripción
      description: Devuelve una lista paginada de los cambios aplicados (o agendados) sobre ítems de suscripciones de la organización — agregados, eliminaciones, cambios de cantidad y cambios de precio. Cada registro incluye el snapshot de las operaciones, los montos de prorrateo en **subcents** (÷10000 → BRL) y los punteros a la factura/credit generados.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por los cambios de una suscripción específica.
          required: false
          name: subscription_id
          in: query
        - schema:
            type: string
            enum:
              - pending
              - scheduled
              - completed
              - canceled
              - failed
            description: "Filtra por estado: `pending`, `scheduled`, `completed`, `canceled` o `failed`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de cambios de ítems.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionItemChangeListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-subscription-item-changes
  /v1/subscription_item_changes/{id}:
    get:
      tags:
        - Mudanças de Assinatura
      summary: Obtener cambio de ítem de suscripción
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del cambio de ítem.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del cambio de ítem.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionItemChangeResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-subscription-item-changes-id
  /v1/usage:
    get:
      tags:
        - Uso (Metered)
      summary: Listar registros de uso
      description: Devuelve los registros de uso (metered) de un ítem de suscripción. Identifica el ítem por `subscription_item_id` **o** por la combinación `product_slug` + `customer_id`. Admite filtros por rango de fechas y ventana `limit`/`offset`.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de suscripción. Alternativa a `product_slug` + `customer_id`.
          required: false
          name: subscription_item_id
          in: query
        - schema:
            type: string
            description: Slug del producto. Combina con `customer_id`.
          required: false
          name: product_slug
          in: query
        - schema:
            type: string
            description: Identificador externo del cliente. Combina con `product_slug`.
          required: false
          name: customer_id
          in: query
        - schema:
            type: string
            description: Fecha/hora inicial del filtro — formato ISO 8601.
          required: false
          name: start_date
          in: query
        - schema:
            type: string
            description: Fecha/hora final del filtro — formato ISO 8601.
          required: false
          name: end_date
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 1000
            description: "Ítems por página. Por defecto: `100`. Máximo: `1000`."
          required: false
          name: limit
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: "Desplazamiento (offset) para paginación. Por defecto: `0`."
          required: false
          name: offset
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de registros de uso con agregación `total_quantity` en `meta`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageListResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-usage
    post:
      tags:
        - Uso (Metered)
      summary: Reportar uso (metered)
      description: "Reporta uso para facturación metered. Acepta un registro único **o** un lote `{ records: [...] }` con hasta **100** ítems. Cada registro debe identificar el ítem de suscripción por `subscription_item_id` **o** por `product_slug` + `customer_id` (recomendado para integraciones externas). El precio/producto debe estar configurado con `usage_type=metered` y la suscripción debe estar `active` o `trialing`. Usa `idempotency_key` para evitar duplicados — cuando la clave ya fue vista, la respuesta marca `duplicate: true`. Los lotes con fallas parciales devuelven **207 Multi-Status**."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UsageRecordCreateRequest"
      responses:
        "200":
          description: Resultado del procesamiento (registro único o lote).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageRecordCreateResponse"
        "207":
          description: Resultado del procesamiento (registro único o lote).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageRecordCreateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-usage
      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.
          schema:
            type: string
            format: uuid
  /v1/usage/summary:
    get:
      tags:
        - Uso (Metered)
      summary: Resumen de uso del período
      description: Devuelve la agregación de uso de un ítem de suscripción en el período de cobro vigente (o en un período arbitrario vía `period_start`/`period_end`). Considera registros `set` (que reemplazan) y la suma de los `increment` posteriores. Cuando el precio es por tiers o unitario, calcula un costo estimado en `estimated_cost_cents` (÷100 → BRL).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de suscripción. Alternativa a `product_slug` + `customer_id`.
          required: false
          name: subscription_item_id
          in: query
        - schema:
            type: string
            description: Slug del producto. Combina con `customer_id`.
          required: false
          name: product_slug
          in: query
        - schema:
            type: string
            description: Identificador externo del cliente. Combina con `product_slug`.
          required: false
          name: customer_id
          in: query
        - schema:
            type: string
            description: "Inicio del período (ISO 8601). Por defecto: `current_period_start` de la suscripción."
          required: false
          name: period_start
          in: query
        - schema:
            type: string
            description: "Fin del período (ISO 8601). Por defecto: `current_period_end` de la suscripción."
          required: false
          name: period_end
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Resumen de uso, período y costo estimado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageSummaryResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-usage-summary
  /v1/usage/snapshots/daily:
    get:
      tags:
        - Uso (Metered)
      summary: Listar snapshots diarios de uso
      description: "Lista los snapshots diarios de uso de la organización. Filtros opcionales: `billing_account_id`, `product_id` y ventana `start_date`/`end_date`. Admite paginación por `limit`/`offset`."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filtra snapshots por UUID de la cuenta de cobro.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra snapshots por UUID del producto.
          required: false
          name: product_id
          in: query
        - schema:
            type: string
            description: Fecha/hora inicial del filtro — formato ISO 8601.
          required: false
          name: start_date
          in: query
        - schema:
            type: string
            description: Fecha/hora final del filtro — formato ISO 8601.
          required: false
          name: end_date
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 1000
            description: "Ítems por página. Por defecto: `100`. Máximo: `1000`."
          required: false
          name: limit
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: "Desplazamiento (offset) para paginación. Por defecto: `0`."
          required: false
          name: offset
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de snapshots diarios con `meta` que incluye `total`, `limit`, `offset`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageDailySnapshotListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-usage-snapshots-daily
    post:
      tags:
        - Uso (Metered)
      summary: Crear snapshots diarios de uso
      description: "Crea snapshots diarios de uso, registro único **o** lote `{ records: [...] }` con hasta **100** ítems. La clave natural (organización, cuenta de cobro, producto, fecha) es única: si ya existe, el snapshot es sobrescrito (`overwritten: true`). Resuelve la cuenta por `billing_account_id` **o** `billing_account_public_id` y el producto por `product_slug` **o** `product_id` dentro del `product_group_slug` informado. Los lotes con fallas parciales devuelven **207 Multi-Status**."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UsageDailySnapshotCreateRequest"
      responses:
        "200":
          description: Resultado del procesamiento (registro único o lote).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageDailySnapshotCreateResponse"
        "207":
          description: Resultado del procesamiento (registro único o lote).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageDailySnapshotCreateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-usage-snapshots-daily
      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.
          schema:
            type: string
            format: uuid
  /v1/usage/snapshots/monthly:
    get:
      tags:
        - Uso (Metered)
      summary: Listar snapshots mensuales de uso
      description: "Lista los snapshots mensuales de uso de la organización. Filtros opcionales: `billing_account_id`, `product_id` y ventana `start_month`/`end_month`. Admite paginación por `limit`/`offset`."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filtra snapshots por UUID de la cuenta de cobro.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra snapshots por UUID del producto.
          required: false
          name: product_id
          in: query
        - schema:
            type: string
            description: Mes inicial del filtro en formato `YYYY-MM-DD` (primer día del mes).
          required: false
          name: start_month
          in: query
        - schema:
            type: string
            description: Mes final del filtro en formato `YYYY-MM-DD` (primer día del mes).
          required: false
          name: end_month
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 1000
            description: "Ítems por página. Por defecto: `100`. Máximo: `1000`."
          required: false
          name: limit
          in: query
        - schema:
            type:
              - integer
              - "null"
            minimum: 0
            description: "Desplazamiento (offset) para paginación. Por defecto: `0`."
          required: false
          name: offset
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista de snapshots mensuales con `meta` que incluye `total`, `limit`, `offset`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageMonthlySnapshotListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-usage-snapshots-monthly
    post:
      tags:
        - Uso (Metered)
      summary: Crear snapshots mensuales de uso
      description: "Crea snapshots mensuales de uso, registro único **o** lote `{ records: [...] }` con hasta **100** ítems. La clave natural (organización, cuenta de cobro, producto, mes) es única: si ya existe, el snapshot es sobrescrito (`overwritten: true`). Resuelve la cuenta por `billing_account_id` **o** `billing_account_public_id` y el producto por `product_slug` **o** `product_id` dentro de `product_group_slug`. Los lotes con fallas parciales devuelven **207 Multi-Status**."
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UsageMonthlySnapshotCreateRequest"
      responses:
        "200":
          description: Resultado del procesamiento (registro único o lote).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageMonthlySnapshotCreateResponse"
        "207":
          description: Resultado del procesamiento (registro único o lote).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageMonthlySnapshotCreateResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-usage-snapshots-monthly
      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.
          schema:
            type: string
            format: uuid
  /v1/tax_rules:
    get:
      tags:
        - Regras Tributárias
      summary: Listar reglas tributarias
      description: Devuelve una lista paginada de las reglas tributarias de la organización actual. Usa los filtros `scope`, `company_id`, `service_item_id`, `billing_account_id` e `is_active` para acotar el resultado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - global
              - organization
              - company
              - service
              - customer
              - municipality
            description: "Filtra por alcance: `global`, `organization`, `company`, `service`, `customer` o `municipality`."
          required: false
          name: scope
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por las reglas de una empresa específica.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por las reglas de un ítem de servicio específico.
          required: false
          name: service_item_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por las reglas de una cuenta de cobro específica.
          required: false
          name: billing_account_id
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Cuando es `true`, devuelve solo reglas activas; cuando es `false`, solo inactivas.
          required: false
          name: is_active
          in: query
        - schema:
            type: string
            description: Búsqueda textual en `name` y `description`.
          required: false
          name: search
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de reglas tributarias.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxRuleListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-tax-rules
    post:
      tags:
        - Regras Tributárias
      summary: Crear regla tributaria
      description: Crea una nueva regla tributaria con sus ítems (alícuotas por tributo). El `scope` define el nivel de aplicación y la `priority` define la precedencia — las reglas de mayor prioridad sobrescriben a las menores. Es obligatorio informar al menos un ítem en `items`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaxRuleCreateRequest"
      responses:
        "201":
          description: Regla tributaria creada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxRuleResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-tax-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.
          schema:
            type: string
            format: uuid
  /v1/tax_rules/{id}:
    get:
      tags:
        - Regras Tributárias
      summary: Obtener regla tributaria
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la regla tributaria.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la regla tributaria.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxRuleResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-tax-rules-id
    patch:
      tags:
        - Regras Tributárias
      summary: Actualizar regla tributaria
      description: Actualiza los campos editables de la regla. Cuando se informa `items`, **reemplaza** por completo la lista de ítems existente.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la regla tributaria.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaxRuleUpdateRequest"
      responses:
        "200":
          description: Regla tributaria actualizada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxRuleResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-tax-rules-id
    delete:
      tags:
        - Regras Tributárias
      summary: Eliminar regla tributaria
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la regla tributaria.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Regla tributaria eliminada.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxRuleDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-tax-rules-id
  /v1/tax_periods:
    get:
      tags:
        - Períodos Tributários
      summary: Listar períodos tributarios
      description: Devuelve una lista paginada de los períodos tributarios (mensuales o trimestrales) de la organización. Permite filtrar por empresa, tipo de período, año y estado.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por empresa emisora.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - monthly
              - quarterly
            description: "Filtra por tipo de período: `monthly` o `quarterly`."
          required: false
          name: period_type
          in: query
        - schema:
            type: string
            pattern: ^\d+$
            description: "Filtra por año de competencia (ej.: `2026`)."
          required: false
          name: year
          in: query
        - schema:
            type: string
            enum:
              - open
              - calculated
              - closed
            description: "Filtra por estado: `open`, `calculated` o `closed`."
          required: false
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de períodos tributarios.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxPeriodListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-tax-periods
    post:
      tags:
        - Períodos Tributários
      summary: Procesar período tributario mensual
      description: Crea (o recupera) el período tributario mensual indicado y calcula los impuestos debidos (ISS, PIS, COFINS, IRPJ, CSLL, CBS, IBS) con base en las NFes emitidas y las retenciones a compensar. Cuando se omite `company_id`, usa la empresa por defecto de la organización.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaxPeriodCreateRequest"
      responses:
        "201":
          description: Período tributario procesado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxPeriodResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-tax-periods
      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.
          schema:
            type: string
            format: uuid
  /v1/tax_periods/{id}:
    get:
      tags:
        - Períodos Tributários
      summary: Obtener período tributario
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del período tributario.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del período tributario.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxPeriodResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-tax-periods-id
    patch:
      tags:
        - Períodos Tributários
      summary: Ejecutar acción en el período tributario
      description: "Aplica una acción sobre el período: `recalculate` recalcula los impuestos (solo períodos mensuales), `close` cierra el período y `reopen` lo reabre."
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del período tributario.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaxPeriodUpdateRequest"
      responses:
        "200":
          description: Período tributario actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxPeriodResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-tax-periods-id
    delete:
      tags:
        - Períodos Tributários
      summary: Eliminar período tributario
      description: Elimina permanentemente el período tributario. Use con precaución — los períodos cerrados normalmente no deben eliminarse.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del período tributario.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Período tributario eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxPeriodDeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-tax-periods-id
  /v1/withholdings:
    get:
      tags:
        - Retenções
      summary: Listar retenciones
      description: Devuelve una lista paginada de las retenciones tributarias de la organización actual. Admite filtros por tipo de tributo, estado, retenedor, factura, NF-e y competencia.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            enum:
              - iss
              - pis
              - cofins
              - csll
              - irpj
              - irrf
              - csrf
              - inss
              - cbs
              - ibs
            description: "Filtra por tipo de tributo: `iss`, `pis`, `cofins`, `csll`, `irpj`, `irrf`, `csrf`, `inss`, `cbs` o `ibs`."
          required: false
          name: taxType
          in: query
        - schema:
            type: string
            enum:
              - retained
              - compensated
              - partially_compensated
            description: "Filtra por estado: `retained`, `compensated` o `partially_compensated`."
          required: false
          name: status
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por las retenciones de un retenedor (cuenta de cobro) específico.
          required: false
          name: withholderId
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por las retenciones asociadas a una factura.
          required: false
          name: invoiceId
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por las retenciones asociadas a una NF-e.
          required: false
          name: nfeId
          in: query
        - schema:
            type: integer
            minimum: 2000
            maximum: 3000
            description: Filtra por el año de competencia (2000–3000).
          required: false
          name: competenceYear
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 12
            description: Filtra por el mes de competencia (1–12).
          required: false
          name: competenceMonth
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de retenciones.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WithholdingListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-withholdings
  /v1/withholdings/convert-to-credits:
    post:
      tags:
        - Retenções
      summary: Convertir retenciones pendientes en créditos (en lote)
      description: Convierte todas las retenciones pendientes de la organización — opcionalmente acotadas por `tax_type` y/o competencia — en créditos. Devuelve la lista de créditos generados, la cantidad total convertida y el monto total en centavos.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WithholdingBulkConvertRequest"
      responses:
        "200":
          description: Lote de retenciones convertido en créditos.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WithholdingBulkConvertResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-withholdings-convert-to-credits
      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.
          schema:
            type: string
            format: uuid
  /v1/withholdings/{id}:
    get:
      tags:
        - Retenções
      summary: Obtener retención
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la retención.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle de la retención.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WithholdingResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-withholdings-id
  /v1/withholdings/{id}/convert-to-credit:
    post:
      tags:
        - Retenções
      summary: Convertir retención en crédito
      description: Convierte una retención (total o parcialmente) en un crédito en la cuenta de cobro derivada de la factura asociada — o, en su ausencia, del propio retenedor. Cuando se omite `amount_cents`, convierte el saldo pendiente de la retención.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID de la retención.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WithholdingConvertToCreditRequest"
      responses:
        "201":
          description: Crédito generado a partir de la retención.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WithholdingConvertToCreditResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-withholdings-id-convert-to-credit
  /v1/service_items:
    get:
      tags:
        - Itens de Serviço (LC 116)
      summary: Listar ítems de servicio
      description: Devuelve una lista paginada de los ítems de servicio (catálogo LC 116/2003) de la organización actual. Usa los filtros para restringir por empresa, código LC 116, NBS, clasificación tributaria, indicador de operación o anexo del Simples.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            format: uuid
            description: Filtra por UUID de la empresa emisora asociada al ítem.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            description: Búsqueda textual libre en `name`, `internal_code` y `description`.
          required: false
          name: search
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: "Filtra por estado: `true` devuelve solo ítems activos; `false`, solo inactivos."
          required: false
          name: is_active
          in: query
        - schema:
            type: string
            description: Filtra por el código LC 116/2003 (p. ej. `01.05`).
          required: false
          name: lc116_code
          in: query
        - schema:
            type: string
            description: Filtra por el código NBS (Nomenclatura Brasileña de Servicios).
          required: false
          name: nbs_code
          in: query
        - schema:
            type: string
            description: Filtra por la clasificación tributaria IBS/CBS (`c_class_trib`).
          required: false
          name: c_class_trib
          in: query
        - schema:
            type: string
            description: Filtra por el Indicador de Operación (`indop_code`) usado en la NFS-e SNNFSE.
          required: false
          name: indop_code
          in: query
        - schema:
            type: string
            enum:
              - annex_iii
              - annex_iv
              - annex_v
            description: "Filtra por el anexo del Simples Nacional aplicable: `annex_iii`, `annex_iv` o `annex_v`."
          required: false
          name: simples_annex
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de ítems de servicio.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceItemListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-service-items
    post:
      tags:
        - Itens de Serviço (LC 116)
      summary: Crear ítem de servicio
      description: Crea un ítem de servicio en el catálogo LC 116/2003. `internal_code` es el identificador interno y `lc116_code` debe seguir el catálogo de la Ley Complementaria 116/2003 (p. ej. `01.05`). Cuando se informa `company_id`, la empresa debe pertenecer a la organización autenticada.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ServiceItemCreateRequest"
      responses:
        "201":
          description: Ítem de servicio creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceItemResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-service-items
      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.
          schema:
            type: string
            format: uuid
  /v1/service_items/{id}:
    get:
      tags:
        - Itens de Serviço (LC 116)
      summary: Obtener ítem de servicio
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de servicio.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del ítem de servicio.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceItemResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-service-items-id
    patch:
      tags:
        - Itens de Serviço (LC 116)
      summary: Actualizar ítem de servicio
      description: Actualiza campos editables del ítem de servicio. `internal_code` es inmutable después de la creación. Usa `is_active=false` para desactivar el ítem sin eliminarlo.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de servicio.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ServiceItemUpdateRequest"
      responses:
        "200":
          description: Ítem de servicio actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceItemResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-service-items-id
    delete:
      tags:
        - Itens de Serviço (LC 116)
      summary: Eliminar ítem de servicio
      description: Elimina el ítem de servicio permanentemente. Para preservar el historial en facturas o NFS-e ya emitidas, prefiere desactivar el ítem (`PATCH` con `is_active=false`).
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del ítem de servicio.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Ítem de servicio eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-service-items-id
  /v1/dashboard_users:
    get:
      tags:
        - Administração da Conta
      summary: Listar usuarios del dashboard
      description: Devuelve una lista paginada de los usuarios del dashboard (miembros del equipo) de la organización actual.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            description: Búsqueda textual por nombre o correo.
          required: false
          name: search
          in: query
        - schema:
            type: string
            enum:
              - all
              - active
              - pending_invitation
              - suspended
            description: "Filtra por estado: `all` (por defecto), `active`, `pending_invitation` o `suspended`."
          required: false
          name: status
          in: query
        - schema:
            type: string
            description: Filtra por los usuarios que tienen el rol indicado (slug del rol).
          required: false
          name: role
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de usuarios del dashboard.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardUserListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-dashboard-users
    post:
      tags:
        - Administração da Conta
      summary: Invitar usuario del dashboard
      description: Invita a un nuevo miembro al equipo de la organización. Es obligatorio informar `email` y al menos un rol en `role_ids`. El usuario recibe una invitación por correo y entra con estado `pending_invitation`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DashboardUserCreateRequest"
      responses:
        "201":
          description: Usuario invitado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardUserResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-dashboard-users
      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.
          schema:
            type: string
            format: uuid
  /v1/dashboard_users/{id}:
    get:
      tags:
        - Administração da Conta
      summary: Obtener usuario del dashboard
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del usuario del dashboard.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del usuario del dashboard.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardUserResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-dashboard-users-id
    patch:
      tags:
        - Administração da Conta
      summary: Actualizar usuario del dashboard
      description: Actualiza el nombre, la foto, el estado, los permisos y/o los roles del usuario. Solo se modifican los campos enviados.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del usuario del dashboard.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DashboardUserUpdateRequest"
      responses:
        "200":
          description: Usuario actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardUserResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-dashboard-users-id
    delete:
      tags:
        - Administração da Conta
      summary: Eliminar usuario del dashboard
      description: Elimina al usuario del dashboard de la organización. Acción irreversible — el usuario debe ser invitado de nuevo para recuperar el acceso.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del usuario del dashboard.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Usuario eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-dashboard-users-id
  /v1/team_roles:
    get:
      tags:
        - Administração da Conta
      summary: Listar roles del equipo
      description: Devuelve la lista paginada de roles configurados para el equipo de la organización. Incluye tanto roles de sistema (`is_system = true`) como roles personalizados.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: integer
            minimum: 1
            description: "Página (comienza en 1). Por defecto: `1`."
            example: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: "Ítems por página. Por defecto: `20`. Máximo: `100`."
            example: 20
          required: false
          name: per_page
          in: query
        - schema:
            type: string
            description: Campo de ordenación. El valor por defecto depende del recurso.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: "Dirección de ordenación. Por defecto: `desc`."
          required: false
          name: sort_order
          in: query
        - schema:
            type: string
            description: Búsqueda textual por nombre o slug.
          required: false
          name: search
          in: query
        - schema:
            type:
              - boolean
              - "null"
            description: Filtra por roles activos (`true`) o inactivos (`false`).
          required: false
          name: is_active
          in: query
        - schema:
            type:
              - boolean
              - "null"
            description: Filtra por roles de sistema (`true`) o personalizados (`false`).
          required: false
          name: is_system
          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>
        - 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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Lista paginada de roles.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamRoleListResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-team-roles
    post:
      tags:
        - Administração da Conta
      summary: Crear rol personalizado
      description: Crea un rol personalizado. El `slug` debe empezar con una letra minúscula y contener solo letras minúsculas, dígitos y `_`.
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamRoleCreateRequest"
      responses:
        "201":
          description: Rol creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamRoleResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: post-team-roles
      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.
          schema:
            type: string
            format: uuid
  /v1/team_roles/{id}:
    get:
      tags:
        - Administração da Conta
      summary: Obtener rol
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del rol.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Detalle del rol.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamRoleResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: get-team-roles-id
    patch:
      tags:
        - Administração da Conta
      summary: Actualizar rol
      description: Actualiza los campos del rol. Solo se modifican los campos enviados. Los roles de sistema tienen restricciones adicionales.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del rol.
          required: true
          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.
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamRoleUpdateRequest"
      responses:
        "200":
          description: Rol actualizado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TeamRoleResponse"
        "400":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: El cuerpo de la solicitud falló en la validación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: patch-team-roles-id
    delete:
      tags:
        - Administração da Conta
      summary: Eliminar rol
      description: Realiza soft delete del rol. Los roles de sistema (`is_system = true`) no pueden eliminarse.
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: UUID del rol.
          required: true
          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.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Rol eliminado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Token bearer ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token válido, pero sin permiso para esta operación.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Recurso no encontrado en la organización actual.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Límite de solicitudes excedido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Error inesperado en el servidor.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
      operationId: delete-team-roles-id
webhooks: {}
