Provisionar WhatsApp para 100 clientes por API: criar, ativar, suspender e cortar
Se criar a conta de WhatsApp de um cliente novo depende de alguém abrir um painel, o processo trava no décimo. Como amarrar o ciclo de vida da instância ao seu billing: criar no onboarding, suspender na inadimplência e cortar no cancelamento.
A pergunta que separa uma integração de um produto: o que acontece quando entra o cliente número 11?
Se a resposta envolve alguém abrindo um painel, preenchendo um formulário e copiando uma chave para o seu sistema, o processo trava — e o custo por cliente cresce com o número de clientes, que é exatamente o contrário do que uma software house precisa.
Provisionamento no onboarding
O modelo que escala amarra a instância ao contrato:
async function ativarCliente(clienteId) {
const cliente = await db.clientes.buscar(clienteId);
// 1. cria a instância
const inst = await wameAdmin.criarInstancia({
nome: `cliente-${cliente.id}`,
// Nome legível ajuda no dia em que você precisar auditar
// a fatura consolidada e descobrir de quem é cada linha.
});
// 2. guarda a chave junto do cliente
await db.clientes.atualizar(clienteId, {
wame_instancia_id: inst.id,
wame_key: cifrar(inst.key),
canal_status: 'aguardando_conexao',
});
// 3. aponta o webhook para a URL DESTE cliente
await wameAdmin.configurarWebhook(inst.key, {
allowWebhook: true,
webhookFormat: 'meta',
webhookMessage: `https://seusistema.com/webhook/wame/${cliente.slug}/${cliente.webhookSecret}`,
});
return inst;
}
O cliente termina o cadastro no seu sistema e a instância já existe. Ninguém abre painel de ninguém.
Uma URL de webhook por cliente, com segredo próprio. Não use uma URL única para todos: além de você precisar descobrir de quem é cada evento, um segredo vazado comprometeria a base inteira. É o mesmo raciocínio de webhook em produção.
Conectar o número do cliente
Duas portas, e a escolha é por cliente:
API oficial — login seguro pela própria Meta, dentro do seu fluxo. O número fica no Business Manager do cliente, e ele nunca digita credencial no seu sistema.
API não oficial — QR Code. Você busca o código e mostra na sua tela:
const { qr } = await wa.instance.connect();
// devolva o QR para o front do SEU produto renderizar
O cliente escaneia dentro do seu sistema, com a sua marca. Nenhuma tela de terceiro aparece.
Cliente pequeno começa no QR Code hoje; cliente que precisa de contrato vai para o oficial. O comparativo entre os dois ajuda a decidir, e mudar depois não reescreve o seu código — muda a instância, o handler continua o mesmo.
Estados do contrato viram estados da instância
O erro clássico é tratar os dois como coisas separadas. O certo é o contrato mandar:
| Contrato | Instância | Chamada |
|---|---|---|
| Ativo | Ativa | — |
| Inadimplente | Suspensa | desativar |
| Pagou | Ativa | ativar |
| Cancelado | Excluída (após carência) | excluir |
| Trial vencido | Suspensa | desativar |
async function aplicarEstado(clienteId, novoEstado) {
const c = await db.clientes.buscar(clienteId);
if (!c.wame_key) return;
switch (novoEstado) {
case 'inadimplente':
case 'trial_vencido':
// Suspender, não excluir: reativação depois do pagamento
// precisa ser imediata, e excluir perderia a conexão.
await wameAdmin.desativar(c.wame_key);
break;
case 'ativo':
await wameAdmin.ativar(c.wame_key);
break;
case 'cancelado':
// Carência antes de excluir. Cancelamento por engano
// acontece, e excluir é irreversível.
await agendar('excluir_instancia', { clienteId }, { emDias: 30 });
break;
}
await db.clientes.atualizar(clienteId, { canal_status: novoEstado });
}
Suspender em vez de excluir é a decisão que mais evita dor de cabeça. Inadimplência costuma ser temporária; exclusão não é.
Carência antes de excluir cobre o cancelamento por engano — e ele acontece mais do que se imagina.
A reconciliação que paga a própria conta
Estado divergente é inevitável: uma chamada falha, um webhook se perde, alguém muda o contrato direto no banco. O resultado é sempre o mesmo — instância ativa de cliente que já saiu, aparecendo na sua fatura.
// roda todo dia de madrugada
async function reconciliar() {
const clientes = await db.clientes.comInstancia();
const instancias = await wameAdmin.listarInstancias();
const porChave = new Map(instancias.map((i) => [i.key, i]));
const divergencias = [];
for (const c of clientes) {
const inst = porChave.get(decifrar(c.wame_key));
if (!inst) {
divergencias.push({ cliente: c.id, problema: 'instancia_sumiu' });
continue;
}
const deveriaEstarAtiva = c.canal_status === 'ativo';
if (inst.ativa !== deveriaEstarAtiva) {
divergencias.push({
cliente: c.id,
problema: 'estado_divergente',
contrato: c.canal_status,
instancia: inst.ativa ? 'ativa' : 'inativa',
});
await aplicarEstado(c.id, c.canal_status); // corrige
}
}
// Órfãs: existem na fatura e não pertencem a ninguém
const chavesConhecidas = new Set(clientes.map((c) => decifrar(c.wame_key)));
for (const i of instancias) {
if (!chavesConhecidas.has(i.key)) {
divergencias.push({ problema: 'instancia_orfa', key: i.key });
}
}
if (divergencias.length) await avisarTime(divergencias);
}
Instância órfã é dinheiro indo embora todo mês em silêncio. Meia hora de código que se paga na primeira fatura.
Monitorar a saúde de todas
Com 100 clientes, você não descobre que a instância caiu pelo cliente ligando:
async function verificarSaude() {
const ativos = await db.clientes.ativos();
for (const c of ativos) {
const info = await wa(c).instance.info();
if (!info.conectada) {
await registrarIncidente(c.id, 'desconectada');
// Avise o cliente ANTES de ele perceber. Muda completamente
// a conversa: de "seu sistema está quebrado" para "vimos que
// caiu e já estamos resolvendo".
await notificarCliente(c, 'canal_desconectado');
}
}
}
Na API não oficial isso importa mais: a sessão pode cair sozinha e precisa de reconexão.
Segurança do multi-tenant
Cifre a chave da instância no banco. Ela permite enviar mensagem em nome do número do cliente. Vazamento de banco não pode virar vazamento de canal.
Nunca exponha a chave no front. Toda chamada sai do seu servidor. Chave no bundle é chave pública.
Isole por cliente em toda consulta. O WHERE cliente_id = ? esquecido é o bug que manda a mensagem de um cliente para a base de outro.
Segredo de webhook por instância, como no exemplo lá em cima.
A fatura
No Programa de Parceiros, todas as instâncias vêm numa fatura mensal única, com preço por volume — R$ 28,00 por instância na faixa de 20 a 99, caindo conforme cresce. Você paga uma vez e cobra dos seus clientes do seu jeito, no seu ciclo.
Como transformar isso em margem previsível está em quanto cobrar pelo módulo.
Conclusão
Provisionamento por API é o que faz o décimo cliente custar o mesmo que o segundo. Sem ele, cada venda nova adiciona trabalho manual, e a operação encontra um teto que não é comercial — é de processo.
Quatro peças resolvem: criar no onboarding, mapear estado do contrato para estado da instância, reconciliar todo dia e monitorar a saúde. Nenhuma é difícil; a que mais se esquece é a reconciliação, e é justamente a que aparece na fatura.
O desenho completo, incluindo a parte comercial, está na página para software house.
Pronto para automatizar seu WhatsApp?
Crie sua conta gratuita e comece a enviar mensagens pela API em minutos.
Começar grátisPerguntas frequentes
Dá para criar instâncias de WhatsApp pela API, sem painel?+
Sim. Criar, ativar, desativar e excluir são chamadas de API. Isso permite que o provisionamento aconteça dentro do seu próprio onboarding: o cliente termina o cadastro no seu sistema e a instância já existe, sem ninguém abrir painel de terceiro.
Como amarrar a instância ao ciclo de vida do cliente?+
Tratando o estado do contrato como fonte da verdade e a instância como consequência. Contrato ativo, instância ativa; contrato inadimplente, instância suspensa; contrato cancelado, instância excluída após um período de carência. Um job diário reconcilia os dois estados e corrige divergência.
O cliente final precisa saber que existe um fornecedor por trás?+
Não. Todo o gerenciamento acontece via API dentro do seu produto, e a cobrança é a sua. O cliente vê o WhatsApp funcionando no sistema que você entregou e conecta o número dele por um login da própria Meta ou por QR Code.
O que acontece com a instância se o cliente não pagar?+
Isso é decisão sua, e é por isso que o controle por API importa. O padrão que funciona é suspender em vez de excluir: a instância para de enviar, mas o histórico e a conexão continuam, então a reativação após o pagamento é imediata.
Como evitar pagar por instância de cliente que já saiu?+
Com reconciliação automática. A causa mais comum de desperdício é o cancelamento registrado no contrato e a instância esquecida ativa. Um job diário que compara os dois lados e reporta divergência resolve — e paga o próprio desenvolvimento no primeiro mês.
Continue lendo
Como criar um chatbot de IA com a API da OpenAI para responder no WhatsApp
Um webhook, uma chamada à API da OpenAI e uma resposta pela WAME API: o código completo de um chatbot de IA que atende no WhatsApp, Instagram e Messenger. Com memória por contato, controle de custo e o que fazer quando a IA não deve responder.
Cobrança por Pix dentro do WhatsApp pela API: como enviar e o que muda na conversão
Mandar o código Pix no WhatsApp resolve o pior ponto da cobrança digital: o cliente não precisa sair do app. Como enviar a cobrança pela API, tratar a confirmação e evitar os erros que transformam a facilidade em suporte.
Erros da API do WhatsApp: o que cada um significa e como tratar
A mensagem não saiu e o log diz apenas 'erro ao enviar'. Os erros que você vai encontrar de verdade — janela fechada, número inválido, template não aprovado, limite atingido, instância caída — e o tratamento certo para cada um.