API WhatsApp · Referencia Interativa · BETA
BETA

WhatsApp API v2 Reference

Documentacao interativa gerada a partir da spec OpenAPI 3.1. Clique em qualquer endpoint para ver parâmetros, exemplos de request e response. Copie com um clique.

Documentacao em desenvolvimento (BETA): Esta documentacao esta em construcao. Alguns endpoints, campos e exemplos podem estar incompletos ou faltar. Reporte inconsistencias via WhatsApp 5551980331519.
--
ENDPOINTS
--
CATEGORIAS
3
MODOS DE CONEXAO
120s
TTL PAIRING CODE
AUTH
Autenticação
Todas as rotas exigem o token de API no header Authorization: {seu_token} (sem prefixo "Bearer "). Tokens são gerados no painel do usuário.
BASH
# Header correto: Authorization + token direto (SEM "Bearer") curl -X POST https://apifacil.dev/api/v2/whatsapp/mensagem/1/enviar \ -H "Authorization: SEU_TOKEN_AQUI" \ -H "Content-Type: application/json" \ -d '{"to":"5511999999999","type":"text","content":"Olá, como vai?"}'
Erro comum: Enviar Authorization: Bearer SEU_TOKEN retorna 401. O middleware valida o token direto no header — não use prefixo "Bearer ".
CONN
3 Modalidades de Conexão
Escolha o melhor metodo para seu caso
1 · QR Code
POST /iniciar → GET /qrcode → escanear
2 · Pairing Code
POST /solicitar-pareamento → digitar código no celular
3 · Importar Sessão
POST /importar-sessão → conecta automatico
1
QR Code (tradicional)
Conexão por escanear código — ideal para onboarding manual
Plano: Todos Tempo: ~30s

O usuário abre o WhatsApp do celular, vai em Configurações › Aparelhos conectados › Conectar aparelho e escaneia o QR Code exibido pela API. A cada chamada de /iniciar um novo QR Code e gerado (validade ~60s, renova automaticamente).

1
Criar instância
POST /instancia/criar
2
Iniciar sessão (gera QR)
POST /{id}/iniciar
3
Poll QR Code
GET /{id}/qrcode
4
Exibir QR para usuário escanear
5
Verificar status
GET /{id}/status

Exemplo completo em JavaScript

JAVASCRIPT · FLUXO COMPLETO
// 1. Iniciar sessão (gera o QR Code) await fetch('https://apifacil.dev/api/v2/whatsapp/instancia/1/iniciar', { method: 'POST', headers: { 'Authorization': 'SEU_TOKEN' } }); // 2. Poll QR Code a cada 3s até obter async function obterQRCode() { for (let i = 0; i < 20; i++) { const res = await fetch('https://apifacil.dev/api/v2/whatsapp/instancia/1/qrcode', { headers: { 'Authorization': 'SEU_TOKEN' } }); const json = await res.json(); if (json.data?.qr_code) { // data.qr_code = "2@/AW1mZXNhc3dhcmUuY29t..." console.log('QR Code obtido!'); return; } await new Promise(r => setTimeout(r, 3000)); } } // 3. Verificar status após escanear async function verificarStatus() { const res = await fetch('https://apifacil.dev/api/v2/whatsapp/instancia/1/status', { headers: { 'Authorization': 'SEU_TOKEN' } }); const json = await res.json(); // json.data.status_banco = "connected" | "disconnected" | "connecting" if (json.data.status_banco === 'connected') { console.log('Conectado!'); } }
Dica: O QR Code expira em ~60 segundos. Se expirar, chame /iniciar novamente para gerar um novo. A renovação é automática enquanto a sessão estiver aberta.
2
Pairing Code (código de pareamento)
Conexão por código de 8 dígitos — ideal para integracoes server-to-server e onboarding sem camera
Plano: Todos TTL: 120s Formato: 8 chars (ABC12345)

O parceiro envia o número de telefone do cliente. A API inicia a sessão e solicita o código ao WhatsApp. O cliente digita o código manualmente no celular: Configurações › Aparelhos conectados › Conectar com código de telefone. O código expira em 120 segundos.

Importante: Não e necessário chamar /iniciar antes — o endpoint /solicitar-pareamento já inicia a sessão automaticamente. Se você chamar /iniciar antes, pode conflitar com a sessão recem-iniciada.
1
Solicitar código
POST /{id}/solicitar-pareamento
body: {"phone":"5511999999999"}
2
API inicia sessão
start-session
3
API pede código ao WhatsApp
4
Retorna código
"W4NQJMRD" (8 chars)
5
Cliente digita no celular
Config › Aparelhos › Conectar com código

Formato do telefone

O telefone deve conter DDI + DDD + número, apenas dígitos, 10 a 15 caracteres. Regex validado: ^\d{10,15}$.

EXEMPLOFORMATOVALIDO?
5511999999999DDI+DDD+númeroSIM
55119999999911 dígitosSIM
11999999999sem DDINÃO (10 dígitos ok mas sem DDI pode falhar)
+55 11 99999-9999com mascaraNÃO (regex rejeita)

Exemplo completo em JavaScript (com polling)

JAVASCRIPT · FLUXO COMPLETO
// 1. Solicitar código de pareamento async function solicitarPareamento(instanciaId, phone) { const res = await fetch( `https://apifacil.dev/api/v2/whatsapp/instancia/${instanciaId}/solicitar-pareamento`, { method: 'POST', headers: { 'Authorization': 'SEU_TOKEN', 'Content-Type': 'application/json' }, body: JSON.stringify({ phone }) // "5511999999999" } ); const json = await res.json(); if (json.error) { throw new Error(json.message); } // json.data = { pairingCode: "W4NQJMRD" } return json.data; } // 2. Mostrar código para cliente digitar no celular const { pairingCode } = await solicitarPareamento(1, '5511999999999'); console.log(`Código: ${pairingCode} (expira em 120s)`); // > "Código: W4NQJMRD (expira em 120s)" // Cliente vai no celular: Config › Aparelhos conectados › Conectar com código // 3. Poll de status para saber quando conectar async function aguardarConexao(instanciaId, timeoutMs = 120000) { const start = Date.now(); while (Date.now() - start < timeoutMs) { const res = await fetch( `https://apifacil.dev/api/v2/whatsapp/instancia/${instanciaId}/status`, { headers: { 'Authorization': 'SEU_TOKEN' } } ); const json = await res.json(); if (json.data?.status_banco === 'connected') { console.log('Conectado com sucesso!'); return true; } await new Promise(r => setTimeout(r, 3000)); // a cada 3s } return false; // timeout } const conectou = await aguardarConexao(1); if (!conectou) { console.log('Código expirou. Solicite um novo.'); } // 4. (Opcional) Recuperar código atual const res = await fetch( 'https://apifacil.dev/api/v2/whatsapp/instancia/1/codigo-pareamento', { headers: { 'Authorization': 'SEU_TOKEN' } } ); const json = await res.json(); // json.data = { pairing_code: "W4NQJMRD" } // se expirado: 404 com mensagem "Código de pareamento não disponível."

Troubleshooting

SINTOMA
CAUSA / SOLUCAO
"Erro ao solicitar código de pareamento"
Microserviço offline ou não respondeu em 10s. Verifique se o servidor da instância esta acessível. Tente novamente.
"O serviço de conexão não retornou o código"
WhatsApp recusou o pareamento. Possiveis causas: número inválido, número sem conta WhatsApp, ou sessão anterior não foi fechada corretamente. Aguarde 30s e tente novamente.
Código retornado mas cliente não consegue digitar
Código expirou (120s). Solicite um novo via /solicitar-pareamento. Não use /código-pareamento para gerar novo — ele so consulta o código já gerado.
Status fica "connecting" para sempre
Cliente digitou código errado ou não confirmou no celular. Peça para refazer o fluxo. Se persistir, chame /desconectar e recomece.
"Sessão já esta ativa. Desconecte antes..."
Instância já conectada. Se quer re-parear, chame POST /{id}/desconectar antes.
telefone inválido (422)
Regex ^\d{10,15}$ rejeitou. Envie apenas dígitos, com DDI+DDD+número. Ex: "5511999999999".
Quando usar Pairing Code vs QR Code?
Pairing Code: integracoes server-to-server, onboarding remoto (sem camera), apps mobile que mandam o código via push.
QR Code: onboarding presencial, web apps com camera, fluxo manual.
3
Importar Sessão (WebAuth)
Importa credenciais exportadas pela extensao WhatsApp Session Extractor — ideal para migrar sessões entre instâncias sem re-escanear
Plano: Todos Auto-connect

Importa uma sessão que já foi pareada antes. A sessão conecta automaticamente após a importacao, sem precisar de QR Code ou pairing code.

Vantagem: Ideal para reconectar uma instância ou migrar a sessão sem re-parear.
1
Solicitar importacao
POST /{id}/importar-sessão
2
A plataforma recupera a sessão guardada
sem QR Code
3
Instância conecta sozinha
não precisa /iniciar

Exemplo completo em JavaScript

JAVASCRIPT · FLUXO COMPLETO
// 1. Enviar solicitacao de importacao para a API const res = await fetch( 'https://apifacil.dev/api/v2/whatsapp/instancia/1/importar-sessao', { method: 'POST', headers: { 'Authorization': 'SEU_TOKEN', 'Content-Type': 'application/json' } } ); const json = await res.json(); if (json.error) { console.error(json.message); return; } console.log(json.message); // > "Sessão importada" // 2. Poll de status (conecta automaticamente em ~5-10s) async function aguardarConexao(instanciaId) { for (let i = 0; i < 30; i++) { const res = await fetch( `https://apifacil.dev/api/v2/whatsapp/instancia/${instanciaId}/status`, { headers: { 'Authorization': 'SEU_TOKEN' } } ); const json = await res.json(); if (json.data?.status_banco === 'connected') { console.log('Sessão importada e conectada!'); return true; } await new Promise(r => setTimeout(r, 2000)); } return false; } const ok = await aguardarConexao(1);

Exemplo em PHP (Laravel)

PHP · LARAVEL HTTP
// Solicitar importacao da sessão guardada $response = Http::withHeaders([ 'Authorization' => 'SEU_TOKEN', ])->post('https://apifacil.dev/api/v2/whatsapp/instancia/1/importar-sessao'); if ($response->successful()) { $data = $response->json(); // $data['message'] = "Sessão importada" }

Troubleshooting

SINTOMA
CAUSA / SOLUCAO
Importa mas status fica "disconnected"
Credenciais expiradas ou invalidas. Re-inicie a sessão e pareie novamente.
"Erro ao importar sessão" (500)
Microserviço offline ou timeout. Verifique o servidor da instância.
Conecta mas cai depois de alguns minutos
WhatsApp detectou sessão duplicada. Desconecte outras sessões antes de importar.

QR Code vs Pairing Code vs Importar Sessão

CRITERIOQR CodePairing CodeImportar Sessão
Requer celular em maosSIMSIMNÃO
Requer cameraSIMNÃONÃO
Requer sessão pareada antesNÃONÃOSIM
Tempo medio de conexão~30s~15s~5-10s
Reconectar após restartNÃONÃOSIM
Onboarding server-to-serverNÃOSIMSIM
Onboarding mobile appSIMSIMNÃO
Atencao: A importacao de sessão so funciona se houver uma sessão valida guardada. Se a sessão foi removida (logout/desconectar), será necessário re-parear via QR Code ou Pairing Code.
Carregando endpoints
HOOK
Webhooks
Eventos enviados para sua URL

Configure via PUT /whatsapp/instancia/{id}/configuracao. A plataforma envia POST para sua URL com Content-Type: application/json, timeout 10s.

URLs de webhook (roteamento)

CAMPODESTINOUSA?
webhook_urlMensagens privadas (message_sent + message_received)SIM
webhook_grupoMensagens de grupoSIM
webhook_ativoLiga/desliga TODOS os webhooks (master switch)SIM

Bloqueio por tipo de mensagem (filtros)

Você pode impedir que certos tipos de mensagem disparem o webhook. Util para ignorar stickers, áudios longos, localizações, etc. Configure o campo tipos_envio dentro de config_json (via PUT /whatsapp/instancia/{id}/configuracao).

O campo tipos_envio recebe um array de tipos. Toda mensagem cujo tipo esteja nesse array será salva no banco (com status skipped) mas não disparará webhook. Comportamento idêntico ao painel de configuração da instância.

VALOR DE tipoDESCRICAO
textMensagem de texto
imageImagem
vídeoVídeo
áudioÁudio (nota de voz ou arquivo)
documentDocumento (PDF, DOC, etc)
stickerSticker / figurinha
locationLocalização
Atencao: O valor comparado pelo filtro e o campo tipo do payload (em minúsculas: text, image, etc). Não confunda com os nomes em maiúsculas usados na API v1 (MENSAGEM_ENVIADA, AUDIO_RECEBIDO). Na v2 os valores são os mesmos do campo tipo retornado no webhook.
BASH · bloquear stickers e localizações
PUT https://apifacil.dev/api/v2/whatsapp/instancia/{id}/configuracao \ -H "Authorization: SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tipos_envio": ["sticker", "location"] }'
JSON · resposta (config_json atualizado)
{ "error": false, "message": "Configuração atualizada", "data": { "config_json": { "tipos_envio": ["sticker", "location"] } } }
Limpar filtro: Envie "tipos_envio": [] para remover todos os bloqueios e voltar a receber todos os tipos via webhook. As mensagens bloqueadas continuam sendo salvas no banco e podem ser consultadas via GET /whatsapp/mensagens.

Eventos de webhook

EVENTODESCRICAOQUANDO
message_sentMensagem enviada por vocêApós envio via API ou diretamente pelo celular
message_receivedMensagem recebida do contatoChega no WhatsApp do número conectado
message_statusAtualização de status (delivered/read/played)Quando o destinatário recebe, le ou reproduz a mídia
message_errorErro ao enviar mensagemFalha no envio (número inválido, sem conexão, etc)

O campo from_me indica a direção. O campo status indica o estado atual (pending, sent, delivered, read, played, failed). Em replies, os campos quoted_message_id e quoted_participant identificam a mensagem original.

JSON · webhook payload (message_received)
{ "event": "message_received", "mensagem_id": 123, "instancia_id": 1, "message_id": "3EB0123456789ABCDEF", "remote_jid": "5511999999999@s.whatsapp.net", "remote_jid_alt": null, "from_me": false, "participant": null, "tipo": "text", "conteúdo": "Olá, como vai?", "caption": null, "media_url": null, "quoted_message_id": null, "quoted_participant": null, "status": "delivered", "push_name": "João Silva", "timestamp": "2026-07-24T12:00:00.000Z" }

Exemplo: webhook message_status (receipt)

JSON · webhook payload (message_status)
{ "event": "message_status", "mensagem_id": 123, "instancia_id": 1, "message_id": "3EB0123456789ABCDEF", "remote_jid": "5511999999999@s.whatsapp.net", "from_me": true, "tipo": "text", "status": "read", "timestamp": "2026-07-24T12:01:00.000Z" }

Exemplo: webhook message_received com reply (quoted)

JSON · webhook payload (reply)
{ "event": "message_received", "mensagem_id": 125, "instancia_id": 1, "message_id": "3EB0998877665544332211", "remote_jid": "5511999999999@s.whatsapp.net", "from_me": false, "tipo": "text", "conteúdo": "Respondendo sua mensagem", "quoted_message_id": "3EB0123456789ABCDEF", "quoted_participant": "5511999999999@s.whatsapp.net", "status": "delivered", "timestamp": "2026-07-24T12:02:00.000Z" }

Valores de status (campo status)

STATUSDESCRICAO
pendingMensagem na fila aguardando envio
sentMensagem enviada para o WhatsApp
deliveredMensagem entregue no dispositivo
readMensagem lida pelo destinatário
playedMensagem de áudio/visual reproduzida
failedFalha ao enviar

Sistema de retry

Retries acontecem em 1min, 5min e 30min em caso de falha (total 4 tentativas em ~36min). Após falha na 4a tentativa: marca erro permanente.

Exemplo: receptor de webhook em Node.js (Express)

JAVASCRIPT · EXPRESS
const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhook', (req, res) => { const { event, mensagem_id, message_id, tipo, conteúdo, from_me, status, remote_jid } = req.body; console.log(`Evento: ${event}, Tipo: ${tipo}, Status: ${status}`); console.log(`De: ${remote_jid}, Enviada por mim: ${from_me}`); if (!from_me && tipo === 'text') { // Mensagem recebida do contato console.log(`Mensagem recebida: ${conteúdo}`); } // Responder 2xx rapidamente (timeout 10s no remetente) res.status(200).json({ received: true }); }); app.listen(3000, () => console.log('Webhook receiver on :3000'));

Exemplo: receptor em PHP (Laravel)

PHP · LARAVEL
// routes/web.php Route::post('/webhook', function (Request $req) { $event = $req->input('event'); $tipo = $req->input('tipo'); $conteúdo = $req->input('conteúdo'); $fromMe = $req->input('from_me'); $status = $req->input('status'); if (!$fromMe && $tipo === 'text') { // Processar mensagem recebida Log::info("Mensagem recebida: {$conteúdo}"); } return response()->json(['received' => true], 200); });
Dica: Seu receptor deve responder 2xx em até 10 segundos (timeout do remetente). Se demorar mais, a entrega é considerada falha e um retry é agendado. Processamento pesado deve ser assíncrono (fila/queue).
CONF
Configurações
Persistência de mensagens, mídia e opções avançadas

Configure via PUT /whatsapp/instancia/{id}/configuracao.

CAMPOTIPODESCRIÇÃO
webhook_urlstringURL do webhook para mensagens privadas
webhook_grupostringURL do webhook para grupos
webhook_ativobooleanLiga/desliga TODOS os webhooks (master switch)
salvar_mensagensbooleanDefault true. Quando false, mensagens recebidas (origem=direto) não são gravadas no banco. O webhook continua disparando com mensagem_id: 0. Mensagens enviadas via API continuam salvas para auditoria.
salvar_midiabooleanDefault true. Quando true, baixa mídia recebida (áudio/vídeo/imagem/documento) e armazena, gravando a URL no campo media_url. Quando false, a mídia não é baixada e media_url fica null.
config_jsonobjectConfigurações avançadas. tipos_envio: array de tipos que NÃO disparam webhook.
BASH · desativar persistência de mensagens e mídia
PUT https://apifacil.dev/api/v2/whatsapp/instancia/{id}/configuracao \ -H "Authorization: SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "salvar_mensagens": false, "salvar_midia": false }'
Dica: Use salvar_mensagens: false quando o cliente não quiser que as conversas sejam armazenadas (LGPD/compliance). O webhook continua funcionando — apenas o banco não persiste as mensagens recebidas.
PLAN
Planos e Recursos
RecursoFreePago
Mensagens de textoSIMSIM
WebhooksNÃOSIM
Envio de imagensNÃOSIM
Envio de vídeosNÃOSIM
Envio de arquivosNÃOSIM
Envio de áudiosNÃOSIM
Envio de localizaçãoNÃOSIM
SAFE
Boas Praticas e Anti-Ban
Evite restrições e banimentos
Importante: O WhatsApp monitora padrões de comportamento para detectar spam. Números novos e contas com pouca atividade são especialmente sensíveis. Siga estas orientações para reduzir o risco de restrições.
1. Aqueça a conta antes de enviar em massa

Relatos indicam que números novos estão sendo banidos logo no primeiro envio. Antes de começar a disparar mensagens em massa, aqueça a conta:

  • Interaja de forma orgânica e gradual por alguns dias (conversas reais, respostas, grupos).
  • Adicione foto de perfil, bio e nome configurados.
  • Converse com contatos que já tem seu número salvo.
  • Evite o primeiro envio sendo uma campanha em massa.
2. Evite comportamento de spam
  • Não envie muitas mensagens de uma vez. Distribua os envios ao longo do dia.
  • Tenha cuidado redobrado com números estrangeiros (DDI diferente do seu), pois isso aumenta significativamente o risco de restrições.
  • Varie o conteúdo. Mensagens idênticas repetidas disparam alertas.
  • Priorize contatos que já interagiram com você (opt-in).
3. O que fazer após uma restrição/banimento

Se o número tomou restrição, aguarde um periodo de descanso antes de voltar a enviar. Embora deixar o número parado por ~15 dias ajude, o foco principal deve ser evitar o comportamento que causou o banimento inicial.

  • Pare os envios em massa imediatamente.
  • Deixe a conta em descanso por pelo menos 7 a 15 dias.
  • Quando retornar, recomece de forma lenta e orgânica (aquecimento novamente).
  • Revise o volume, horários e tipos de mensagens que estavam sendo enviados.
  • Se o banimento se repetir, considere trocar o número e começar do zero com aquecimento adequado.
Resumo: Aqueça → envie gradual → evite números estrangeiros em massa → se tomar restrição, descanse e mude o comportamento antes de voltar.