WaClient recebe um objeto WaClientOptions e um logger opcional:
store e sessionId são obrigatórios; todo o resto tem um valor padrão sensato.
Opções obrigatórias
A instância da store construída por
createStore. Mantém cada domínio por sessão (auth, signal, app-state, …).Identificador lógico da sessão — ele indexa cada domínio dentro da
store. Use uma string estável por dispositivo/conta. Trocá-lo entre execuções deixa as credenciais anteriores órfãs e força um novo pareamento.Sessões e multi-tenancy
Como cada domínio da store é indexado porsessionId, uma única store pode conter muitas contas independentes. Para rodar várias contas em um processo, crie um WaClient por sessionId sobre a mesma store:
Fingerprint do dispositivo
Estes controlam como o dispositivo aparece em Dispositivos conectados no celular:Id do navegador anunciado durante o pareamento (
'chrome', 'firefox', 'safari', …; veja WA_BROWSERS). Define o rótulo em Dispositivos conectados.Sobrescrita do id numérico de plataforma companion (
WA_COMPANION_PLATFORM_IDS). Inferido de deviceBrowser quando omitido; defina explicitamente para plataformas não-navegador.Sincronização de histórico
Controla o processamento dos chunks de
historySyncNotification — tanto o bootstrap inicial que o WhatsApp envia após o pareamento quanto o backfill sob demanda disparado por message.requestHistorySync.enabled?: boolean— processa os chunks de histórico recebidos. Padrãotrue. Defina comofalsepara descartá-los silenciosamente (útil quando você não persiste mailbox/threads/contacts e o download da conversa só gastaria banda). A lib ainda envia ack do chunk para o servidor parar de re-enviar, igual ao wa-web.requireFullSync?: boolean— solicita o arquivo completo em vez de apenas os chats recentes.
history_sync_chunk.
Timeouts
Todos em milissegundos; os padrões são ajustados para produção.Versão do WhatsApp
O zapo embarca uma versão de produção testada por transporte. Ocasionalmente o WhatsApp rejeita clients antigos durante o handshake noise com HTTP405 / failure_client_too_old. Você tem três opções para se recuperar.
Sobrescreve a string de versão anunciada pelo client. Aceita um literal ou um resolver chamado uma vez por
connect() — útil para buscar a versão atual sob demanda sem recriar o client. O formato aceito depende do transporte resolvido para o connect:- Web aceita uma versão de 3 a 5 partes (
2.3000.x[.y.z]); as 4ª e 5ª partes, quando fornecidas, são anunciadas no payload noise. - Mobile aceita exatamente uma app version Android de 4 partes (
2.26.x.y); ela sobrescrevemobileTransport.deviceInfo.appVersionno payload de login.
connect().Quando
true, ao receber failure_client_too_old o client emite um warning, busca a versão atual para o transporte ativo (fetchLatestWaWebVersion() para Web, fetchLatestWaMobileVersion() para Mobile), aplica como override de uso único e reconecta automaticamente. Em Mobile, o override é aplicado atualizando deviceInfo.appVersion para o próximo connect. Trate como paliativo até atualizar o zapo — o default embarcado continua sendo o caminho recomendado.fetchLatestWaWebVersion()
Faz scraping do client_revision atual em web.whatsapp.com/sw.js e retorna uma string de versão no formato 2.3000.x aceito por version em uma sessão Web.
timeoutMs (padrão 10s), proxy (apenas dispatcher do undici — o fetch global não honra http.Agent), signal, userAgent, headers e uma override de fetch para testes. Erros de rede e parsing são lançados — envolva em try/catch se quiser cair no default embarcado.
fetchLatestWaMobileVersion()
Faz scraping da versão atual do WhatsApp para Android em uma página pública de app-listing e retorna uma string 2.26.x.y de 4 partes adequada para version em uma sessão Mobile (ou como override para mobileTransport.deviceInfo.appVersion).
timeoutMs, proxy, signal, userAgent, headers, fetch) mais:
url?: string— sobrescreve a página a ser lida. A fonte padrão é um mirror público de app-listing porque a própria página do WhatsApp emwhatsapp.com/androidsó mostra a versão mínima obsoleta; re-aponte se o layout mudar ou for inalcançável da sua rede.versionPattern?: RegExp— sobrescreve o regex de extração. Precisa expor a versão no capture group 1. O padrão combina com um2.x.x.xde 4 partes e retorna o primeiro hit na página.
invalid wa-mobile version parsed from page). Erros de rede e parsing são lançados — envolva em try/catch se quiser cair em uma versão hardcoded conhecida.
Presença ao conectar
false(padrão) — anuncia como indisponível. Igual ao WhatsApp Web quando a aba não está em foco e mantém bots headless invisíveis por padrão. Com isso desligado, você continua recebendo notificações de mensagens enquanto está “offline”.true— anuncia o client como online (igual ao WhatsApp Web com a aba em foco no momento do login).
Linking com passkey
Signer WebAuthn externo para o handshake Shortcake de passkey forçado pelo servidor. Chamado com o
PublicKeyCredentialRequestOptions bruto (Uint8Array) que o servidor emitiu; deve retornar { credentialId, webauthnAssertion }. A source da credencial (authenticator real / virtual, relay) fica fora da lib.Sem isso, uma conta que receber um passkey prologue forçado pelo servidor emite auth_passkey_required com hasSigner: false e o link fica parado — veja o deep dive de reverse engineering pro detalhe no nível do fio.Addons (reações, votos em enquetes)
addons
{ autoDecrypt?: boolean, persistAllSecrets?: boolean }
padrão:"{ autoDecrypt: true, persistAllSecrets: false }"
Addons criptografados (votos em enquetes, reações, edições de mensagem, …) são descriptografados automaticamente e emitidos como eventos tipados
message_addon. Defina autoDecrypt: false para recebê-los criptografados e descriptografar você mesmo via client.message.tryDecryptAddon(event). O secret da mensagem-pai é procurado no cache messageSecret primeiro, depois no store messages.persistAllSecrets: true persiste o secret de 32 bytes de toda mensagem enviada e recebida, não só as de poll / event / bot-prompt que a lib sabe que terão follow-up. Addons criptografados cujo pai pode ser qualquer tipo de mensagem — reações, comentários, edições via secretEncryptedMessage — precisam do secret do pai para decifrar; sem essa flag, esses pais só continuam decifráveis depois de um restart quando o arquivo messages completo é persistente. Use para manter a decifragem possível guardando apenas o secret (messages: 'none').Sem efeito quando o cache messageSecret é 'none' — toda escrita de secret cai no noop store e é silenciosamente descartada. Com o provider 'memory' default funciona pelo tempo de vida do processo, mas perde no restart e fica limitado ao LRU e ao TTL messageSecretMs do cache; aponte messageSecret para um backend persistente para manter os secrets entre restarts.Mídia
Processamento de mídia. Passe um
processor (de @zapo-js/media-utils) para gerar thumbnails/previews, medir dimensões e durações e construir waveforms de notas de voz antes do upload — e ligue/desligue cada etapa. Sem um processor a mídia ainda é enviada, só sem esse processamento. Veja o guia de mídia para a configuração completa.processor?: WaMediaProcessor— a instância do processorgenerateThumbnail?: boolean— thumbnails de preview de imagem/vídeogenerateProbe?: boolean— mede largura/altura/duraçãogenerateWaveform?: boolean— waveform de nota de voz (PTT)generateStickerThumbnail?: booleannormalizeVoiceNote?: boolean— recodifica o áudio PTT para o formato que o WhatsApp espera
Previews de link
Configuração global do fetcher embutido de preview de link, usado ao enviar texto que contém uma URL. Sobrescreva por mensagem com a opção de envio
linkPreview.enabled?: boolean— liga ou desliga globalmente a busca automática de preview de linkfetchTimeoutMs?: number— quanto esperar pela página de destinouploadHqThumbnail?: boolean— envia uma thumbnail de preview em alta resoluçãoallowPrivateHosts?: boolean— permite buscar endereços privados/loopback (desligado por padrão, como proteção contra SSRF)maxHtmlBytes?: number/maxThumbnailBytes?: number— limites de tamanho para o HTML e a imagem buscadosuserAgent?: string— User-Agent enviado na buscaproxy?: WaProxyTransport— aplica proxy só a este fetcher (igual aproxy.linkPreview)fetcher?: WaLinkPreviewFetcher— substitui o fetcher padrão por completo (ex.: seu próprio pipeline de scraping)
Eventos de chat
Defina
emitSnapshotMutations: true para reemitir eventos mutation para cada mudança vista durante uma sincronização de snapshot de app-state. Desligado por padrão, já que mutações de snapshot representam estado histórico, não mudanças ao vivo.Persistência write-behind
Agrupa as mensagens recebidas em lotes antes de descarregá-las nas stores
messages / threads / contacts.maxPendingKeys?: numbermaxWriteConcurrency?: numberflushTimeoutMs?: number
Proxy
Roteie cada perna através de um proxy de forma independente:
ws— a conexão WebSocket.mediaUpload/mediaDownload— transferências de mídia.linkPreview— o fetcher padrão de preview de link.
WaProxyTransport, que é:
- um dispatcher do undici (
WaProxyDispatcher, por exemplo umProxyAgentdo undici) — usado para as pernas baseadas emfetch(mídia, preview de link), ou - um
Agenthttp/httpsdo Node (WaProxyAgent) — usado para a perna WebSocket (ws).
A perna
ws requer o pacote ws, porque o WebSocket nativo do runtime não consegue aceitar um Agent HTTP. Sem um proxy, nenhum pacote extra é necessário.Proxy HTTP / HTTPS
Use umProxyAgent do undici (um dispatcher) para as pernas de mídia/preview de link, e um https-proxy-agent (um http.Agent) para a perna ws:
Proxy SOCKS
Usesocks-proxy-agent (funciona como um http.Agent para todas as pernas, incluindo ws):
Hosts IPv4 e IPv6
O host do proxy pode ser um domínio ou um literal de IP. Endereços IPv6 devem ser envolvidos em colchetes:Limpeza da store no logout
Controle por domínio do que o
logout() apaga.Por padrão, o arquivo da mailbox (messages, threads, contacts) é preservado para que o usuário mantenha seu histórico ao reparear. Todos os outros domínios (credenciais, estado Signal, app-state, caches, privacy tokens) são limpos para começar o próximo pareamento do zero. true / false explícitos sempre vencem o padrão.Logging
OWaClient aceita um Logger como segundo argumento do construtor. Se você omitir, um ConsoleLogger('info') padrão é usado. Níveis, do mais baixo ao mais alto: trace, debug, info, warn, error.
Duas implementações vêm com o pacote.
ConsoleLogger
Sem dependências. Escreve registros estruturados emconsole.log / console.warn / console.error. Bom para desenvolvimento, testes e funções serverless onde você não consegue adicionar um transport de logger.
createPinoLogger
Factory async que carrega dinamicamente opino (e o pino-pretty quando pretty: true), configura e envolve no adapter PinoLogger. Lança optional dependency "pino" is not installed quando o pino não está presente — instale com npm i pino pino-pretty.
PinoLogger (traga o seu Pino)
Se você já configura Pino centralmente — child loggers, transports customizados, destinos em arquivo — construaPinoLogger diretamente para envolver sua instância existente. A factory é uma conveniência; a classe é o adapter de fato, e usá-la evita o import dinâmico do pino.
new PinoLogger(logger, level = 'info'). O nível é encaminhado pra logger.level e usado como o level reportado pelo adapter.
Logger customizado
Precisa de um sink que as implementações nativas não cobrem — Datadog, OpenTelemetry, syslog, um pipeline de observabilidade interno? Implemente a interfaceLogger e passe a instância pro WaClient. A interface é pequena:
LogLevel é 'trace' | 'debug' | 'info' | 'warn' | 'error'. A biblioteca chama os cinco métodos de level diretamente — não há uma camada de level-gating à frente, então sua implementação é responsável por filtrar contra this.level se quiser pular chamadas baratas.
Um exemplo mínimo que encaminha para um sink externo e rastreia bindings por child():
child() é usado internamente para anexar bindings por componente (ex.: { component: 'noise' }, { component: 'signal', sessionId }). Retornar uma nova instância com bindings mesclados — em vez de mutar — mantém esses tags com escopo no subsistema que os produziu.Plugins
Plugins opcionais de
WaClient — hooks de comportamento e/ou coordinators expostos em client[exposeAs]. Autorados com defineWaClientPlugin. O plugin de chamadas de voz (@zapo-js/voip) é a implementação de referência; veja a página do sistema de plugins para saber como conectar e autorar plugins.Opções avançadas
Raramente necessárias — listadas para completude.chatSocketUrls?: readonly string[]— sobrescreve a lista de endpoints WebSocket de chat do WhatsApp (ex.: rotear por um servidor falso em testes, ou fixar um edge específico).privacyToken?: WaPrivacyTokenOptions— ajusta a emissão de trusted-contact-token (TC token): durações e número de buckets.testHooks?: WaClientTestHooks— fixtures só para testes (ex.: um root CA Noise customizado). Eles não burlam nenhuma verificação de segurança; para realmente pular uma checagem, use as opçõesdangerousabaixo.
Opções perigosas
As flags
dangerous desabilitam, cada uma, uma verificação de segurança que o caminho de produção aplica (verificação de assinatura, checagens de MAC do app-state, …). Elas existem para testes contra um servidor falso. Nunca as habilite em produção.