Enviar WhatsApp com Ruby: exemplo de API REST (Net::HTTP e Rails)
Para enviar WhatsApp com Ruby você faz um POST com Net::HTTP em /api/v1/{instanceId}/send/text, com os cabeçalhos x-api-key e x-api-secret e um JSON com number e text. Net::HTTP vem na biblioteca padrão, então não há gem a instalar. Abaixo: texto, mídia, retry, um job no Rails e um webhook em Sinatra.
Publicado em · 2 min de leitura
O que você precisa antes de começar
Conta no ZapZap, instância conectada por QR code, chave e segredo da API e o ID da instância. A autenticação usa x-api-key e x-api-secret. Guarde as credenciais em ENV ou nas credentials do Rails.
Teste com cURL primeiro, para separar erro de código de erro de conta.
cURL
curl -X POST https://api.zapzapapi.com/api/v1/SEU_INSTANCE_ID/send/text \
-H "Content-Type: application/json" \
-H "x-api-key: SUA_API_KEY" \
-H "x-api-secret: SEU_API_SECRET" \
-d '{"number":"5511999999999","text":"Olá! Teste de envio pela API."}'Como enviar uma mensagem de texto com Net::HTTP
A classe abaixo reaproveita a configuração, define open_timeout e read_timeout e levanta um erro com status e corpo quando a resposta não é 2xx. Use JSON.generate para o corpo.
Ruby
require "net/http"
require "json"
class ZapZap
class Erro < StandardError
attr_reader :status, :corpo
def initialize(status, corpo)
@status, @corpo = status, corpo
super("ZapZap #{status}: #{corpo}")
end
end
def initialize(instancia: ENV.fetch("ZAPZAP_INSTANCE_ID"),
key: ENV.fetch("ZAPZAP_API_KEY"),
secret: ENV.fetch("ZAPZAP_API_SECRET"))
@base = "https://api.zapzapapi.com/api/v1/#{instancia}"
@headers = { "Content-Type" => "application/json",
"x-api-key" => key, "x-api-secret" => secret }
end
def enviar_texto(numero, texto)
post("/send/text", number: numero, text: texto)
end
def post(caminho, dados)
uri = URI(@base + caminho)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.open_timeout = 5
http.read_timeout = 30
r = http.post(uri.path, JSON.generate(dados), @headers)
raise Erro.new(r.code.to_i, r.body) unless r.is_a?(Net::HTTPSuccess)
JSON.parse(r.body)
end
end
puts ZapZap.new.enviar_texto("5511999999999", "Olá! Seu pedido saiu para entrega.")Como enviar imagem, PDF e áudio com Ruby
A rota é /send/media. O campo type define o tipo (image, video, audio, ptt, document, sticker e outros), file recebe a URL pública, text é a legenda e docName o nome do documento. Para PDF, type document.
Ruby
class ZapZap
def enviar_pdf(numero, url, nome, legenda = "")
post("/send/media", number: numero, type: "document",
file: url, docName: nome, text: legenda)
end
endComo fazer retry e enviar pelo Rails com Active Job
Repita só erros transitórios (429 e 5xx) com espera crescente. No Rails, use um job: o Active Job com retry_on repete sozinho; o job só deixa o erro subir para 429 e 5xx e apenas registra os 4xx, que nunca vão funcionar. Assim a requisição do usuário não espera o WhatsApp.
Ruby (Rails)
class EnviarWhatsappJob < ApplicationJob
queue_as :default
retry_on ZapZap::Erro, Net::OpenTimeout, Net::ReadTimeout,
wait: :polynomially_longer, attempts: 4
def perform(numero, texto)
ZapZap.new.enviar_texto(numero, texto)
rescue ZapZap::Erro => e
raise if e.status == 429 || e.status >= 500 # o retry_on repete
Rails.logger.error(e.message) # 4xx: repetir não resolve, só registra
end
end
# Uso: EnviarWhatsappJob.perform_later("5511999999999", "Seu pedido foi enviado.")Como receber as respostas com um webhook em Sinatra
Configure a URL em configurar webhook e ligue o evento messages. A mensagem recebida está em data.message (text, chatid, fromMe). Ignore fromMe verdadeiro e responda 200 logo. No Rails, o equivalente é um controller com skip_forgery_protection.
A validação da assinatura e a deduplicação seguem o guia de webhook em produção.
Ruby (Sinatra)
require "sinatra"
require "json"
post "/webhooks/zapzap" do
evento = JSON.parse(request.body.read)
msg = evento.dig("data", "message")
if evento["event"] == "messages" && msg && !msg["fromMe"]
Thread.new { puts "mensagem de #{msg["chatid"]}: #{msg["text"]}" }
end
status 200
endChecklist antes de colocar em produção
Credenciais em ENV ou credentials; timeouts de abertura e leitura; erro com status e corpo; retry só para 429 e 5xx; envio por job; assinatura do webhook validada; e envio só para quem aceitou receber.
Outras linguagens: Python, Node.js, PHP, Go. Endpoints completos na referência da API.
Resumo rápido
- Net::HTTP da biblioteca padrão basta: nenhuma gem é necessária.
- Defina open_timeout e read_timeout e levante erro com status e corpo.
- No Rails, envie por Active Job com retry_on para falhas transitórias.
- No webhook, ignore fromMe e responda 200 antes de processar.
Continue lendo
Perguntas frequentes
Posso usar Faraday ou HTTParty no lugar do Net::HTTP?
Pode. A API é REST: os cabeçalhos x-api-key e x-api-secret e o JSON com number e text são os mesmos em qualquer cliente HTTP.
Como guardar as credenciais no Rails?
Em credentials criptografadas ou variáveis de ambiente. Nunca no repositório nem no front-end.
O exemplo foi executado contra a API?
O código usa os endpoints e campos da referência da API. Rode o cURL do início na sua instância e depois o Ruby, e confira a resposta antes de ir para produção.
Usar a API não oficial pode levar a bloqueio do número?
Pode. Uma API não oficial viola os termos do WhatsApp e o número pode ser limitado ou bloqueado. Opt-in, ritmo humano, aquecimento e lista limpa reduzem o risco, sem garantia. Para canal homologado, use a API oficial da Meta.
Rode o Ruby na sua instância
Crie a conta, conecte um número e envie a primeira mensagem. R$15 por número ativo ao mês, sem cobrança por mensagem.
