---
title: "Provisionar WhatsApp para 100 clientes por API: criar, ativar, suspender e cortar"
description: "Se criar a conta 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."
url: "https://wame.api.br/blog/provisionar-whatsapp-multiplos-clientes-api"
language: "pt-BR"
og:type: "article"
published_time: "2026-09-10"
modified_time: "2026-09-10"
author: "Raphael Serafim"
keywords: "whatsapp api multi instância, provisionar whatsapp api, gerenciar várias contas whatsapp, multi tenant whatsapp, api whatsapp para vários clientes, criar instância whatsapp api, whatsapp white label"
reading_time: "10 min de leitura"
---

# Provisionar WhatsApp para 100 clientes por API: criar, ativar, suspender e cortar

> Se criar a conta 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.

**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:

```js
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](/blog/webhook-producao-assinatura-retry-idempotencia).

## 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:

```js
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](/blog/whatsapp-api-oficial-vs-nao-oficial) 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` |

```js
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**.

```js
// 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:

```js
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](/blog/quanto-cobrar-integracao-whatsapp-recorrencia).

## 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](/api-whatsapp-para-software-house).

## Perguntas 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.
