Documentação
Guia completo da API REST do wappmeow e do SDK oficial. A API roda em /v1, responde JSON e transmite eventos por SSE. Respostas de sucesso vêm em { "data": ... } e erros em { "error": { "code", "message" } }.
Introdução
O wappmeow conecta números de WhatsApp como dispositivos vinculados (multidevice), igual ao WhatsApp Web: o celular não precisa ficar online depois do pareamento. Cada número é uma sessão; você cria sessões, pareia, envia/recebe mensagens e escuta eventos — tudo por HTTP.
- Base URL (produção):
https://web.lagoshost.com.br— a URL oficial, já embutida no SDK. Em desenvolvimento local, usehttp://localhost:3000. - Versão da API: prefixo
/v1(ex.:GET /v1/sessions). - Saúde:
GET /health— público, retorna nome, versão e grupos de recursos. - JIDs: usuários são
5511999999999@s.whatsapp.net, grupos...@g.us, canais...@newsletter, LIDs...@lid.
curl https://web.lagoshost.com.br/health
# { "name": "wappmeow", "version": "...", "features": { ... } }Instalação & servidor
Requer Node ≥ 22. No diretório do projeto:
npm install
npm startA configuração vai num arquivo .env na raiz do projeto — copie o modelo .env.example e ajuste. O servidor carrega o .env automaticamente (via dotenv), então funciona igual com npm start ou rodando node src/server.js direto:
cp .env.example .env
# edite o .env: PORT, HOST, WAPPMEOW_STORE_KEY...
npm startVariáveis de ambiente principais:
| Variável | Para quê |
|---|---|
PORT / HOST | Porta e endereço de escuta (padrão 3000 em 127.0.0.1). |
WAPPMEOW_STORE_KEY | Chave de 32 bytes (hex de openssl rand -hex 32) que criptografa os bundles das sessões em disco. Sem ela, as sessões vivem só em memória e as rotas de pareamento não iniciam. |
WAPPMEOW_DEVICE_STORE_DIR | Diretório dos dispositivos persistidos (ex.: ./data/devices). |
Autenticação
Não há API token. Por design, a API é aberta — qualquer cliente que alcança o servidor pode usar as rotas /v1/*. Isso simplifica bots e integrações internas, mas significa que quem protege o acesso é a sua rede: firewall, VPN ou reverse proxy autenticado na frente. Nunca exponha a porta crua na internet.
SDK oficial (zero-config)
O pacote @eduh_dev021/wappmeow-sdk é um cliente TypeScript da REST API (ESM + CommonJS + tipos). A URL de produção já está embutida no módulo — em produção você não configura nada:
import { WappMeowSDK } from '@eduh_dev021/wappmeow-sdk';
const sdk = new WappMeowSDK(); // produção: https://web.lagoshost.com.br (embutida) 🐱
// dev local: new WappMeowSDK({ baseUrl: 'http://localhost:3000' })
const { session } = await sdk.createSession({ label: 'meu-bot' });
session.on('message', (m) => console.log(m.kind, m.text ?? m.decoded));
await session.messages.text('5511999999999@s.whatsapp.net', 'oi! 🐾');Namespaces do handle de sessão: pair, messages, media, groups, users, presence, privacy, profile, chats, status, newsletters, appState, account, bots, broadcastLists, calls, labels, commerce, store, server, wam, mobile e events (SSE) — espelho 1:1 das rotas abaixo. A constante exportada DEFAULT_BASE_URL vale https://web.lagoshost.com.br. Erros viram WappMeowApiError com code/status do servidor.
Teste rápido (ping)
Pra validar servidor, sessão e pareamento sem escrever um bot inteiro:
await sdk.ping(); // { ok, latencyMs, health }
await session.ping(); // sessão conectada/pareada?
await session.ping(jid); // + envia mensagem de teste
session.enablePingPong(); // mande "ping" no WhatsApp → "🏓 pong"No pacote do SDK há o exemplo pronto: npm run ping (aceita SESSION_ID, TO e PONG=1). E o script de pareamento npm run pair:phone -- +5511999999999 liga o ping-pong sozinho logo após o login (PONG=0 desliga).
Gerenciar sessões
Cria uma sessão. Corpo opcional: { "label": "meu-bot", "browser": "chrome-ubuntu", "os": "Ubuntu", "waVersion": "auto" }. Sem browser, a identidade é a nativa do whatsmeow (igual ao framework Go); waVersion escolhe a versão do WhatsApp Web gravada no pareamento ("auto" = mais recente, via GET /wa-web-version; ou defina WAPPMEOW_WA_VERSION no servidor). Opções v0.8: ignoredJids (suprime o evento message para JIDs exatos ou sufixo @servidor), syncFullHistory: false (não baixa o histórico completo) e proxyUrl (proxy HTTP(S) só desta sessão). Retorna 201 com o id gerado.
Lista as sessões (id, label, status, jid, pushName…).
Detalhes de uma sessão.
Remove a sessão e apaga os dados locais dela.
Abre o transporte (login se já pareada; fluxo de pareamento se não).
Fecha o transporte sem desvincular.
Desvincula o dispositivo da conta (remove-companion-device) e desconecta.
Marca o cliente passivo/ativo: { "passive": true }.
unpaired → pairing_pending → connected/authenticated. Rotas de recurso retornam 409 enquanto a sessão não está pareada/conectada.Pareamento (QR · código · passkey)
Inicia o fluxo de QR e retorna o conteúdo do QR atual (acompanhe rotações pelo evento qr-rotated / pairing-code* no SSE).
Pareamento por código de 8 dígitos: { "phoneNumber": "5511999999999" } (opcionais: customPairCode, clientDisplayName). Renovação automática enquanto pendente. A notificação no celular ("um dispositivo quer se conectar") é pedida com showPushNotification: true (padrão), mas o WhatsApp pode segurá-la (app aberto, vários pareamentos seguidos, bateria) — nesse caso é só abrir Dispositivos conectados > Conectar > com número e digitar o código.
Cancela o pedido de código pendente.
Opções WebAuthn do fluxo de pareamento por passkey.
Envia a assertion da passkey: { "assertion": { ... } }.
Confirma o handoff da passkey após o evento pair-passkey-confirmation.
Enviar mensagens
Tudo passa por um único endpoint com conteúdo tipado — ou escape total com custom/rawMessageBase64:
Campos: to (JID) + um de text, media, poll, location, contact, interactive/buttons/list, forward, album, product, order, groupInvite, buttonReply, listReply, event, pin, keep, limitSharing, sharePhoneNumber, requestPhoneNumber, custom, rawMessageBase64. Extras: quoted, mentions[], linkPreview (true = OpenGraph automático), waitForAckMs.
curl -X POST https://web.lagoshost.com.br/v1/sessions/$ID/messages -d '{
"to": "5511999999999@s.whatsapp.net",
"text": "Olá do wappmeow! 🐾"
}'
# mídia: { "to": ..., "media": { "kind": "image", "dataBase64": "...", "caption": "foto" } }
# enquete:{ "to": ..., "poll": { "question": "Pizza hoje?", "options": ["sim", "não"] } }
# custom: { "to": ..., "custom": { "eventMessage": { "name": "Festa", "startTime": 1735689600 } } }Botões, mídia especial e mais (mesmo endpoint)
# Botões NativeFlow (tipos: reply, url, call, copy, list, location):
# { "to": ..., "interactive": { "text": "Escolha uma opção:", "footer": "toque em um botão",
# "buttons": [
# { "type": "reply", "text": "Cardápio", "id": "menu" },
# { "type": "url", "text": "Abrir site", "url": "https://..." },
# { "type": "copy", "text": "Copiar cupom", "copyCode": "GATO10" } ] } }
# O clique volta no SSE como message kind "interactiveResponse" (nativeFlowReply.id).
#
# Botões legados: { "buttons": { "text": "...", "buttons": [{ "id", "text" }] } } (máx. 3)
# Lista legada: { "list": { "title", "text", "buttonText", "sections": [...] } }
#
# Mídia view-once: { "media": { "kind": "image", "dataBase64": "...", "viewOnce": true } }
# Encaminhar: { "forward": { "rawBase64": "<msg.rawBase64 do evento>" } }
# Álbum: { "album": { "media": [ { "kind": "image", "dataBase64": "..." }, ... ] } }
# Evento: { "event": { "name": "Festa", "startTime": 1735689600 } }
# Link preview: { "text": "veja https://...", "linkPreview": true }Reação: { "to", "messageId", "emoji" } (emoji vazio remove).
Edita mensagem própria: { "to", "messageId", "text" }.
Apaga para todos: { "to", "messageId", "fromMe": true, "participant?" }.
Vota numa enquete (payload do SDK: PollVoteInput — chave da enquete, texto das opções; seleção vazia = remover voto).
Marca como lidas: { "chat", "ids": [...], "sender?" }.
Fixa/desfixa pra todos: { "to", "messageId", "fromMe?", "durationSecs?" } ("unpin": true desfixa). messages/keep mantém mensagem temporária; messages/limit-sharing liga/desliga a restrição de encaminhar/copiar do chat.
RSVP criptografado num evento de calendário: { "to", "messageId", "response": "going"|"notGoing"|"maybe", "extraGuestCount?" }. Respostas de outros chegam no evento SSE event-response.
Compartilha seu número num chat por username/LID (messages/request-phone pede o do outro). messages/poll-aggregate soma votos descriptografados ({ "chat", "pollId", "votes" }).
OpenGraph de uma URL (título/descrição/miniatura) pronto pra usar como linkPreview: { "url" } ou { "text" } (usa a 1ª URL do texto).
Recebendo
Mensagens recebidas chegam pelo SSE já descriptografadas: { id, chat, sender, fromMe, kind, text, media?, poll?, reaction?, location?, contact?, quoted, mentions, decoded? }. Tipos "exóticos" (evento, lista, botões, álbum…) vêm com o objeto completo em decoded — nada vira unknown opaco.
Mídia (upload/download)
Sobe bytes (dataBase64 + kind) e retorna o descritor pronto pra anexar em messages. Metadados (dimensões/duração/waveform/thumbnail) são calculados automaticamente no envio se omitidos.
Baixa e descriptografa a partir do descritor media de uma mensagem recebida; retorna dataBase64.
Pede re-upload quando o download falha com 404/410 (mídia antiga): { "id", "chat", "mediaKey", "fromMe?", "sender?" }. A resposta chega no evento media-retry.
Presença & digitando
Define sua presença (available/unavailable).
Assina a presença de um JID (atualizações via evento presence).
“Digitando/gravando”: composing/recording/paused por chat.
Grupos & comunidades
Cria grupo: { "name", "participants": [jid...] } (emite joined-group).
Grupos dos quais a conta participa.
Metadados completos (participantes, admins, ephemeral, comunidade pai…).
add/remove/promote/demote participantes.
Entra por código/link de convite; invite-info mostra prévia, invite-link obtém/revoga o link, leave sai do grupo.
Assunto (nome) do grupo. Também: description, settings (announce/locked), photo.
Pedidos de entrada pendentes; requests/update aprova/rejeita (modo de aprovação).
Vincula/desvincula grupo a uma comunidade; subgroups lista os subgrupos e participantes vinculados.
Mudanças ao vivo chegam no evento group (e picture para foto).
Canais (newsletters)
Metadados do canal por JID (ou via convite no SDK).
Canais seguidos.
Cria canal: { "name", "description?", "pictureBase64?" }.
Segue/deixa de seguir (follow: true/false). Ao seguir, a sessão assina os live updates.
Posts do canal (count, before serverId). Posts novos chegam como mensagem plaintext e em newsletter-live-update.
Marca posts como vistos: { "jid", "serverIds": [...] }.
Reage a um post: { "jid", "serverId", "reaction": "👍" } (vazio remove).
Silencia/dessilencia o canal.
Atualizações de contadores (views/forwards/reações): { "jid", "count?", "sinceSeconds?", "after?" }. update altera metadados do canal (admin).
Postar no próprio canal usa POST /messages com to = JID do canal.
Status
Publica status (texto/mídia) com lista de privacidade opcional.
Configuração atual de privacidade do status.
Conta, perfil & privacidade
Seu nome de exibição (push name). Também: profile/about (recado) e profile/picture (foto).
Verifica quais números têm WhatsApp (usync). users/info traz sobre/foto/dispositivos; users/profile-picture a foto; users/business-profile o perfil comercial; users/resolve-username/usernames resolvem @usernames.
Configurações de privacidade; POST altera uma categoria. Mudanças externas chegam em privacy-settings.
Lista de bloqueados; POST bloqueia/desbloqueia. Mudanças chegam em blocklist.
E-mail da conta: GET consulta, POST define, email/verify confirma o código.
Resolve um link wa.me/qr/<code> para { jid, pushName, type }; POST /contact-qr obtém (ou revoga) o seu próprio código.
Mensagens temporárias: com chat ajusta a conversa; sem chat, o padrão da conta. seconds: 0 desliga.
Chats (app state)
Fixa/desafixa (pin). Irmãs: chats/archive, chats/mute, chats/read, chats/delete, chats/star (favoritar mensagem) e chats/contact (salvar contato na agenda sincronizada).
Bots (Meta AI)
Lista de bots disponíveis (Meta AI e parceiros).
Perfil de um bot/persona.
Envia um prompt (direto ou por menção em grupo). As respostas chegam em chunks e são descriptografadas automaticamente (botChunk nas mensagens do SSE).
Listas de transmissão
Listas de transmissão business (app state business_broadcast_list):
Cria/atualiza: { "listId", "name?", "participants": [...] }. broadcast-lists/remove apaga.
Dispara para a lista (fan-out por sender key para <listId>@broadcast).
Chamadas
A sinalização chega no SSE como call (e por ação: call-offer, call-accept, call-reject, call-terminate), com a stanza bruta para add-ons.
Rejeita uma chamada recebida: { "from", "callId" }.
Cria link de chamada (call.whatsapp.com): { "type": "video"|"audio", "startTime?" } → { token, type, link }.
@eduh_dev021/wappmeow-voip implementa call key E2EE + RTP/SRTP (RFC 3711) e o VoipManager — a mídia contra os relays reais do WhatsApp ainda é experimental.App State & sincronização
Sincroniza uma coleção (regular, critical_unblock_low…) sob demanda. Mutações chegam em appstate e nos eventos granulares (pin, archive, mute, star, contact…).
History sync (histórico do pareamento) é processado automaticamente: conversas, contatos, nomes e mapeamento LID↔PN chegam em history-sync e alimentam os caches da sessão.
Business: etiquetas, catálogo & store
Etiquetas do WhatsApp Business vivem no app state (label_edit/label_association) e agora são editáveis:
Cria etiqueta: { "id?", "name", "color?" } (201). labels/edit renomeia/troca cor; labels/delete apaga.
Associa ao chat: { "chatJid", "labelId", "labeled?" } (false remove). labels/message faz o mesmo por mensagem (+ "messageId").
Catálogo/comércio (IQs w:biz:catalog, igual ao Baileys):
Produtos de uma loja (com nextPageCursor). commerce/collections lista coleções; commerce/orders/:orderId?token= abre um pedido.
Cria produto no seu catálogo: { "name", "price", "currency", "images": [{ "url" } | { "dataBase64" }], ... }. commerce/products/update (+ "productId"), commerce/products/delete ({ "productIds" }) e commerce/images (sobe a foto do produto, sem criptografia).
A store sincronizada (history sync + app state + mensagens ao vivo) é consultável:
Todos os chats conhecidos; ?jid= retorna um. store/contacts é o equivalente para contatos. Mudanças chegam como chats-upsert/update/delete, contacts-upsert/update e messaging-history-set no SSE.
Pede ao celular o reenvio de mensagens placeholder: { "keys": [{ "remoteJid", "id" }] } → evento placeholder-resolved. history-sync/request pede histórico antigo de um chat ({ "chat", "oldestMsgId", "count?" }).
Re-upload de mídia expirada (media retry): { "id", "chat", "mediaKey" } → novo directPath.
Feature flags (AB props) do servidor do WhatsApp.
Telemetria WAM
Pipeline WAM opt-in com registry próprio embutido (444 eventos nomeados da WA Web — nada de pacote externo):
Liga o pipeline da sessão.
Commit nomeado ({ "name": "UiAction", "payload": { ... } }) ou cru ({ "eventId", "fields": [...] }).
Envia o lote acumulado agora.
Mobile (companion-host)
Permite que uma conta primária (device 0) hospede o pareamento de companheiros. Rotas: mobile/parse-qr (parse local do QR do companheiro), mobile/pair-device, mobile/key-index-list, mobile/remove-companion e mobile/primary-hello. Os artefatos assinados são fornecidos pelo chamador.
Referência da API
Resumo de todas as rotas (:id = id da sessão). Detalhes de corpo/resposta nas seções acima e nos tipos do SDK.
| Área | Rotas |
|---|---|
| Sessões | POST/GET /v1/sessions, GET/DELETE /v1/sessions/:id, connect, disconnect, logout, passive, events (SSE) |
| Pareamento | pair/qr, pair/code (POST/DELETE), pair/passkey/options|response|confirm |
| Mensagens | messages, messages/react|edit|revoke|read|poll-vote|pin|keep|limit-sharing|share-phone|request-phone|event-respond|poll-aggregate|comment, url-info, presence, presence/subscribe, chatstate |
| Mídia | media/upload, media/download, media/delete, media/retry-receipt, media/update, stickers/fetch-pack |
| Business | labels (POST), labels/edit|delete|chat|message, commerce/catalog|collections (GET), commerce/orders/:orderId (GET), commerce/products|products/update|products/delete|images (POST) |
| Store & misc | store/chats (GET, ?jid=), store/contacts, placeholder-resync, history-sync/request, props (GET), calls/link, GET /wa-web-version (raiz), GET /health (raiz) |
| Grupos | groups (POST/GET), groups/metadata|participants|join|leave|invite-info|invite-link|subject|description|settings|join-approval|member-add-mode|photo|requests|requests/update|link|subgroups|accept-invite-v4|revoke-invite-v4|member-label |
| Canais | newsletters (POST), newsletters/info|subscribed|follow|messages|viewed|reaction|mute|update|updates|subscribers|admin-count|change-owner|demote|delete |
| Conta | profile/name|about|picture, privacy (GET/POST), blocklist (GET/POST), account/email (+verify), contact-qr, contact-qr/resolve, disappearing-timer, status, status/privacy |
| Usuários | users/check|info|profile-picture|business-profile|resolve-username|usernames |
| Chats | chats/pin|archive|mute|read|delete|star|contact, appstate/fetch|mark-not-dirty |
| Extras | bots, bots/profile, bots/profiles, bots/prompt, broadcast-lists (+remove/send), calls/reject, wam/enable|commit|flush, mobile/* |
| Site | GET /, /docs, /privacidade, /termos (aliases /privacy e /terms) — servidos pela própria API |
Eventos em tempo real (SSE)
Stream text/event-stream com event: <nome> e data: <json>. No SDK: session.on('message', ...) com reconexão automática.
curl -N https://web.lagoshost.com.br/v1/sessions/$ID/events
# event: session
# data: {"id":"...","status":"connected"}
#
# event: message
# data: {"id":"3EB0...","chat":"5511@s.whatsapp.net","kind":"text","text":"oi!"}| Evento | Quando |
|---|---|
session | Mudança de estado da sessão (também é o primeiro frame do stream). |
message | Mensagem recebida (descriptografada/decodificada, incl. canais). |
message-sent · message-ack · receipt | Envios próprios e recibos (entregue/lido/ouvido). |
presence · chatstate | Online/offline e “digitando…”. |
group · joined-group · picture | Ciclo de vida de grupos e fotos. |
call · call-offer · call-accept · call-reject · call-terminate | Sinalização de chamadas. |
pushname · contact · pushname-setting | Nomes/contatos aprendidos ou alterados. |
blocklist · privacy-settings · devices | Sincronizações da conta vindas de outro dispositivo. |
chats-upsert · chats-update · chats-delete · contacts-upsert · contacts-update · messaging-history-set | Store sincronizada (estilo Baileys) a partir de history sync/app state/mensagens. |
lid-mapping-update · settings-update · chat-lock · chats-lock | Mapeamento PN↔LID aprendido, settings do app state e trava de conversa. |
group-member-tag-update · event-response · placeholder-resolved | Member label em grupos, RSVP de evento, placeholder recuperado do celular. |
pin · archive · mute · star · mark-chat-as-read · clear-chat · delete-chat | Ações de chat (app state) feitas em qualquer dispositivo. |
newsletter-live-update · media-retry | Posts/contadores de canais e respostas de retry de mídia. |
history-sync · appstate · appstate-keys | Sincronizações pós-pareamento e mutações brutas. |
offline-sync-preview · offline-sync-completed | Backlog offline ao conectar. |
pairing-code* · pair-passkey-* · temporary-ban · identity-change · decrypt-error · message-retried · client-outdated · blocklist-change · user-about · mex-notification · newsletter-join/leave/mute-change/milestone · account-reachout-timelock · delete-for-me · label-edit/label-association-* · appstate-sync-complete/appstate-sync-error · rotate-adv-secret | Pareamento, ban temporário, troca de identidade Signal e erros de descriptografia. |
Erros & códigos
Formato único: { "error": { "code": "CODIGO", "message": "..." } }.
| HTTP | Significado |
|---|---|
400 | VALIDATION_ERROR — corpo fora do schema (detalhes em error.details). |
404 | Sessão inexistente. |
409 | Sessão não pareada/conectada para a operação (ex.: SESSION_NOT_PAIRED). |
502/503 | Falha falando com o WhatsApp (transporte/IQ) — tente de novo; veja os logs do servidor. |
WappMeowApiError e use error.code/error.status — os mesmos valores desta tabela.Feito com o wappmeow — a API de WhatsApp que ronrona.