📸 Documentação Completa

Mídias e Arquivos - WhatsApp Multi-Sessão API

← Voltar para Documentação Principal

Guia completo para envio e recebimento de imagens, vídeos, áudios e documentos via WhatsApp API.

✅ Sistema Otimizado:

Mídias são armazenadas em disco (redução de 99% no uso de memória), com cache automático e download único.

📑 Índice

📋 Tipos de Mídia Suportados

🖼️ Imagens

  • image/jpeg - JPG, JPEG
  • image/png - PNG
  • image/gif - GIF
  • image/webp - WebP

Tamanho máximo: ~16 MB

🎬 Vídeos

  • video/mp4 - MP4 (recomendado)
  • video/avi - AVI
  • video/quicktime - MOV

Tamanho máximo: ~64 MB

🎵 Áudios

  • audio/mpeg - MP3
  • audio/ogg - OGG
  • audio/mp4 - M4A
  • audio/wav - WAV

Tamanho máximo: ~16 MB

📄 Documentos

  • application/pdf - PDF
  • application/msword - DOC
  • application/vnd.openxmlformats-officedocument.wordprocessingml.document - DOCX
  • application/vnd.ms-excel - XLS
  • application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - XLSX

Tamanho máximo: ~100 MB

⚠️ ATENÇÃO: Para enviar mídia, você DEVE usar o endpoint POST /api/messages/media.

NÃO envie URLs de mídia através de POST /api/messages/text - isso resultará em links ao invés de arquivos. Veja a seção Erro Comum para mais detalhes.

📤 Enviar Mídia

POST /api/messages/media

Envia mídia (imagem, vídeo, áudio ou documento) para um número via WhatsApp.

❌ ERRO COMUM: Não envie URLs de mídia através de POST /api/messages/text!

Se você enviar uma URL de mídia como mensagem de texto, o destinatário receberá um link ao invés do arquivo. SEMPRE use este endpoint (/api/messages/media) para enviar mídias.

POST https://app.autowhats.com.br/api/messages/media
Headers: X-API-Key: sua_api_key
Content-Type: application/json
Opção 1: Enviar via URL (Recomendado para arquivos grandes)
{
  "sessionId": "sua-sessao",
  "to": "5511999999999",
  "mediaUrl": "https://exemplo.com/imagem.jpg",
  "caption": "Legenda opcional",
  "mimetype": "image/jpeg",  // opcional, será detectado automaticamente
  "filename": "imagem.jpg"   // opcional
}

💡 URLs Relativas Suportadas:

Você pode usar URLs relativas (ex: /api/media/arquivo.jpg). A API converte automaticamente para URL absoluta.

Opção 2: Enviar via Base64 (Para arquivos pequenos)
{
  "sessionId": "sua-sessao",
  "to": "5511999999999",
  "mediaBase64": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
  "caption": "Legenda opcional",
  "mimetype": "image/jpeg",
  "filename": "imagem.jpg"
}
Resposta de Sucesso:
{
  "success": true,
  "messageId": "true_5511999999999@c.us_ABC123",
  "to": "5511999999999@c.us",
  "type": "media",
  "mimetype": "image/jpeg",
  "timestamp": "2024-11-27T19:00:00.000Z"
}

⚠️ Importante:

  • URLs devem ser acessíveis publicamente (não localhost ou IPs privados)
  • URLs relativas são convertidas automaticamente para absolutas
  • Base64 aumenta o tamanho em ~33% - use apenas para arquivos pequenos
  • Para arquivos grandes, sempre use mediaUrl

📥 Receber Mídia

Mídias recebidas são armazenadas em disco e você recebe uma URL para acessá-las. Isso reduz o uso de memória em 99% e melhora significativamente a performance.

✅ Vantagens do Sistema Otimizado:

  • Redução de 99% no uso de memória (mídias em disco, não em memória)
  • Download único (elimina download duplo)
  • Cache automático (mídias não são baixadas repetidamente)
  • Acesso direto via URL (mais rápido que Base64)

🔍 Como a API Processa Mídias Recebidas

1️⃣ Detecção Automática

Quando uma mensagem é recebida, a API verifica automaticamente se contém mídia usando a propriedade msg.hasMedia do WhatsApp Web.js.

if (msg.hasMedia) {
  // Mensagem contém mídia (imagem, vídeo, áudio, documento)
  mediaInfo = await getMediaWithCache(msg, sessionId);
}
2️⃣ Download e Armazenamento

A mídia é baixada uma única vez e salva em disco no diretório /api/media/ com um nome único baseado no ID da mensagem.

  • Nome do arquivo: session-{sessionId}_{messageId}.{extensao}
  • Exemplo: session-abc123_true_1234567890@lid_ABC.jpg
  • Cache: Se a mídia já foi baixada, retorna do cache (sem novo download)
3️⃣ Salvamento no Banco de Dados

A mensagem é salva na tabela messages com os seguintes campos:

{
  "direction": "received",
  "message_type": "image",  // ou "video", "audio", "document"
  "body": "Legenda da mídia (se houver)",
  "media_url": "/api/media/session-abc123_ABC123.jpg",
  "media_mimetype": "image/jpeg",
  "media_filename": "foto.jpg"  // nome original (se disponível)
}

⚠️ IMPORTANTE:

O campo message_type indica o tipo de mídia:

  • "image" ou "image/jpeg", "image/png", etc. → Imagem
  • "video" ou "video/mp4", etc. → Vídeo
  • "audio" ou "audio/mpeg", etc. → Áudio
  • "document" ou "application/pdf", etc. → Documento
  • "text" → Mensagem de texto (sem mídia)
4️⃣ Envio para Webhook

Se um webhook estiver configurado, a API envia um payload JSON com a URL da mídia (não Base64):

{
  "sessionId": "sua-sessao",
  "from": "5511999999999@c.us",
  "body": "Legenda",
  "type": "image",
  "mediaUrl": "/api/media/session-abc123_ABC123.jpg",
  "mediaMimetype": "image/jpeg",
  "mediaFilename": "foto.jpg",
  "timestamp": 1701108000
}

🔎 Como Verificar se uma Mensagem Tem Mídia

No Webhook:
// JavaScript/Node.js
if (payload.mediaUrl) {
  console.log('Mensagem tem mídia!');
  console.log('Tipo:', payload.mediaMimetype);
  console.log('URL:', payload.mediaUrl);
}
No Banco de Dados:
-- SQL: Buscar mensagens com mídia
SELECT * FROM messages 
WHERE direction = 'received' 
  AND media_url IS NOT NULL
  AND message_type != 'text'
ORDER BY timestamp DESC;

-- Verificar tipo específico
SELECT * FROM messages 
WHERE message_type LIKE 'image%'  -- Imagens
  OR message_type LIKE 'video%'    -- Vídeos
  OR message_type LIKE 'audio%';  -- Áudios
Via API (Polling):
// JavaScript
const response = await fetch(
  'https://app.autowhats.com.br/api/sessions/sua-sessao/messages',
  { headers: { 'X-API-Key': 'sua_api_key' } }
);
const data = await response.json();

data.messages.forEach(msg => {
  if (msg.mediaUrl) {
    console.log('Mensagem com mídia:', msg.type);
    console.log('URL:', msg.mediaUrl);
  }
});

Via Webhook (Recomendado)

Quando uma mídia é recebida, o webhook envia um payload com a URL do arquivo:

{
  "sessionId": "sua-sessao",
  "from": "5511999999999@c.us",
  "to": "5511888888888@c.us",
  "body": "Legenda da imagem (se houver)",
  "type": "image",
  "timestamp": 1701108000,
  "mediaUrl": "/api/media/session-001_ABC123.jpg",
  "mediaMimetype": "image/jpeg",
  "mediaFilename": "foto.jpg",
  "isGroup": false
}

📥 Como Acessar a Mídia:

O campo mediaUrl contém uma URL relativa. Para baixar a mídia, faça:

GET https://app.autowhats.com.br/api/media/session-001_ABC123.jpg

Via API (Polling)

Use GET /api/sessions/:id/messages para buscar mensagens com mídia:

GET https://app.autowhats.com.br/api/sessions/sua-sessao/messages
Headers: X-API-Key: sua_api_key

Resposta:

{
  "success": true,
  "messages": [
    {
      "id": "msg_123",
      "from": "5511999999999@c.us",
      "body": "Legenda",
      "type": "image",
      "mediaUrl": "/api/media/session-001_ABC123.jpg",
      "mediaMimetype": "image/jpeg",
      "mediaFilename": "foto.jpg",
      "timestamp": 1701108000000
    }
  ]
}

📁 Endpoint de Mídias

GET /api/media/:filename

Serve mídias armazenadas em disco. Este endpoint é público (não requer API Key) e pode ser usado diretamente em tags HTML ou para download.

GET https://app.autowhats.com.br/api/media/session-001_ABC123.jpg
Parâmetros:
  • :filename - Nome do arquivo de mídia (ex: session-001_ABC123.jpg)
Resposta:

Retorna o arquivo binário com os headers apropriados:

  • Content-Type: Tipo MIME detectado automaticamente
  • Content-Length: Tamanho do arquivo
  • Cache-Control: Cache por 1 ano (melhor performance)
  • Content-Disposition: inline (exibe no navegador)
Exemplos de Uso:

HTML (Exibir Imagem):

<img src="https://app.autowhats.com.br/api/media/session-001_ABC123.jpg" alt="Mídia">

JavaScript (Baixar Arquivo):

const mediaUrl = '/api/media/session-001_ABC123.jpg';
const fullUrl = `https://app.autowhats.com.br${mediaUrl}`;
const response = await fetch(fullUrl);
const blob = await response.blob();

// Criar URL para exibir
const imageUrl = URL.createObjectURL(blob);
document.getElementById('image').src = imageUrl;

// Ou baixar arquivo
const a = document.createElement('a');
a.href = imageUrl;
a.download = 'imagem.jpg';
a.click();

cURL (Baixar Arquivo):

curl https://app.autowhats.com.br/api/media/session-001_ABC123.jpg -o imagem.jpg

⚠️ Nota de Segurança: Este endpoint é público. Nomes de arquivo são validados para prevenir path traversal.

💻 Exemplos Práticos Completos

Enviar Imagem (JavaScript)

async function enviarImagem(sessionId, numero, urlImagem, legenda) {
  const response = await fetch('https://app.autowhats.com.br/api/messages/media', {
    method: 'POST',
    headers: {
      'X-API-Key': 'sua_api_key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      sessionId: sessionId,
      to: numero,
      mediaUrl: urlImagem,
      caption: legenda,
      mimetype: 'image/jpeg'
    })
  });
  
  const data = await response.json();
  if (data.success) {
    console.log('Imagem enviada!', data.messageId);
  }
  return data;
}

// Uso
enviarImagem('sua-sessao', '5511999999999', 'https://exemplo.com/foto.jpg', 'Olha essa foto!');

Enviar Vídeo (cURL)

curl -X POST https://app.autowhats.com.br/api/messages/media \
  -H "X-API-Key: sua_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "sua-sessao",
    "to": "5511999999999",
    "mediaUrl": "https://exemplo.com/video.mp4",
    "caption": "Assista este vídeo!",
    "mimetype": "video/mp4"
  }'

Enviar Áudio via Base64 (Python)

import requests
import base64

def enviar_audio(session_id, numero, caminho_arquivo, legenda=''):
    # Ler arquivo e converter para base64
    with open(caminho_arquivo, 'rb') as f:
        audio_base64 = base64.b64encode(f.read()).decode('utf-8')
        data_uri = f"data:audio/mpeg;base64,{audio_base64}"
    
    response = requests.post(
        'https://app.autowhats.com.br/api/messages/media',
        headers={'X-API-Key': 'sua_api_key'},
        json={
            'sessionId': session_id,
            'to': numero,
            'mediaBase64': data_uri,
            'caption': legenda,
            'mimetype': 'audio/mpeg',
            'filename': 'audio.mp3'
        }
    )
    
    return response.json()

# Uso
resultado = enviar_audio('sua-sessao', '5511999999999', 'audio.mp3', 'Escute isso!')
print(resultado)

📥 Fluxo Completo de Integração - Recebimento de Mídias

Passo a Passo para Integrar Recebimento de Mídias
1
Configurar Webhook

Configure o webhook da sua sessão para receber notificações em tempo real:

POST /api/sessions/sua-sessao/webhook
Headers: X-API-Key: sua_api_key
Body: { "webhookUrl": "https://seu-servidor.com/webhook" }
2
Receber Payload no Webhook

Quando uma mídia é recebida, você receberá um POST no seu webhook:

{
  "sessionId": "sua-sessao",
  "from": "5511999999999@c.us",
  "body": "Legenda (se houver)",
  "type": "image",
  "mediaUrl": "/api/media/session-abc123_ABC123.jpg",
  "mediaMimetype": "image/jpeg",
  "mediaFilename": "foto.jpg",
  "timestamp": 1701108000
}
3
Verificar se Tem Mídia

Sempre verifique se mediaUrl existe antes de processar:

if (payload.mediaUrl) {
  // Mensagem tem mídia!
  // Processar mídia...
} else {
  // Mensagem de texto normal
}
4
Baixar Mídia da API

Converta a URL relativa para absoluta e baixe o arquivo:

const fullUrl = `https://app.autowhats.com.br${payload.mediaUrl}`;
const response = await fetch(fullUrl);
const blob = await response.blob();
5
Processar ou Armazenar

Use o arquivo conforme sua necessidade (salvar, processar, exibir, etc.)

📥 Exemplos Completos - Receber e Processar Mídias

Node.js/Express - Webhook Completo
const express = require('express');
const fetch = require('node-fetch');
const fs = require('fs');
const app = express();

app.use(express.json());

app.post('/webhook', async (req, res) => {
  const payload = req.body;
  
  // Responder rapidamente para evitar timeout
  res.status(200).json({ received: true });
  
  // Verificar se mensagem tem mídia
  if (payload.mediaUrl) {
    console.log('📥 Mídia recebida!');
    console.log('Tipo:', payload.mediaMimetype);
    console.log('De:', payload.from);
    
    try {
      // Converter URL relativa para absoluta
      const fullUrl = `https://app.autowhats.com.br${payload.mediaUrl}`;
      
      // Baixar mídia
      const response = await fetch(fullUrl);
      if (!response.ok) {
        throw new Error(`Erro ao baixar: ${response.status}`);
      }
      
      const buffer = await response.buffer();
      const filename = payload.mediaFilename || `midia_${Date.now()}.${getExtension(payload.mediaMimetype)}`;
      
      // Salvar arquivo
      fs.writeFileSync(`./midias/${filename}`, buffer);
      console.log(`✅ Mídia salva: ${filename}`);
      
      // Processar conforme tipo
      if (payload.mediaMimetype.startsWith('image/')) {
        console.log('🖼️ Processando imagem...');
        // Ex: redimensionar, aplicar filtros, etc.
      } else if (payload.mediaMimetype.startsWith('video/')) {
        console.log('🎬 Processando vídeo...');
        // Ex: extrair thumbnail, converter formato, etc.
      } else if (payload.mediaMimetype.startsWith('audio/')) {
        console.log('🎵 Processando áudio...');
        // Ex: transcrever, analisar, etc.
      }
      
    } catch (error) {
      console.error('❌ Erro ao processar mídia:', error.message);
    }
  } else {
    // Mensagem de texto normal
    console.log('💬 Mensagem de texto:', payload.body);
  }
});

function getExtension(mimetype) {
  const map = {
    'image/jpeg': 'jpg',
    'image/png': 'png',
    'image/gif': 'gif',
    'image/webp': 'webp',
    'video/mp4': 'mp4',
    'audio/mpeg': 'mp3',
    'application/pdf': 'pdf'
  };
  return map[mimetype] || 'bin';
}

app.listen(3000, () => {
  console.log('Webhook rodando na porta 3000');
});
Python/Flask - Webhook Completo
from flask import Flask, request, jsonify
import requests
import os
from pathlib import Path

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.json
    
    # Responder rapidamente
    response = jsonify({'received': True})
    
    # Verificar se mensagem tem mídia
    if payload.get('mediaUrl'):
        print('📥 Mídia recebida!')
        print(f'Tipo: {payload.get("mediaMimetype")}')
        print(f'De: {payload.get("from")}')
        
        try:
            # Converter URL relativa para absoluta
            media_url = payload['mediaUrl']
            if media_url.startswith('/'):
                full_url = f'https://app.autowhats.com.br{media_url}'
            else:
                full_url = media_url
            
            # Baixar mídia
            response = requests.get(full_url)
            response.raise_for_status()
            
            # Criar diretório se não existir
            os.makedirs('midias', exist_ok=True)
            
            # Salvar arquivo
            filename = payload.get('mediaFilename') or f'midia_{int(time.time())}.{get_extension(payload.get("mediaMimetype"))}'
            filepath = Path('midias') / filename
            
            with open(filepath, 'wb') as f:
                f.write(response.content)
            
            print(f'✅ Mídia salva: {filepath}')
            
            # Processar conforme tipo
            mimetype = payload.get('mediaMimetype', '')
            if mimetype.startswith('image/'):
                print('🖼️ Processando imagem...')
                # Ex: redimensionar, aplicar filtros, etc.
            elif mimetype.startswith('video/'):
                print('🎬 Processando vídeo...')
                # Ex: extrair thumbnail, converter formato, etc.
            elif mimetype.startswith('audio/'):
                print('🎵 Processando áudio...')
                # Ex: transcrever, analisar, etc.
                
        except Exception as e:
            print(f'❌ Erro ao processar mídia: {str(e)}')
    else:
        # Mensagem de texto normal
        print(f'💬 Mensagem de texto: {payload.get("body")}')
    
    return response

def get_extension(mimetype):
    map_ext = {
        'image/jpeg': 'jpg',
        'image/png': 'png',
        'image/gif': 'gif',
        'image/webp': 'webp',
        'video/mp4': 'mp4',
        'audio/mpeg': 'mp3',
        'application/pdf': 'pdf'
    }
    return map_ext.get(mimetype, 'bin')

if __name__ == '__main__':
    app.run(port=3000, debug=True)
PHP - Webhook Completo
 true]);
flush();

// Verificar se mensagem tem mídia
if (isset($payload['mediaUrl']) && !empty($payload['mediaUrl'])) {
    error_log('📥 Mídia recebida!');
    error_log('Tipo: ' . ($payload['mediaMimetype'] ?? 'desconhecido'));
    error_log('De: ' . ($payload['from'] ?? 'desconhecido'));
    
    try {
        // Converter URL relativa para absoluta
        $mediaUrl = $payload['mediaUrl'];
        if (strpos($mediaUrl, '/') === 0) {
            $fullUrl = 'https://app.autowhats.com.br' . $mediaUrl;
        } else {
            $fullUrl = $mediaUrl;
        }
        
        // Baixar mídia
        $binaryData = file_get_contents($fullUrl);
        if ($binaryData === false) {
            throw new Exception('Erro ao baixar mídia');
        }
        
        // Criar diretório se não existir
        if (!is_dir('midias')) {
            mkdir('midias', 0755, true);
        }
        
        // Salvar arquivo
        $filename = $payload['mediaFilename'] ?? 'midia_' . time() . '.' . getExtension($payload['mediaMimetype'] ?? '');
        $filepath = 'midias/' . $filename;
        
        file_put_contents($filepath, $binaryData);
        error_log('✅ Mídia salva: ' . $filepath);
        
        // Processar conforme tipo
        $mimetype = $payload['mediaMimetype'] ?? '';
        if (strpos($mimetype, 'image/') === 0) {
            error_log('🖼️ Processando imagem...');
            // Ex: redimensionar, aplicar filtros, etc.
        } elseif (strpos($mimetype, 'video/') === 0) {
            error_log('🎬 Processando vídeo...');
            // Ex: extrair thumbnail, converter formato, etc.
        } elseif (strpos($mimetype, 'audio/') === 0) {
            error_log('🎵 Processando áudio...');
            // Ex: transcrever, analisar, etc.
        }
        
    } catch (Exception $e) {
        error_log('❌ Erro ao processar mídia: ' . $e->getMessage());
    }
} else {
    // Mensagem de texto normal
    error_log('💬 Mensagem de texto: ' . ($payload['body'] ?? ''));
}

function getExtension($mimetype) {
    $map = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/gif' => 'gif',
        'image/webp' => 'webp',
        'video/mp4' => 'mp4',
        'audio/mpeg' => 'mp3',
        'application/pdf' => 'pdf'
    ];
    return $map[$mimetype] ?? 'bin';
}
?>

💡 Dica Importante:

Sempre responda ao webhook rapidamente (200 OK) antes de processar a mídia. Isso evita timeouts e garante que a API saiba que a mensagem foi recebida.

Processar Mídia Recebida (PHP - Exemplo Simples)

 true]);
?>

Processar Mídia Recebida (Node.js)

const express = require('express');
const fetch = require('node-fetch');
const fs = require('fs');
const app = express();

app.use(express.json({ limit: '50mb' }));

app.post('/webhook', async (req, res) => {
  const { mediaUrl, mediaMimetype, mediaFilename, from, body } = req.body;
  
  if (mediaUrl) {
    // NOVO: mediaUrl agora é uma URL, não Base64
    // Baixar arquivo da API
    const fullUrl = 'https://app.autowhats.com.br' + mediaUrl;
    const response = await fetch(fullUrl);
    const buffer = await response.buffer();
    
    // Determinar extensão
    const ext = mediaMimetype.split('/')[1];
    const filename = mediaFilename || `midia_${Date.now()}.${ext}`;
    
    // Salvar arquivo
    fs.writeFileSync(`uploads/${filename}`, buffer);
    
    console.log(`Mídia recebida de ${from}: ${filename}`);
  }
  
  res.status(200).send('OK');
});

app.listen(3001);

❌ Erro Comum: Enviando Link ao Invés de Arquivo

🐛 Problema Identificado

Alguns aplicativos estão enviando URLs de mídia como mensagens de texto através do endpoint POST /api/messages/text, resultando em:

  • ❌ Destinatário recebe um link ao invés do arquivo
  • ❌ Mensagem salva no banco como message_type='text'
  • ❌ URL exibida como texto no WhatsApp (não a imagem/vídeo)
// ❌ ERRADO - Enviando URL como texto
POST /api/messages/text
{
"sessionId": "sua-sessao",
"to": "5511999999999",
"message": "📎 https://supabase.co/storage/.../imagem.png"
}
// Resultado: Link exibido no WhatsApp ❌

✅ Solução Correta

Para enviar mídia, você DEVE usar o endpoint POST /api/messages/media:

// ✅ CORRETO - Enviando mídia via endpoint correto
POST /api/messages/media
{
"sessionId": "sua-sessao",
"to": "5511999999999",
"mediaUrl": "https://supabase.co/storage/.../imagem.png",
"caption": "Legenda opcional"
}
// Resultado: Arquivo enviado corretamente ✅
Exemplo Completo (JavaScript):
// ❌ ERRADO - NÃO FAÇA ISSO
fetch('https://app.autowhats.com.br/api/messages/text', {
  method: 'POST',
  headers: {
    'X-API-Key': 'sua_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'sua-sessao',
    to: '5511999999999',
    message: '📎 https://supabase.co/storage/.../imagem.png'
  })
});
// Resultado: Link enviado como texto ❌

// ✅ CORRETO - USE ESTE MÉTODO
fetch('https://app.autowhats.com.br/api/messages/media', {
  method: 'POST',
  headers: {
    'X-API-Key': 'sua_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    sessionId: 'sua-sessao',
    to: '5511999999999',
    mediaUrl: 'https://supabase.co/storage/.../imagem.png',
    caption: 'Legenda opcional'
  })
});
// Resultado: Arquivo enviado corretamente ✅

⚠️ Importante:

  • NUNCA envie URLs de mídia através de /api/messages/text
  • SEMPRE use /api/messages/media para enviar mídias
  • URLs devem ser acessíveis publicamente (não localhost)
  • O campo mediaUrl deve conter apenas a URL, não texto adicional

📊 Como Verificar no Banco de Dados:

✅ Mensagens enviadas corretamente terão:

  • message_type = tipo de mídia (ex: 'image/png', 'video/mp4')
  • media_url = URL da mídia (se aplicável)
  • body = caption/legenda (não a URL completa)

❌ Mensagens enviadas incorretamente terão:

  • message_type = 'text'
  • media_url = NULL
  • body = URL completa da mídia (ex: "📎 https://supabase.co/.../imagem.png") ❌

Exemplo Real do Erro (ID 45):

message_type: 'text'
body: '📎 https://mgmrqskvwztsmcoksbgq.supabase.co/storage/.../imagem.png'
media_url: NULL
media_mimetype: NULL

Deveria ser:

message_type: 'image/png'
body: 'Legenda' (ou vazio)
media_url: 'https://mgmrqskvwztsmcoksbgq.supabase.co/...'
media_mimetype: 'image/png'

🗄️ Verificação no Banco de Dados

Como verificar se uma mensagem recebida contém mídia consultando diretamente o banco de dados MySQL.

Estrutura da Tabela messages

Campos importantes para identificar mídias recebidas:

Campo Tipo Descrição
direction VARCHAR 'received' = recebida, 'sent' = enviada
message_type VARCHAR Tipo da mensagem: 'text', 'image', 'video', 'audio', 'document' ou MIME type completo
media_url VARCHAR URL da mídia (ex: /api/media/session-abc123_ABC123.jpg) ou NULL
media_mimetype VARCHAR Tipo MIME da mídia (ex: 'image/jpeg') ou NULL
media_filename VARCHAR Nome original do arquivo (se disponível) ou NULL
body TEXT Texto da mensagem ou legenda da mídia

✅ Mensagem com Mídia (Correta)

Uma mensagem recebida com mídia terá os seguintes valores:

direction: 'received'
message_type: 'image'  OU  'image/jpeg'  OU  'image/png'  etc.
media_url: '/api/media/session-abc123_ABC123.jpg'  (NÃO NULL)
media_mimetype: 'image/jpeg'  (NÃO NULL)
media_filename: 'foto.jpg'  (pode ser NULL)
body: 'Legenda da imagem'  (ou vazio se não houver legenda)

Query para buscar mensagens com mídia:

SELECT * FROM messages 
WHERE direction = 'received' 
  AND media_url IS NOT NULL
  AND message_type != 'text'
ORDER BY timestamp DESC;

❌ Mensagem sem Mídia (Texto Normal)

Uma mensagem de texto normal terá:

direction: 'received'
message_type: 'text'
media_url: NULL
media_mimetype: NULL
media_filename: NULL
body: 'Texto da mensagem'

⚠️ Exemplo Real - Mensagem ID 51 (Erro)

A mensagem ID 51 foi enviada incorretamente como texto quando deveria ser mídia:

id: 51
direction: 'sent'
message_type: 'text'  ❌ (deveria ser 'image/webp')
media_url: NULL  ❌ (deveria ter URL)
media_mimetype: NULL  ❌ (deveria ser 'image/webp')
body: '? https://supabase.co/.../imagem.webp'  ❌ (URL no body)

Problema: A URL de mídia foi enviada como mensagem de texto ao invés de usar o endpoint /api/messages/media.

Solução: A API agora detecta automaticamente URLs de mídia em mensagens de texto e converte para envio de mídia.

📊 Queries Úteis

Buscar todas as mídias recebidas:

SELECT id, session_id, from_number, message_type, 
       media_url, media_mimetype, media_filename, 
       body, timestamp
FROM messages 
WHERE direction = 'received' 
  AND media_url IS NOT NULL
ORDER BY timestamp DESC;

Contar mídias por tipo:

SELECT 
  CASE 
    WHEN message_type LIKE 'image%' THEN 'Imagem'
    WHEN message_type LIKE 'video%' THEN 'Vídeo'
    WHEN message_type LIKE 'audio%' THEN 'Áudio'
    WHEN message_type LIKE 'application%' THEN 'Documento'
    ELSE 'Outro'
  END as tipo,
  COUNT(*) as total
FROM messages 
WHERE direction = 'received' 
  AND media_url IS NOT NULL
GROUP BY tipo
ORDER BY total DESC;

Buscar mídias de uma sessão específica:

SELECT * FROM messages 
WHERE session_id = 'sua-sessao'
  AND direction = 'received'
  AND media_url IS NOT NULL
ORDER BY timestamp DESC
LIMIT 50;

🔧 Solução de Problemas

❌ Erro: "Não foi possível baixar a mídia da URL fornecida"

  • Verifique se a URL é acessível publicamente (não localhost ou IPs privados)
  • Teste a URL no navegador: curl -I https://sua-url.com/imagem.jpg
  • Certifique-se de que a URL retorna o arquivo diretamente (não HTML)
  • Verifique se o servidor permite download (CORS)

❌ Destinatário recebe link ao invés de arquivo

Causa mais comum: Você está enviando a URL através de /api/messages/text ao invés de /api/messages/media.

  • Verifique qual endpoint você está usando - deve ser /api/messages/media
  • Não envie URLs como mensagem de texto - sempre use o endpoint de mídia
  • Verifique os logs da API: pm2 logs whatsapp-api | grep -i "mídia\|enviando"
  • Certifique-se de que a URL é acessível e retorna o arquivo binário
  • Use mediaBase64 como alternativa se a URL não funcionar
  • Verifique se o mimetype está correto

Exemplo do Erro no Banco:

message_type='text', body='📎 https://supabase.co/.../imagem.png'

Deveria ser: message_type='image/png', body='Legenda'

ℹ️ Mídia não aparece no WhatsApp

  • Verifique o mimetype - deve ser exato (ex: image/jpeg, não image/jpg)
  • Certifique-se de que o Base64 está completo e válido
  • Verifique os logs do PM2: pm2 logs whatsapp-api
  • Verifique se a sessão está conectada: GET /api/sessions/:id/status

✅ Webhook não recebe mídia

  • Verifique se o webhookUrl está configurado corretamente
  • Certifique-se de que seu servidor webhook está acessível publicamente
  • Verifique os logs da API para erros de webhook
  • O campo mediaUrl agora contém uma URL, não Base64

💡 Boas Práticas

Use URLs para arquivos grandes

Para arquivos > 5MB, sempre use mediaUrl ao invés de mediaBase64

Use Base64 para arquivos pequenos

Para arquivos < 1MB, mediaBase64 é mais simples e direto

Sempre especifique o mimetype

Embora opcional, especificar o mimetype garante melhor compatibilidade

Use webhooks para receber mídias

Webhooks são mais eficientes que polling para receber mídias em tempo real

Baixe mídias recebidas quando necessário

Mídias são armazenadas em disco. Baixe apenas quando precisar processar