Dev

Enviar WhatsApp pelo Google Sheets com Apps Script (passo a passo)

Para enviar WhatsApp pelo Google Sheets, abra Extensões > Apps Script, guarde a chave e o segredo da API em Propriedades do script e use UrlFetchApp.fetch para chamar POST /api/v1/{instanceId}/send/text a cada linha. O script abaixo lê Nome, Telefone e Mensagem, envia, grava OK ou o erro na coluna Status e pula quem já foi. Também mostramos o menu dentro da planilha e como receber respostas por webhook.

Publicado em · 6 min de leitura

Este artigo também está disponível em: English · Español

O que você precisa e como organizar a planilha

Uma conta no ZapZap, uma instância conectada por QR code, a chave e o segredo da API (x-api-key e x-api-secret) e o ID da instância. A planilha precisa de quatro colunas na primeira aba, nesta ordem: A Nome, B Telefone, C Mensagem, D Status.

O telefone vai com DDI e só dígitos, por exemplo 5511999999999. Se a coluna estiver formatada como número, o Sheets pode transformar o valor em notação científica ou remover zeros; formate a coluna B como Texto simples. A coluna D começa vazia: o script escreve nela o resultado de cada envio, e é isso que impede reenvio em dobro. O passo a passo resumido também está na integração com Google Sheets.

Como guardar a chave e o segredo sem escrever no código

Nunca cole credenciais no código da planilha: quem tem acesso de edição vê tudo. No editor do Apps Script, abra Configurações do projeto (engrenagem) e, em Propriedades do script, adicione ZAPZAP_KEY, ZAPZAP_SECRET e ZAPZAP_INSTANCE. O código lê esses valores com PropertiesService.

Atenção a quem tem acesso: o dono do script e quem tem permissão de edição na planilha conseguem abrir o editor e ler as propriedades. Compartilhe a planilha como Leitor com quem só precisa ver os dados, e use uma instância separada para uso em planilha, não o número principal do negócio.

Como o script lê a planilha e chama a API

O código abaixo percorre as linhas a partir da segunda, pula as que já têm Status preenchido, envia a mensagem com UrlFetchApp.fetch e escreve o resultado na coluna D. O parâmetro muteHttpExceptions evita que um erro HTTP interrompa o script: você lê o código de resposta e decide o que gravar.

O Utilities.sleep entre envios cria um intervalo. O valor é escolha sua: não existe número oficial de segurança do WhatsApp, e rajada idêntica para uma lista fria é o comportamento que mais pesa. Veja o guia de anti-bloqueio antes de aumentar volume.

Apps Script

function enviarMensagens() {
  const props = PropertiesService.getScriptProperties();
  const key = props.getProperty("ZAPZAP_KEY");
  const secret = props.getProperty("ZAPZAP_SECRET");
  const instancia = props.getProperty("ZAPZAP_INSTANCE");
  const url = "https://api.zapzapapi.com/api/v1/" + instancia + "/send/text";

  const aba = SpreadsheetApp.getActiveSheet();
  const linhas = aba.getDataRange().getValues();

  for (let i = 1; i < linhas.length; i++) {
    const [nome, telefone, mensagem, status] = linhas[i];
    if (status || !telefone || !mensagem) continue;

    const texto = String(mensagem).replace("{nome}", nome);
    const resposta = UrlFetchApp.fetch(url, {
      method: "post",
      contentType: "application/json",
      headers: { "x-api-key": key, "x-api-secret": secret },
      payload: JSON.stringify({ number: String(telefone), text: texto }),
      muteHttpExceptions: true,
    });

    const codigo = resposta.getResponseCode();
    aba.getRange(i + 1, 4).setValue(
      codigo < 300 ? "OK " + new Date().toLocaleString("pt-BR") : "ERROR " + codigo + ": " + resposta.getContentText().slice(0, 120)
    );
    SpreadsheetApp.flush();
    Utilities.sleep(3000 + Math.floor(Math.random() * 4000));
  }
}

Como criar um menu dentro da planilha para disparar com um clique

Com a função onOpen, o Sheets ganha um menu WhatsApp ao abrir o arquivo. Quem opera a planilha não precisa entrar no editor de código. Na primeira execução o Google pede autorização para o script acessar a planilha e fazer requisições externas; é esperado.

Acrescente também uma função de teste que envia só para o seu número, para validar credenciais e instância antes de rodar a lista inteira.

Apps Script

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("WhatsApp")
    .addItem("Enviar pendentes", "enviarMensagens")
    .addItem("Enviar teste para mim", "enviarTeste")
    .addToUi();
}

function enviarTeste() {
  const props = PropertiesService.getScriptProperties();
  const url = "https://api.zapzapapi.com/api/v1/" + props.getProperty("ZAPZAP_INSTANCE") + "/send/text";
  const r = UrlFetchApp.fetch(url, {
    method: "post",
    contentType: "application/json",
    headers: { "x-api-key": props.getProperty("ZAPZAP_KEY"), "x-api-secret": props.getProperty("ZAPZAP_SECRET") },
    payload: JSON.stringify({ number: props.getProperty("MEU_NUMERO"), text: "Test from Google Sheets" }),
    muteHttpExceptions: true,
  });
  SpreadsheetApp.getUi().alert("Response " + r.getResponseCode() + ": " + r.getContentText());
}

Como enviar PDF ou imagem a partir de uma coluna da planilha

Acrescente uma coluna E com a URL pública do arquivo e use /send/media com type, file, text (legenda) e docName. O arquivo precisa estar acessível por URL pública; um link do Drive só funciona se estiver aberto para qualquer pessoa com o link e apontar para o arquivo em si, não para a página de visualização.

Para listas maiores, considere a fila de disparo da própria API (POST /sender/simple), que recebe um array de numbers, o tipo, o texto e um intervalo mínimo e máximo entre mensagens (delayMin e delayMax) e agenda o envio no servidor. Assim o seu script não precisa ficar rodando, o que também evita o limite de tempo de execução do Apps Script (consulte as cotas atuais na documentação do Google).

Apps Script

function enviarDocumento(numero, urlArquivo, nome, legenda) {
  const props = PropertiesService.getScriptProperties();
  const url = "https://api.zapzapapi.com/api/v1/" + props.getProperty("ZAPZAP_INSTANCE") + "/send/media";
  return UrlFetchApp.fetch(url, {
    method: "post",
    contentType: "application/json",
    headers: { "x-api-key": props.getProperty("ZAPZAP_KEY"), "x-api-secret": props.getProperty("ZAPZAP_SECRET") },
    payload: JSON.stringify({ number: numero, type: "document", file: urlArquivo, docName: nome, text: legenda }),
    muteHttpExceptions: true,
  });
}

Como receber as respostas dos clientes na própria planilha

Publique o script como aplicativo da Web (Implantar > Nova implantação > App da Web) e use a URL gerada como webhook da instância. A função doPost recebe o JSON do evento e grava uma linha em uma aba Respostas. Configure o webhook com POST em /webhook, enviando url, enabled true e events com messages.

Dois cuidados. Primeiro, a estrutura exata do evento deve ser conferida em um log real: grave o corpo cru na primeira versão e só depois mapeie os campos. Segundo, o Apps Script como web app tem limites de tempo e de execuções simultâneas; serve bem para volume baixo ou médio e não substitui um servidor para operação grande. O guia de webhook explica o desenho para volume maior.

Apps Script

function doPost(e) {
  const evento = JSON.parse(e.postData.contents);
  const aba = SpreadsheetApp.getActive().getSheetByName("Respostas") || SpreadsheetApp.getActive().insertSheet("Respostas");
  aba.appendRow([new Date(), JSON.stringify(evento).slice(0, 4000)]);
  return ContentService.createTextOutput("ok");
}

cURL (registrar o webhook)

curl -X POST https://api.zapzapapi.com/api/v1/SEU_INSTANCE_ID/webhook \
  -H "Content-Type: application/json" \
  -H "x-api-key: SUA_API_KEY" \
  -H "x-api-secret: SEU_API_SECRET" \
  -d '{"url":"https://script.google.com/macros/s/SEU_ID/exec","enabled":true,"events":["messages"]}'

Quais erros aparecem e o que fazer

SintomaCausa provávelSolução
ERROR 401 ou 403 na coluna StatusPropriedades do script erradas ou com espaçoReabrir as Propriedades e conferir os valores
Erro 400Telefone sem DDI, com símbolos ou vazioFormatar a coluna B como texto e limpar o número
Exceção de autorizaçãoPrimeira execução sem permissãoAutorizar o script quando o Google pedir
Script para no meioLimite de tempo de execuçãoEnviar em lotes ou usar a fila POST /sender/simple
Nada chega no WhatsApp, status OKInstância desconectadaVer GET /instance/status e reconectar por QR code
Webhook não gravaImplantação antiga ou sem acesso Qualquer pessoaCriar nova implantação como App da Web com acesso para qualquer pessoa

Antes de qualquer investigação, rode o teste do menu. Se ele responde 200, credenciais e instância estão corretas, e o problema está nos dados da planilha.

Boas práticas para usar planilha como painel de envio

Planilha é ótima para começar e para operações pequenas. Registre sempre o resultado por linha (para auditoria e para não reenviar), use um número dedicado à planilha, escreva mensagens com variação em vez de texto idêntico para toda a lista e dê uma saída fácil a quem não quer receber. Mensagens para quem não pediu contato são problema em qualquer ferramenta, e a LGPD vale igual.

Quando a lista ou a lógica crescer, migre a mesma ideia para n8n ou código, mantendo a API igual. A referência de send/text e o início rápido ajudam a dar o próximo passo.

Resumo rápido

  • No Apps Script, UrlFetchApp.fetch chama POST /send/text com x-api-key e x-api-secret em headers e JSON em payload.
  • Guarde chave e segredo em Propriedades do script, nunca no código da planilha.
  • Grave o resultado na coluna Status e pule linhas já enviadas para não duplicar.
  • Para listas grandes, use a fila POST /sender/simple em vez de manter o script rodando.
  • Receba respostas publicando o script como App da Web e registrando a URL em POST /webhook.

Perguntas frequentes

Preciso pagar algo do Google para usar o Apps Script?

O Apps Script é um recurso do Google Workspace e das contas pessoais, com cotas de uso que variam por tipo de conta. Consulte as cotas atuais na documentação do Google antes de planejar volume alto.

Como enviar para uma lista grande sem o script estourar o tempo?

Use a fila de disparo da API (POST /sender/simple) com os números em um array e um intervalo mínimo e máximo entre mensagens. O servidor agenda e envia, e o script só precisa fazer uma chamada.

O telefone aparece em notação científica na planilha. E agora?

Formate a coluna de telefone como Texto simples antes de colar os números e mantenha DDI e DDD, só dígitos. No script, converta com String(telefone).

Dá para receber a resposta do cliente na planilha?

Dá. Publique o script como App da Web com uma função doPost, registre a URL em POST /webhook com o evento messages e grave o corpo recebido em uma aba. Confira o formato do evento em um log real antes de mapear as colunas.

É seguro deixar a chave da API na planilha?

Guarde em Propriedades do script, não nas células nem no código. Ainda assim, quem tem acesso de edição pode lê-las. Limite quem edita, use uma instância dedicada e gere novas credenciais se alguém sair do projeto.

Transforme a sua planilha em canal de WhatsApp

Crie a conta, verifique o telefone e receba o crédito de boas-vindas, sem depositar (um por telefone, sujeito à campanha vigente). Conecte por QR code e cole o script.