openapi: 3.0.3
info:
  title: Ogmma API
  description: |
    API da plataforma Ogmma para atendimento omnichannel via WhatsApp.

    ## Autenticacao

    Todas as requisicoes devem incluir o header `Authorization` com um Bearer token (API Key ou JWT).

    ```
    Authorization: Bearer oapi_sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
    ```

    ## Formato de Telefone

    Numeros de telefone devem seguir o formato internacional sem caracteres especiais: `DDI + DDD + NUMERO`.
    Exemplo Brasil: `5511999998888`

    ## Rate Limiting

    Requisicoes sao limitadas por plano. Headers de resposta incluem:
    - `X-RateLimit-Limit` - Limite por minuto
    - `X-RateLimit-Remaining` - Requisicoes restantes
    - `X-RateLimit-Reset` - Timestamp de reset

    ## Processamento Assincrono

    Endpoints de envio de mensagem retornam `202 Accepted`. A mensagem e enfileirada e processada de forma assincrona. Use webhooks para acompanhar o status de entrega.
  version: 1.0.0
  contact:
    name: Ogmma
    url: https://ogmma.com.br

servers:
  - url: https://oapi.ogmma.com.br/v1
    description: Producao
  - url: https://oapi.staging.ogmma.com.br/v1
    description: Staging

security:
  - bearerAuth: []

tags:
  - name: Canais
    description: Gerenciamento de canais WhatsApp
  - name: Mensagens
    description: Envio e consulta de mensagens
  - name: Conversas
    description: Gerenciamento de conversas
  - name: Contatos
    description: Gerenciamento de contatos
  - name: Webhooks
    description: Configuracao de webhooks para eventos em tempo real
  - name: Envio em Massa
    description: Envio de mensagens em lote
  - name: Sandbox
    description: Ambiente de testes
  - name: API Keys
    description: Gerenciamento de chaves de API

paths:
  # ============================================================
  # CHANNELS
  # ============================================================
  /channels:
    post:
      tags: [Canais]
      summary: Criar canal
      description: Cria um novo canal na instituicao. Apos a criacao, o canal estara com status DISCONNECTED.
      operationId: createChannel
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateChannelRequest'
            example:
              name: Atendimento Principal
              type: WHATSAPP_BAILEYS
      responses:
        '201':
          description: Canal criado com sucesso
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Channel'
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9d0"
                  name: Atendimento Principal
                  type: WHATSAPP_BAILEYS
                  status: DISCONNECTED
                  phone: null
                  createdAt: "2024-01-15T10:30:00.000Z"
                  updatedAt: "2024-01-15T10:30:00.000Z"
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    get:
      tags: [Canais]
      summary: Listar canais
      description: Retorna a lista de todos os canais da instituicao.
      operationId: listChannels
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [CONNECTED, DISCONNECTED, AWAITING_QR]
          description: Filtrar por status
        - name: type
          in: query
          schema:
            type: string
            enum: [WHATSAPP_BAILEYS, WHATSAPP_WABA]
          description: Filtrar por tipo
      responses:
        '200':
          description: Lista de canais
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Channel'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'
              example:
                data:
                  - id: "6650a1b2c3d4e5f6a7b8c9d0"
                    name: Atendimento Principal
                    type: WHATSAPP_BAILEYS
                    status: CONNECTED
                    phone: "5511999998888"
                    createdAt: "2024-01-15T10:30:00.000Z"
                    updatedAt: "2024-01-15T10:35:00.000Z"
                meta:
                  total: 2
                  page: 1
                  limit: 20
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/status:
    get:
      tags: [Canais]
      summary: Status do canal
      description: Retorna informacoes detalhadas sobre o estado atual de um canal, incluindo metricas de uso nas ultimas 24 horas.
      operationId: getChannelStatus
      parameters:
        - $ref: '#/components/parameters/channelId'
      responses:
        '200':
          description: Status do canal
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9d0"
                  name: Atendimento Principal
                  type: WHATSAPP_BAILEYS
                  status: CONNECTED
                  phone: "5511999998888"
                  uptime: "3d 14h 22m"
                  lastActivity: "2024-01-18T14:30:00.000Z"
                  messagesSent24h: 142
                  messagesReceived24h: 89
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/ChannelNotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/connect:
    post:
      tags: [Canais]
      summary: Conectar canal
      description: |
        Inicia o processo de conexao de um canal.
        - **WHATSAPP_BAILEYS**: Gera um QR Code em base64. O usuario deve escanear com o WhatsApp.
        - **WHATSAPP_WABA**: Requer credenciais da API Meta Business no body.
      operationId: connectChannel
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        description: Obrigatorio apenas para canais WHATSAPP_WABA
        content:
          application/json:
            schema:
              type: object
              properties:
                phoneNumberId:
                  type: string
                  description: ID do numero de telefone no Meta Business
                businessAccountId:
                  type: string
                  description: ID da conta WhatsApp Business
                accessToken:
                  type: string
                  description: Token de acesso permanente da Graph API
                verifyToken:
                  type: string
                  description: Token personalizado para verificacao de webhook
      responses:
        '200':
          description: Conexao iniciada
          content:
            application/json:
              examples:
                baileys:
                  summary: Resposta Baileys (QR Code)
                  value:
                    data:
                      id: "6650a1b2c3d4e5f6a7b8c9d0"
                      status: AWAITING_QR
                      qrCode: "data:image/png;base64,iVBORw0KGgoAAAANSUhEU..."
                waba:
                  summary: Resposta WABA
                  value:
                    data:
                      id: "6650a1b2c3d4e5f6a7b8c9d1"
                      status: CONNECTED
                      phone: "5511988887777"
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/disconnect:
    post:
      tags: [Canais]
      summary: Desconectar canal
      description: Desconecta um canal ativo. A sessao WhatsApp sera encerrada.
      operationId: disconnectChannel
      parameters:
        - $ref: '#/components/parameters/channelId'
      responses:
        '200':
          description: Canal desconectado
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9d0"
                  name: Atendimento Principal
                  type: WHATSAPP_BAILEYS
                  status: DISCONNECTED
                  phone: "5511999998888"
                  updatedAt: "2024-01-15T18:00:00.000Z"
        '400':
          description: Canal ja desconectado
          content:
            application/json:
              example:
                message: O canal ja esta desconectado.
                code: CHANNEL_ALREADY_DISCONNECTED
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}:
    delete:
      tags: [Canais]
      summary: Deletar canal
      description: Remove permanentemente um canal. Acao irreversivel. Mensagens e conversas associadas nao sao removidas.
      operationId: deleteChannel
      parameters:
        - $ref: '#/components/parameters/channelId'
      responses:
        '200':
          description: Canal deletado
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9d0"
                  deletedAt: "2024-01-15T18:00:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/ChannelNotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  # ============================================================
  # MESSAGES
  # ============================================================
  /channels/{channelId}/messages/text:
    post:
      tags: [Mensagens]
      summary: Enviar texto
      description: Envia uma mensagem de texto simples para um numero de telefone.
      operationId: sendText
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, text]
              properties:
                to:
                  type: string
                  description: Numero do destinatario (DDI+DDD+NUM)
                  example: "5511999998888"
                text:
                  type: string
                  description: Conteudo da mensagem
                  example: "Ola!"
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/image:
    post:
      tags: [Mensagens]
      summary: Enviar imagem
      description: Envia uma mensagem de imagem. Formatos suportados JPEG, PNG, WebP. Tamanho maximo 16MB.
      operationId: sendImage
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, imageUrl]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                imageUrl:
                  type: string
                  description: URL publica da imagem
                  example: "https://exemplo.com/imagem-produto.jpg"
                caption:
                  type: string
                  description: Legenda da imagem. Maximo 1024 caracteres.
                  example: "Confira nosso novo produto!"
                fileName:
                  type: string
                mimeType:
                  type: string
                fileSize:
                  type: number
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/video:
    post:
      tags: [Mensagens]
      summary: Enviar video
      description: Envia uma mensagem de video. Formatos suportados MP4, 3GP. Tamanho maximo 16MB.
      operationId: sendVideo
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, videoUrl]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                videoUrl:
                  type: string
                  description: URL publica do video
                  example: "https://exemplo.com/video-tutorial.mp4"
                caption:
                  type: string
                  description: Legenda do video. Maximo 1024 caracteres.
                fileName:
                  type: string
                mimeType:
                  type: string
                fileSize:
                  type: number
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/audio:
    post:
      tags: [Mensagens]
      summary: Enviar audio
      description: |
        Envia uma mensagem de audio. Use `ptt: true` para mensagem de voz (bolha) ou `ptt: false` para arquivo de audio com player.
      operationId: sendAudio
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, audioUrl]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                audioUrl:
                  type: string
                  description: URL publica do arquivo de audio
                  example: "https://exemplo.com/audio-atendimento.ogg"
                ptt:
                  type: boolean
                  description: Se true, envia como mensagem de voz. Padrao false.
                  default: false
                fileName:
                  type: string
                mimeType:
                  type: string
                fileSize:
                  type: number
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/document:
    post:
      tags: [Mensagens]
      summary: Enviar documento
      description: Envia um documento (arquivo). Qualquer extensao aceita. Tamanho maximo 100MB.
      operationId: sendDocument
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, documentUrl, fileName]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                documentUrl:
                  type: string
                  description: URL publica do documento
                  example: "https://exemplo.com/relatorio-mensal.pdf"
                fileName:
                  type: string
                  description: Nome do arquivo com extensao
                  example: "relatorio-janeiro-2024.pdf"
                caption:
                  type: string
                  description: Legenda do documento. Maximo 1024 caracteres.
                mimeType:
                  type: string
                fileSize:
                  type: number
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/contact:
    post:
      tags: [Mensagens]
      summary: Enviar contato
      description: Envia um cartao de contato (vCard).
      operationId: sendContact
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, name, phone]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                name:
                  type: string
                  description: Nome completo do contato
                  example: "Maria Silva - Suporte"
                phone:
                  type: string
                  description: Numero de telefone do contato (DDI+DDD+NUM)
                  example: "5511988887777"
                email:
                  type: string
                organization:
                  type: string
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/buttons:
    post:
      tags: [Mensagens]
      summary: Enviar botoes
      description: |
        Envia uma mensagem interativa com botoes de resposta rapida.
        Disponivel apenas para canais WHATSAPP_BAILEYS. Maximo 3 botoes.
      operationId: sendButtons
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, content, buttons]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                content:
                  type: string
                  description: Texto principal exibido acima dos botoes
                  example: "Como voce avalia nosso atendimento?"
                buttons:
                  type: array
                  maxItems: 3
                  items:
                    type: object
                    required: [id, text]
                    properties:
                      id:
                        type: string
                        example: "btn_otimo"
                      text:
                        type: string
                        description: Maximo 20 caracteres
                        example: "Otimo"
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/template:
    post:
      tags: [Mensagens]
      summary: Enviar template
      description: |
        Envia uma mensagem de template pre-aprovado pelo WhatsApp.
        Disponivel apenas para canais WHATSAPP_WABA. Templates sao o unico tipo de mensagem que pode ser enviado fora da janela de 24 horas.
      operationId: sendTemplate
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, templateId]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                templateId:
                  type: string
                  description: ID do template aprovado no Meta Business Manager
                  example: "6650a1b2c3d4e5f6a7b8c9d1"
                variables:
                  type: object
                  description: Variaveis dinamicas do template (chave-valor)
                  additionalProperties:
                    type: string
                  example:
                    "1": "Joao Silva"
                    "2": "#12345"
                headerParams:
                  type: object
                  properties:
                    imageUrl:
                      type: string
                    videoUrl:
                      type: string
                    documentUrl:
                      type: string
                    text:
                      type: string
                conversationId:
                  type: string
                  description: ID de uma conversa existente para associar a mensagem
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/react:
    post:
      tags: [Mensagens]
      summary: Reagir a mensagem
      description: Adiciona uma reacao (emoji) a uma mensagem existente. Envie emoji vazio para remover.
      operationId: reactMessage
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [messageId, emoji]
              properties:
                messageId:
                  type: string
                  example: "6650a1b2c3d4e5f6a7b8c9f0"
                emoji:
                  type: string
                  description: Emoji da reacao. String vazia para remover.
      responses:
        '200':
          description: Reacao processada
          content:
            application/json:
              example:
                success: true
                message: Reacao processada
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/MessageNotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/edit:
    post:
      tags: [Mensagens]
      summary: Editar mensagem
      description: Edita uma mensagem de texto enviada anteriormente. Apenas mensagens de texto OUTBOUND dentro de 15 minutos.
      operationId: editMessage
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [messageId, newText]
              properties:
                messageId:
                  type: string
                  example: "6650a1b2c3d4e5f6a7b8c9f0"
                newText:
                  type: string
                  description: Novo conteudo da mensagem
                  example: "Texto editado"
      responses:
        '200':
          description: Edicao processada
          content:
            application/json:
              example:
                success: true
                message: Edicao processada
        '400':
          description: Fora da janela de edicao ou tipo nao editavel
          content:
            application/json:
              example:
                message: A mensagem nao pode mais ser editada. O limite de 15 minutos foi excedido.
                code: EDIT_WINDOW_EXPIRED
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/MessageNotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/delete:
    post:
      tags: [Mensagens]
      summary: Apagar mensagem
      description: Apaga uma mensagem para todos os participantes. Apenas mensagens OUTBOUND.
      operationId: deleteMessage
      parameters:
        - $ref: '#/components/parameters/channelId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [messageId]
              properties:
                messageId:
                  type: string
                  example: "6650a1b2c3d4e5f6a7b8c9f0"
      responses:
        '200':
          description: Exclusao processada
          content:
            application/json:
              example:
                success: true
                message: Exclusao processada
        '400':
          description: Mensagem nao pode ser apagada
          content:
            application/json:
              example:
                message: A mensagem nao pode ser apagada.
                code: MESSAGE_NOT_DELETABLE
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/MessageNotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/conversations/{conversationId}:
    get:
      tags: [Mensagens]
      summary: Listar mensagens
      description: Retorna as mensagens de uma conversa especifica.
      operationId: listMessages
      parameters:
        - $ref: '#/components/parameters/channelId'
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
          description: ID da conversa
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 500
          description: Quantidade de mensagens retornadas
      responses:
        '200':
          description: Lista de mensagens
          content:
            application/json:
              example:
                data:
                  - id: "6650a1b2c3d4e5f6a7b8c9f0"
                    conversationId: "6650a1b2c3d4e5f6a7b8c9d1"
                    channelId: "6650a1b2c3d4e5f6a7b8c9d2"
                    type: text
                    direction: OUTBOUND
                    status: DELIVERED
                    content: "Ola! Como posso ajudar?"
                    mediaUrl: null
                    from: "5511999998888"
                    to: "5511977776666"
                    createdAt: "2024-01-15T10:30:00.000Z"
                    updatedAt: "2024-01-15T10:30:05.000Z"
                meta:
                  limit: 50
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /channels/{channelId}/messages/{messageId}:
    get:
      tags: [Mensagens]
      summary: Buscar mensagem
      description: Retorna os detalhes completos de uma mensagem especifica.
      operationId: getMessage
      parameters:
        - $ref: '#/components/parameters/channelId'
        - name: messageId
          in: path
          required: true
          schema:
            type: string
          description: ID da mensagem
      responses:
        '200':
          description: Detalhes da mensagem
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9f0"
                  conversationId: "6650a1b2c3d4e5f6a7b8c9d1"
                  channelId: "6650a1b2c3d4e5f6a7b8c9d2"
                  type: image
                  direction: OUTBOUND
                  status: READ
                  content: null
                  caption: "Foto do produto"
                  mediaUrl: "https://oapi.ogmma.com.br/v1/media/6650a1b2c3d4e5f6a7b8c9f0"
                  from: "5511999998888"
                  to: "5511977776666"
                  quotedMessageId: null
                  createdAt: "2024-01-15T10:30:00.000Z"
                  updatedAt: "2024-01-15T10:32:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/MessageNotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  # ============================================================
  # CONVERSATIONS
  # ============================================================
  /conversations:
    get:
      tags: [Conversas]
      summary: Listar conversas
      description: Retorna a lista de conversas da instituicao com filtros opcionais.
      operationId: listConversations
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [OPEN, CLOSED, PENDING]
        - name: channelId
          in: query
          schema:
            type: string
        - name: assignedTo
          in: query
          schema:
            type: string
        - name: departmentId
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Lista de conversas
          content:
            application/json:
              example:
                data:
                  - id: "6650a1b2c3d4e5f6a7b8c9d1"
                    channelId: "6650a1b2c3d4e5f6a7b8c9d0"
                    contact:
                      id: "6650a1b2c3d4e5f6a7b8c9e0"
                      name: "Joao Silva"
                      phone: "5511977776666"
                    status: OPEN
                    unreadCount: 3
                    lastMessage:
                      content: "Preciso de ajuda"
                      direction: INBOUND
                      timestamp: "2024-01-15T10:30:00.000Z"
                    createdAt: "2024-01-15T10:00:00.000Z"
                meta:
                  total: 45
                  page: 1
                  limit: 20
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /conversations/{conversationId}:
    get:
      tags: [Conversas]
      summary: Buscar conversa
      description: Retorna os detalhes de uma conversa especifica.
      operationId: getConversation
      parameters:
        - $ref: '#/components/parameters/conversationId'
      responses:
        '200':
          description: Detalhes da conversa
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9d1"
                  channelId: "6650a1b2c3d4e5f6a7b8c9d0"
                  contact:
                    id: "6650a1b2c3d4e5f6a7b8c9e0"
                    name: "Joao Silva"
                    phone: "5511977776666"
                  assignedTo:
                    id: "6650a1b2c3d4e5f6a7b8c9a0"
                    name: "Ana Atendente"
                  departmentId: "6650a1b2c3d4e5f6a7b8c9b0"
                  status: OPEN
                  unreadCount: 0
                  createdAt: "2024-01-15T10:00:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Conversa nao encontrada
          content:
            application/json:
              example:
                message: Conversa nao encontrada.
                code: CONVERSATION_NOT_FOUND
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /conversations/{conversationId}/assign:
    patch:
      tags: [Conversas]
      summary: Atribuir conversa
      description: Atribui uma conversa a um atendente especifico.
      operationId: assignConversation
      parameters:
        - $ref: '#/components/parameters/conversationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [attendantId]
              properties:
                attendantId:
                  type: string
                  example: "6650a1b2c3d4e5f6a7b8c9a0"
      responses:
        '200':
          description: Conversa atribuida
          content:
            application/json:
              example:
                success: true
                message: Conversa atribuida com sucesso
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /conversations/{conversationId}/close:
    patch:
      tags: [Conversas]
      summary: Encerrar conversa
      description: Encerra uma conversa ativa.
      operationId: closeConversation
      parameters:
        - $ref: '#/components/parameters/conversationId'
      responses:
        '200':
          description: Conversa encerrada
          content:
            application/json:
              example:
                success: true
                message: Conversa encerrada com sucesso
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /conversations/{conversationId}/transfer:
    patch:
      tags: [Conversas]
      summary: Transferir conversa
      description: Transfere uma conversa para outro departamento ou atendente.
      operationId: transferConversation
      parameters:
        - $ref: '#/components/parameters/conversationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                departmentId:
                  type: string
                  example: "6650a1b2c3d4e5f6a7b8c9b0"
                attendantId:
                  type: string
                  example: "6650a1b2c3d4e5f6a7b8c9a0"
      responses:
        '200':
          description: Conversa transferida
          content:
            application/json:
              example:
                success: true
                message: Conversa transferida com sucesso
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  # ============================================================
  # CONTACTS
  # ============================================================
  /contacts:
    post:
      tags: [Contatos]
      summary: Criar contato
      description: Cria um novo contato na instituicao.
      operationId: createContact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContactRequest'
            example:
              fullName: "Joao Silva"
              phone: "5511999998888"
              email: "joao@email.com"
              tags: ["cliente-vip"]
      responses:
        '201':
          description: Contato criado
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9e0"
                  fullName: "Joao Silva"
                  phone: "5511999998888"
                  email: "joao@email.com"
                  tags: ["cliente-vip"]
                  createdAt: "2024-01-15T10:00:00.000Z"
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    get:
      tags: [Contatos]
      summary: Listar contatos
      description: Retorna a lista de contatos da instituicao.
      operationId: listContacts
      parameters:
        - name: search
          in: query
          schema:
            type: string
          description: Buscar por nome, telefone ou email
        - name: tags
          in: query
          schema:
            type: string
          description: Filtrar por tags (separadas por virgula)
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Lista de contatos
          content:
            application/json:
              example:
                data:
                  - id: "6650a1b2c3d4e5f6a7b8c9e0"
                    fullName: "Joao Silva"
                    phone: "5511999998888"
                    email: "joao@email.com"
                    tags: ["cliente-vip"]
                    createdAt: "2024-01-15T10:00:00.000Z"
                meta:
                  total: 150
                  page: 1
                  limit: 20
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /contacts/{contactId}:
    get:
      tags: [Contatos]
      summary: Buscar contato
      description: Retorna os detalhes de um contato especifico.
      operationId: getContact
      parameters:
        - $ref: '#/components/parameters/contactId'
      responses:
        '200':
          description: Detalhes do contato
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9e0"
                  fullName: "Joao Silva"
                  phone: "5511999998888"
                  email: "joao@email.com"
                  tags: ["cliente-vip"]
                  notes: "Cliente desde 2023"
                  createdAt: "2024-01-15T10:00:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Contato nao encontrado
          content:
            application/json:
              example:
                message: Contato nao encontrado.
                code: CONTACT_NOT_FOUND
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    patch:
      tags: [Contatos]
      summary: Atualizar contato
      description: Atualiza os dados de um contato existente.
      operationId: updateContact
      parameters:
        - $ref: '#/components/parameters/contactId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                fullName:
                  type: string
                email:
                  type: string
                phone:
                  type: string
                tags:
                  type: array
                  items:
                    type: string
                notes:
                  type: string
            example:
              fullName: "Joao Silva Jr."
              email: "joao.jr@email.com"
      responses:
        '200':
          description: Contato atualizado
          content:
            application/json:
              example:
                data:
                  id: "6650a1b2c3d4e5f6a7b8c9e0"
                  fullName: "Joao Silva Jr."
                  phone: "5511999998888"
                  email: "joao.jr@email.com"
                  updatedAt: "2024-01-16T08:00:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Contato nao encontrado
          content:
            application/json:
              example:
                message: Contato nao encontrado.
                code: CONTACT_NOT_FOUND
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    delete:
      tags: [Contatos]
      summary: Excluir contato
      description: Remove um contato. Os dados sao anonimizados para conformidade com LGPD.
      operationId: deleteContact
      parameters:
        - $ref: '#/components/parameters/contactId'
      responses:
        '200':
          description: Contato removido
          content:
            application/json:
              example:
                message: Contato removido
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  # ============================================================
  # WEBHOOKS
  # ============================================================
  /webhooks:
    post:
      tags: [Webhooks]
      summary: Criar webhook
      description: Registra um novo webhook para receber notificacoes de eventos.
      operationId: createWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
            example:
              name: "Notificacoes de Mensagens"
              url: "https://meuservidor.com.br/webhooks/ogmma"
              events:
                - message.received
                - message.sent
                - conversation.created
              secret: "minha_chave_secreta"
      responses:
        '201':
          description: Webhook criado
          content:
            application/json:
              example:
                data:
                  id: "665f1a2b3c4d5e6f7a8b9c0d"
                  name: "Notificacoes de Mensagens"
                  url: "https://meuservidor.com.br/webhooks/ogmma"
                  events: ["message.received", "message.sent", "conversation.created"]
                  active: true
                  consecutiveFailures: 0
                  createdAt: "2024-06-10T14:30:00.000Z"
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    get:
      tags: [Webhooks]
      summary: Listar webhooks
      description: Retorna todos os webhooks configurados para a instituicao.
      operationId: listWebhooks
      responses:
        '200':
          description: Lista de webhooks
          content:
            application/json:
              example:
                data:
                  - id: "665f1a2b3c4d5e6f7a8b9c0d"
                    name: "Notificacoes de Mensagens"
                    url: "https://meuservidor.com.br/webhooks/ogmma"
                    events: ["message.received", "message.sent"]
                    active: true
                    consecutiveFailures: 0
                    lastFailedAt: null
                    createdAt: "2024-06-10T14:30:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /webhooks/{webhookId}:
    patch:
      tags: [Webhooks]
      summary: Atualizar webhook
      description: Atualiza configuracoes de um webhook. Use active=true para reativar webhooks desativados.
      operationId: updateWebhook
      parameters:
        - $ref: '#/components/parameters/webhookId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                active:
                  type: boolean
                secret:
                  type: string
            example:
              events: ["message.received", "message.sent"]
              active: true
      responses:
        '200':
          description: Webhook atualizado
          content:
            application/json:
              example:
                data:
                  id: "665f1a2b3c4d5e6f7a8b9c0d"
                  name: "Notificacoes de Mensagens"
                  events: ["message.received", "message.sent"]
                  active: true
                  updatedAt: "2024-06-11T09:00:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    delete:
      tags: [Webhooks]
      summary: Excluir webhook
      description: Remove permanentemente um webhook. Acao irreversivel.
      operationId: deleteWebhook
      parameters:
        - $ref: '#/components/parameters/webhookId'
      responses:
        '200':
          description: Webhook removido
          content:
            application/json:
              example:
                message: Webhook removido
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /webhooks/{webhookId}/logs:
    get:
      tags: [Webhooks]
      summary: Logs de entrega
      description: Retorna os logs de entrega de eventos de um webhook especifico.
      operationId: getWebhookLogs
      parameters:
        - $ref: '#/components/parameters/webhookId'
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: status
          in: query
          schema:
            type: string
            enum: [success, failed]
      responses:
        '200':
          description: Logs de entrega
          content:
            application/json:
              example:
                data:
                  - id: "665f3a4b5c6d7e8f9a0b1c2d"
                    event: "message.received"
                    status: success
                    statusCode: 200
                    responseTime: 145
                    attemptNumber: 1
                    createdAt: "2024-06-10T15:00:00.000Z"
                meta:
                  total: 250
                  page: 1
                  limit: 20
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  # ============================================================
  # BULK SEND
  # ============================================================
  /bulk-send:
    post:
      tags: [Envio em Massa]
      summary: Enviar mensagens em massa
      description: Cria um lote de envio de mensagens de texto. Maximo 1000 destinatarios.
      operationId: createBulkSend
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBulkSendRequest'
            example:
              channelId: "6650a1b2c3d4e5f6a7b8c9d0"
              recipients:
                - phone: "5511999998888"
                  text: "Ola Joao, temos uma novidade!"
                - phone: "5511977776666"
                  text: "Ola Maria, confira nossa promocao!"
              delay: 3000
              jitter: true
      responses:
        '201':
          description: Lote criado
          content:
            application/json:
              example:
                data:
                  id: "6650b1c2d3e4f5a6b7c8d9e0"
                  channelId: "6650a1b2c3d4e5f6a7b8c9d0"
                  type: text
                  status: QUEUED
                  totalRecipients: 2
                  sent: 0
                  failed: 0
                  pending: 2
                  delay: 3000
                  createdAt: "2024-01-15T10:00:00.000Z"
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    get:
      tags: [Envio em Massa]
      summary: Listar lotes
      description: Retorna a lista de lotes de envio em massa.
      operationId: listBulkSend
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [QUEUED, IN_PROGRESS, COMPLETED, PARTIALLY_FAILED, CANCELLED]
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Lista de lotes
          content:
            application/json:
              example:
                data:
                  - id: "6650b1c2d3e4f5a6b7c8d9e0"
                    channelId: "6650a1b2c3d4e5f6a7b8c9d0"
                    type: text
                    status: COMPLETED
                    totalRecipients: 100
                    sent: 98
                    failed: 2
                    delay: 3000
                    createdAt: "2024-01-15T10:00:00.000Z"
                    completedAt: "2024-01-15T10:15:00.000Z"
                meta:
                  total: 10
                  page: 1
                  limit: 20
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /bulk-send/template:
    post:
      tags: [Envio em Massa]
      summary: Enviar templates em massa
      description: Cria um lote de envio de templates WABA. Maximo 1000 destinatarios.
      operationId: createBulkSendTemplate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [channelId, templateId, recipients]
              properties:
                channelId:
                  type: string
                templateId:
                  type: string
                recipients:
                  type: array
                  maxItems: 1000
                  items:
                    type: object
                    required: [phone]
                    properties:
                      phone:
                        type: string
                      variables:
                        type: object
                        additionalProperties:
                          type: string
                delay:
                  type: integer
                  minimum: 1000
                jitter:
                  type: boolean
            example:
              channelId: "6650a1b2c3d4e5f6a7b8c9d0"
              templateId: "6650a1b2c3d4e5f6a7b8c9d1"
              recipients:
                - phone: "5511999998888"
                  variables:
                    "1": "Joao"
                - phone: "5511977776666"
                  variables:
                    "1": "Maria"
              delay: 3000
      responses:
        '201':
          description: Lote de templates criado
          content:
            application/json:
              example:
                data:
                  id: "6650b1c2d3e4f5a6b7c8d9e1"
                  type: template
                  status: QUEUED
                  totalRecipients: 2
                  createdAt: "2024-01-15T10:00:00.000Z"
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /bulk-send/{batchId}:
    get:
      tags: [Envio em Massa]
      summary: Status do lote
      description: Retorna o status e progresso de um lote de envio.
      operationId: getBulkSendStatus
      parameters:
        - $ref: '#/components/parameters/batchId'
      responses:
        '200':
          description: Status do lote
          content:
            application/json:
              example:
                data:
                  id: "6650b1c2d3e4f5a6b7c8d9e0"
                  type: text
                  status: IN_PROGRESS
                  totalRecipients: 100
                  sent: 45
                  failed: 2
                  pending: 53
                  createdAt: "2024-01-15T10:00:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Lote nao encontrado
          content:
            application/json:
              example:
                message: Lote nao encontrado.
                code: BULK_BATCH_NOT_FOUND
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    delete:
      tags: [Envio em Massa]
      summary: Cancelar lote
      description: Cancela um lote em andamento. Mensagens ja enviadas nao sao revertidas.
      operationId: cancelBulkSend
      parameters:
        - $ref: '#/components/parameters/batchId'
      responses:
        '200':
          description: Lote cancelado
          content:
            application/json:
              example:
                data:
                  id: "6650b1c2d3e4f5a6b7c8d9e0"
                  status: CANCELLED
                  sent: 45
                  cancelledAt: "2024-01-15T10:05:00.000Z"
        '400':
          description: Lote nao pode ser cancelado
          content:
            application/json:
              example:
                message: Este lote ja foi concluido.
                code: BULK_BATCH_NOT_CANCELLABLE
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  # ============================================================
  # SANDBOX
  # ============================================================
  /sandbox/messages/{channelId}/{type}:
    post:
      tags: [Sandbox]
      summary: Enviar mensagem de teste
      description: Envia uma mensagem no ambiente sandbox. Mesmos parametros da API de producao, sem envio real ao WhatsApp.
      operationId: sendSandboxMessage
      parameters:
        - $ref: '#/components/parameters/channelId'
        - name: type
          in: path
          required: true
          schema:
            type: string
            enum: [text, image, video, audio, document, contact]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to]
              properties:
                to:
                  type: string
                  example: "5511999998888"
                text:
                  type: string
                imageUrl:
                  type: string
                videoUrl:
                  type: string
                audioUrl:
                  type: string
                documentUrl:
                  type: string
                caption:
                  type: string
      responses:
        '202':
          $ref: '#/components/responses/MessageQueued'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /sandbox/messages:
    get:
      tags: [Sandbox]
      summary: Listar mensagens sandbox
      description: Retorna as mensagens enviadas no ambiente sandbox. Dados retidos por 30 dias.
      operationId: listSandboxMessages
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Lista de mensagens sandbox
          content:
            application/json:
              example:
                data:
                  - id: "6650c1d2e3f4a5b6c7d8e9f0"
                    type: text
                    to: "5511999998888"
                    content: "Mensagem de teste"
                    status: SENT
                    createdAt: "2024-01-15T10:00:00.000Z"
                meta:
                  total: 5
                  page: 1
                  limit: 20
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /sandbox/messages/simulate:
    post:
      tags: [Sandbox]
      summary: Simular mensagem recebida
      description: Simula o recebimento de uma mensagem de um contato. Util para testar webhooks e fluxos.
      operationId: simulateIncoming
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [channelId, from, type, content]
              properties:
                channelId:
                  type: string
                  example: "6650a1b2c3d4e5f6a7b8c9d0"
                from:
                  type: string
                  example: "5511977776666"
                type:
                  type: string
                  enum: [text, image, video, audio, document]
                  example: text
                content:
                  type: string
                  example: "Ola, preciso de ajuda!"
      responses:
        '200':
          description: Mensagem simulada
          content:
            application/json:
              example:
                success: true
                message: Mensagem simulada com sucesso
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  # ============================================================
  # API KEYS
  # ============================================================
  /api-keys:
    post:
      tags: [API Keys]
      summary: Criar API Key
      description: Cria uma nova chave de API com permissoes especificas.
      operationId: createApiKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, permissions]
              properties:
                name:
                  type: string
                  example: "Integracao N8N"
                permissions:
                  type: array
                  items:
                    type: string
                  example: ["messages.send", "messages.read", "contacts.read"]
                environment:
                  type: string
                  enum: [live, test]
                  default: live
      responses:
        '201':
          description: API Key criada
          content:
            application/json:
              example:
                data:
                  id: "6650d1e2f3a4b5c6d7e8f9a0"
                  name: "Integracao N8N"
                  key: "oapi_sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                  permissions: ["messages.send", "messages.read", "contacts.read"]
                  environment: live
                  createdAt: "2024-01-15T10:00:00.000Z"
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

    get:
      tags: [API Keys]
      summary: Listar API Keys
      description: Retorna todas as API Keys da instituicao. A chave completa so e exibida na criacao.
      operationId: listApiKeys
      responses:
        '200':
          description: Lista de API Keys
          content:
            application/json:
              example:
                data:
                  - id: "6650d1e2f3a4b5c6d7e8f9a0"
                    name: "Integracao N8N"
                    keyPreview: "oapi_sk_live_xxxx...xxxx"
                    permissions: ["messages.send", "messages.read"]
                    environment: live
                    lastUsedAt: "2024-01-15T14:30:00.000Z"
                    createdAt: "2024-01-15T10:00:00.000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

  /api-keys/{apiKeyId}:
    delete:
      tags: [API Keys]
      summary: Revogar API Key
      description: Revoga permanentemente uma API Key. Requisicoes com essa chave serao rejeitadas.
      operationId: revokeApiKey
      parameters:
        - name: apiKeyId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: API Key revogada
          content:
            application/json:
              example:
                message: API Key revogada com sucesso
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: API Key nao encontrada
          content:
            application/json:
              example:
                message: API Key nao encontrada.
                code: API_KEY_NOT_FOUND
        '429':
          $ref: '#/components/responses/RateLimitExceeded'

# ============================================================
# COMPONENTS
# ============================================================
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API Key no formato oapi_sk_live_... ou JWT token

  parameters:
    channelId:
      name: channelId
      in: path
      required: true
      schema:
        type: string
      description: ID do canal
      example: "6650a1b2c3d4e5f6a7b8c9d0"
    conversationId:
      name: conversationId
      in: path
      required: true
      schema:
        type: string
      description: ID da conversa
    contactId:
      name: contactId
      in: path
      required: true
      schema:
        type: string
      description: ID do contato
    webhookId:
      name: webhookId
      in: path
      required: true
      schema:
        type: string
      description: ID do webhook
    batchId:
      name: batchId
      in: path
      required: true
      schema:
        type: string
      description: ID do lote de envio

  schemas:
    Channel:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
          enum: [WHATSAPP_BAILEYS, WHATSAPP_WABA]
        status:
          type: string
          enum: [CONNECTED, DISCONNECTED, AWAITING_QR]
        phone:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    CreateChannelRequest:
      type: object
      required: [name, type]
      properties:
        name:
          type: string
          minLength: 3
          maxLength: 100
        type:
          type: string
          enum: [WHATSAPP_BAILEYS, WHATSAPP_WABA]

    CreateContactRequest:
      type: object
      required: [fullName, phone]
      properties:
        fullName:
          type: string
        phone:
          type: string
        email:
          type: string
          format: email
        tags:
          type: array
          items:
            type: string
        notes:
          type: string

    CreateWebhookRequest:
      type: object
      required: [name, url, events]
      properties:
        name:
          type: string
        url:
          type: string
          format: uri
          description: URL HTTPS obrigatoria
        events:
          type: array
          items:
            type: string
            enum:
              - message.received
              - message.sent
              - message.delivered
              - message.read
              - conversation.created
              - conversation.closed
              - channel.connected
              - channel.disconnected
              - contact.created
              - contact.updated
              - bulk.completed
              - bulk.failed
        secret:
          type: string
          description: Chave secreta para assinatura HMAC-SHA256

    CreateBulkSendRequest:
      type: object
      required: [channelId, recipients]
      properties:
        channelId:
          type: string
        recipients:
          type: array
          maxItems: 1000
          items:
            type: object
            required: [phone, text]
            properties:
              phone:
                type: string
              text:
                type: string
        delay:
          type: integer
          minimum: 1000
          description: Intervalo em ms entre envios
        jitter:
          type: boolean
          description: Variacao aleatoria no delay

    PaginationMeta:
      type: object
      properties:
        total:
          type: integer
        page:
          type: integer
        limit:
          type: integer

  responses:
    MessageQueued:
      description: Mensagem enfileirada para envio
      content:
        application/json:
          example:
            success: true
            message: Mensagem enfileirada para envio

    ValidationError:
      description: Dados de entrada invalidos
      content:
        application/json:
          example:
            message: Dados de entrada invalidos.
            code: VALIDATION_ERROR
            errors:
              - field: to
                message: O numero do destinatario e obrigatorio.

    Unauthorized:
      description: Token de autenticacao invalido ou ausente
      content:
        application/json:
          example:
            message: Token de autenticacao invalido ou ausente.
            code: UNAUTHORIZED

    RateLimitExceeded:
      description: Limite de requisicoes excedido
      content:
        application/json:
          example:
            message: Limite de requisicoes excedido. Tente novamente em alguns segundos.
            code: RATE_LIMIT_EXCEEDED

    ChannelNotFound:
      description: Canal nao encontrado
      content:
        application/json:
          example:
            message: Canal nao encontrado.
            code: CHANNEL_NOT_FOUND

    MessageNotFound:
      description: Mensagem nao encontrada
      content:
        application/json:
          example:
            message: Mensagem nao encontrada.
            code: MESSAGE_NOT_FOUND
