API de WhatsApp em Node.js: guia prático com o SDK oficial
Em Node.js, o caminho mais curto é o pacote d-api-sdk: um npm install, uma instância do client e você já cria a conexão, lê o QR Code e envia a primeira mensagem. Este guia cobre o fluxo inteiro em TypeScript, incluindo o webhook no Express e o tratamento de erro.
Por que usar o SDK em vez de montar as requisições
Toda a API de WhatsApp da D-API é REST, então nada impede que você escreva as chamadas com fetch ou axios. O ganho do SDK aparece no dia a dia: os métodos têm nome de ação (sessions.create, messages.sendText, interactive.sendList), os parâmetros são tipados e o editor avisa quando falta um campo obrigatório antes de o código ir para produção.
Node.js é a única linguagem com biblioteca oficial da D-API. Nas outras stacks a integração é feita com o cliente HTTP da própria linguagem, e o contrato é o mesmo. A página da integração Node.js SDK resume os módulos disponíveis.
Instalação e client configurado
Instale o pacote e crie um único client para a aplicação inteira. Guarde a API Key em variável de ambiente; ela vai no header Authorization sem o prefixo Bearer, e o SDK cuida disso sozinho.
npm install d-api-sdk// src/dapi.ts
import { DApi } from 'd-api-sdk'
export const dapi = new DApi({
apiKey: process.env.DAPI_API_KEY!,
timeout: 10_000, // padrão é 30000 ms; em rota HTTP prefira algo menor
})O baseUrl é opcional e aponta para https://api.d-api.cloud por padrão. O parâmetro timeout vale para todas as chamadas: quando estoura, o SDK aborta o fetch e lança um erro com status 408. Deixar esse valor explícito evita que uma requisição lenta segure o event loop de uma rota da sua aplicação por meio minuto.
Criar a conexão e exibir o QR Code
Cada número de WhatsApp é uma sessão, identificada por um sessionId que você escolhe. Na criação você já informa a URL do webhook, assim os eventos começam a chegar assim que o número conectar.
import { dapi } from './dapi'
await dapi.sessions.create({
sessionId: 'loja-centro',
connectionMode: 'qr',
webhookUrl: 'https://seu-app.com/webhooks/whatsapp/' + process.env.WEBHOOK_TOKEN,
})
const { qrCodeImage } = await dapi.sessions.getQRCode('loja-centro')
// qrCodeImage é um data URI (data:image/png;base64,...)
// basta usar como src de uma <img> no painel do seu clienteO QR expira em poucos segundos e é renovado pela API. Na tela de conexão, consulte getQRCode de novo em intervalos curtos até o número conectar, ou ouça o evento connection.status no webhook e troque a tela quando data.status vier como connected. Os detalhes de pareamento, inclusive por código em vez de QR, estão em API de WhatsApp com QR Code.
Enviar texto e lista com poucas linhas
Com o número conectado, o envio de texto pede três campos: sessão, destino no formato internacional sem o sinal de mais e o conteúdo. A lista interativa acrescenta o texto do botão e as seções com as opções.
await dapi.messages.sendText({
sessionId: 'loja-centro',
to: '5511999999999',
text: 'Oi, Ana! Seu pedido 4812 foi aprovado.',
})
await dapi.interactive.sendList({
sessionId: 'loja-centro',
to: '5511999999999',
description: 'Quando prefere receber?',
buttonText: 'Ver horários',
sections: [
{
title: 'Entrega',
rows: [
{ rowId: 'manha', title: 'Manhã', description: '8h às 12h' },
{ rowId: 'tarde', title: 'Tarde', description: '13h às 18h' },
],
},
],
})O rowId é o que volta para o seu sistema quando a pessoa escolhe uma opção, então use valores que o código entenda sem consulta extra. Limites de seções e linhas, e o que o WhatsApp permite em botões, estão em botões e listas no WhatsApp.
Tratar erro e timeout
Quando a API responde com status de erro, ela devolve um corpo no formato { success: false, error, statusCode }. O SDK transforma isso em uma exceção do tipo DApiError, com message vindo do campo error, o status HTTP e o corpo original em response.
import type { DApiError } from 'd-api-sdk'
try {
await dapi.messages.sendText({ sessionId: 'loja-centro', to, text })
} catch (err) {
const e = err as DApiError
if (e.status === 408) {
// timeout do client: a mensagem pode ter saído ou não
logger.warn({ to, status: e.status }, 'envio sem resposta a tempo')
} else if (e.status >= 400 && e.status < 500) {
// payload inválido ou sessão inexistente: tentar de novo não resolve
logger.error({ status: e.status, error: e.message }, 'envio recusado')
}
throw err
}Duas decisões valem ser tomadas cedo. Erro 4xx indica problema no pedido, como campo faltando ou sessão desconectada, e repetir a chamada só gera ruído. Já timeout e 5xx podem ser temporários. Quem manda muito volume costuma colocar o envio em uma fila (BullMQ, por exemplo), assim o request do seu produto responde rápido e o envio acontece em segundo plano.
Receber o webhook com Express
Mensagens recebidas, confirmações de leitura e mudanças de conexão chegam por POST na URL que você cadastrou. Todo evento tem o mesmo envelope: event, sessionId, data, timestamp e traceId. A rota abaixo valida o token secreto da URL, responde imediatamente e só depois trata a resposta da lista.
import express from 'express'
import { dapi } from './dapi'
const app = express()
app.post('/webhooks/whatsapp/:token', express.json({ limit: '1mb' }), (req, res) => {
if (req.params.token !== process.env.WEBHOOK_TOKEN) {
res.sendStatus(401)
return
}
res.sendStatus(200) // responde antes de qualquer trabalho pesado
const { event, sessionId, data } = req.body
if (event !== 'messages.received' || data.fromMe) return
if (data.type === 'list_response') {
const escolha = data.data.selected_row_id // 'manha' ou 'tarde'
const numero = data.from.jid.split('@')[0]
dapi.messages
.sendText({ sessionId, to: numero, text: 'Combinado, entrega agendada.' })
.catch((err) => logger.error({ err, traceId: req.body.traceId }, 'falha na resposta'))
}
})
app.listen(3000)O webhook de WhatsApp não traz assinatura HMAC, por isso o token no caminho da URL faz o papel de segredo. Também dá para conferir o header User-Agent, que chega como Deliverify-Webhook/1.0, e se o sessionId pertence a um cliente seu. Os eventos disponíveis e a política de reenvio estão em webhook de WhatsApp.
Checklist antes de colocar em produção
- API Key e token do webhook em variáveis de ambiente, nunca no repositório.
- Timeout do client ajustado ao contexto: menor dentro de rota HTTP, maior em worker.
- Deduplicação pelo
data.idda mensagem, porque a mesma entrega pode chegar mais de uma vez se o seu servidor demorar a responder. - Log com o
traceIddo evento, que ajuda o suporte a rastrear uma entrega específica. - Um
sessionIdpor número. Se o seu produto terá uma conexão por cliente, veja como isso se organiza na página de API de WhatsApp para SaaS.
Perguntas frequentes
Preciso usar o SDK ou posso chamar a API direto com fetch?
Os dois funcionam. A API é REST e aceita qualquer cliente HTTP. O d-api-sdk só poupa trabalho: já monta a URL, manda o header Authorization, aplica timeout e entrega tipos prontos para TypeScript.
O d-api-sdk funciona com JavaScript puro, sem TypeScript?
Sim. O pacote é publicado em JavaScript com as definições de tipo junto. Em um projeto sem TypeScript você importa e chama os mesmos métodos, só perde o autocomplete tipado no editor.
Qual versão do Node.js eu preciso?
O SDK usa o fetch nativo e o AbortController, então precisa de uma versão do Node.js que já traga fetch global, como a 18 ou superior. Em versões antigas o import funciona, mas as chamadas falham.
Como sei qual opção o cliente escolheu na lista?
A resposta chega no webhook como evento messages.received com type igual a list_response. Dentro de data.data vem selected_row_id, que é o rowId que você definiu ao enviar a lista.
Continue lendo
Teste a API de WhatsApp da D-API
Trial de 3 dias com acesso completo. Sem cartão, sem fidelidade.