openapi: 3.1.0
info:
  title: Visto Azul API
  version: "1.0.0"
  description: |
    API REST para enviar e automatizar o WhatsApp via Visto Azul: texto, mídia,
    cobrança PIX, enquetes, campanhas em massa com anti-ban, grupos, funil de grupos,
    contatos e webhooks. Autenticação por API key (Bearer). Sem taxa por mensagem da Meta.

    Importe este arquivo no Postman, Insomnia, no HTTP Request do n8n ou em qualquer
    agente de IA para operar a API automaticamente.

    Documentação completa: https://vistoazul.com.br/docs
  contact:
    name: Visto Azul
    url: https://vistoazul.com.br
servers:
  - url: https://dashboard.vistoazul.com.br/api/v1
security:
  - bearerAuth: []
tags:
  - name: Instâncias
  - name: Mensagens
  - name: Recursos avançados
  - name: Campanhas
  - name: Contatos
  - name: Funil de grupos
  - name: Webhooks
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: "Use sua API key. Header Authorization: Bearer SUA_API_KEY."
  parameters:
    XInstance:
      in: header
      name: X-Instance
      required: false
      schema: { type: string }
      description: Nome da instância (omita se houver só um número na conta).
  responses:
    Unauthorized:
      description: API key ausente ou inválida
    PaymentRequired:
      description: Assinatura inativa
paths:
  /instances:
    get:
      tags: [Instâncias]
      summary: Lista as instâncias (números conectados) da conta
      responses:
        "200": { description: OK }
        "401": { $ref: "#/components/responses/Unauthorized" }
    post:
      tags: [Instâncias]
      summary: Cria uma instância (devolve o QR code para conectar)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string, example: vendas }
      responses:
        "201": { description: Criada }
        "403": { description: Limite do plano atingido }
  /instances/{instance}/webhook:
    post:
      tags: [Webhooks]
      summary: Configura o webhook para receber eventos no seu endpoint
      parameters:
        - in: path
          name: instance
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url: { type: string, example: "https://seu-sistema.com/webhook" }
                enabled: { type: boolean, example: true }
      responses:
        "200": { description: Configurado }
  /messages/text:
    post:
      tags: [Mensagens]
      summary: Envia uma mensagem de texto
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [instance, number, text]
              properties:
                instance: { type: string, example: vendas }
                number: { type: string, example: "5511999999999" }
                text: { type: string, example: "Olá!" }
      responses:
        "200": { description: Enviada }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /wa/send/text:
    post:
      tags: [Recursos avançados]
      summary: Texto (com opções avançadas)
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [number, text]
              properties:
                number: { type: string }
                text: { type: string }
                mentions: { type: array, items: { type: string } }
      responses:
        "200": { description: Enviada }
  /wa/send/media:
    post:
      tags: [Recursos avançados]
      summary: Envia mídia (imagem, vídeo ou documento) com legenda
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [number, type, file]
              properties:
                number: { type: string }
                type: { type: string, enum: [image, video, document] }
                file: { type: string, description: URL pública do arquivo }
                text: { type: string, description: legenda (opcional) }
      responses:
        "200": { description: Enviada }
  /wa/send/menu:
    post:
      tags: [Recursos avançados]
      summary: Envia uma enquete (poll)
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [number, type, text, choices]
              properties:
                number: { type: string }
                type: { type: string, enum: [poll], example: poll }
                text: { type: string, example: "Qual horário?" }
                choices: { type: array, items: { type: string }, example: ["Manhã", "Tarde"] }
                selectableCount: { type: integer, example: 1 }
      responses:
        "200": { description: Enviada }
  /wa/send/request-payment:
    post:
      tags: [Recursos avançados]
      summary: Envia uma cobrança PIX na conversa
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [number, amount, pixKey, pixType]
              properties:
                number: { type: string }
                amount: { type: number, example: 97.00 }
                pixKey: { type: string }
                pixType: { type: string, enum: [email, cpf, cnpj, phone, random] }
                title: { type: string }
                text: { type: string }
                itemName: { type: string }
      responses:
        "200": { description: Enviada }
  /wa/sender/simple:
    post:
      tags: [Recursos avançados]
      summary: Envio ou agendamento em lote (com anti-ban)
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [numbers, type, text]
              properties:
                numbers: { type: array, items: { type: string } }
                type: { type: string, example: text }
                text: { type: string }
                delayMin: { type: integer, example: 8 }
                delayMax: { type: integer, example: 40 }
                scheduled_for: { type: integer, description: "epoch ms (opcional)" }
      responses:
        "200": { description: OK }
  /wa/chat/check:
    post:
      tags: [Recursos avançados]
      summary: Verifica quais números têm WhatsApp (anti-ban)
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [numbers]
              properties:
                numbers: { type: array, items: { type: string } }
      responses:
        "200": { description: OK }
  /wa/group/create:
    post:
      tags: [Recursos avançados]
      summary: Cria um grupo
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                participants: { type: array, items: { type: string } }
      responses:
        "200": { description: Criado }
  /wa/group/list:
    get:
      tags: [Recursos avançados]
      summary: Lista os grupos
      parameters: [{ $ref: "#/components/parameters/XInstance" }]
      responses:
        "200": { description: OK }
  /campaigns:
    post:
      tags: [Campanhas]
      summary: Cria uma campanha (disparo em massa com anti-ban)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [instances, template, recipients]
              properties:
                instances: { type: array, items: { type: string } }
                template: { type: string, description: "spintax {a|b} e variáveis {{nome}}" }
                recipients:
                  type: array
                  items:
                    type: object
                    properties:
                      number: { type: string }
                      vars: { type: object, additionalProperties: { type: string } }
                minDelayMs: { type: integer, example: 8000 }
                maxDelayMs: { type: integer, example: 40000 }
                scheduledForMs: { type: integer, description: "epoch ms; futuro = agendada" }
      responses:
        "201": { description: Criada, devolve campaignId }
  /campaigns/{id}:
    get:
      tags: [Campanhas]
      summary: Status de uma campanha
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200": { description: "OK — { state, sent, failed, total }" }
        "404": { description: Não encontrada }
  /campaigns/{id}/pause:
    post:
      tags: [Campanhas]
      summary: Pausa uma campanha
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200": { description: OK }
  /campaigns/{id}/resume:
    post:
      tags: [Campanhas]
      summary: Retoma uma campanha pausada
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200": { description: OK }
  /campaigns/{id}/cancel:
    post:
      tags: [Campanhas]
      summary: Cancela uma campanha
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200": { description: OK }
  /contacts:
    get:
      tags: [Contatos]
      summary: Lista contatos (filtra por tag com ?tag=)
      parameters:
        - { in: query, name: tag, required: false, schema: { type: string } }
      responses:
        "200": { description: OK }
    post:
      tags: [Contatos]
      summary: Cria ou atualiza um contato
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [number]
              properties:
                number: { type: string }
                name: { type: string }
                tags: { type: array, items: { type: string } }
      responses:
        "201": { description: OK }
  /contacts/import:
    post:
      tags: [Contatos]
      summary: Importa vários contatos de uma vez
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                text: { type: string, description: "uma linha por contato: numero,nome" }
                rows:
                  type: array
                  items:
                    type: object
                    properties:
                      number: { type: string }
                      name: { type: string }
                tags: { type: array, items: { type: string } }
      responses:
        "200": { description: "OK — { imported }" }
  /funnels:
    post:
      tags: [Funil de grupos]
      summary: Cria um funil de grupos (link que enche um grupo e pula pro próximo)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, instance, jids]
              properties:
                name: { type: string }
                instance: { type: string }
                jids: { type: array, items: { type: string } }
                slug: { type: string }
                capacity: { type: integer }
                welcomeMsg: { type: string }
                fullMessage: { type: string }
      responses:
        "201": { description: Criado }
    get:
      tags: [Funil de grupos]
      summary: Lista os funis
      responses:
        "200": { description: OK }
  /funnels/{id}/status:
    get:
      tags: [Funil de grupos]
      summary: Status de um funil
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200": { description: OK }
  /funnels/{id}:
    delete:
      tags: [Funil de grupos]
      summary: Remove um funil
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        "200": { description: Removido }
