Enviar WhatsApp con Ruby: ejemplo de API REST (Net::HTTP y Rails)
Para enviar WhatsApp con Ruby haces un POST con Net::HTTP a /api/v1/{instanceId}/send/text, con los encabezados x-api-key y x-api-secret y un JSON con number y text. Net::HTTP viene en la biblioteca estándar, así que no hay gem que instalar. Abajo: texto, multimedia, retry, un job en Rails y un webhook en Sinatra.
Publicado el · 3 min de lectura
Este artículo también está disponible en: Português · English
Qué necesitas antes de empezar
Cuenta en ZapZap, instancia conectada por código QR, clave y secreto de la API y el ID de la instancia. La autenticación usa x-api-key y x-api-secret. Guarda las credenciales en ENV o en las credentials de Rails.
Prueba primero con cURL, para separar error de código de error de cuenta.
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."}'Cómo enviar un mensaje de texto con Net::HTTP
La clase de abajo reutiliza la configuración, define open_timeout y read_timeout y lanza un error con estado y cuerpo cuando la respuesta no es 2xx. Usa JSON.generate para el cuerpo.
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.")Cómo enviar imagen, PDF y audio con Ruby
La ruta es /send/media. El campo type define el tipo (image, video, audio, ptt, document, sticker y otros), file recibe la URL pública, text es el pie y docName el nombre del 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
endCómo hacer retry y enviar desde Rails con Active Job
Repite solo errores transitorios (429 y 5xx) con espera creciente. En Rails, usa un job: el retry_on de Active Job repite solo; el job solo deja subir el error en 429 y 5xx y apenas registra los 4xx, que nunca van a funcionar. Así la solicitud del usuario no espera a 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.")Cómo recibir las respuestas con un webhook en Sinatra
Configura la URL en configurar webhook y activa el evento messages. El mensaje recibido está en data.message (text, chatid, fromMe). Ignora fromMe verdadero y responde 200 enseguida. En Rails, el equivalente es un controller con skip_forgery_protection.
La validación de la firma y la deduplicación siguen la guía de webhook en producción.
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
endLista de verificación antes de producción
Credenciales en ENV o credentials; timeouts de apertura y lectura; error con estado y cuerpo; retry solo para 429 y 5xx; envío por job; firma del webhook validada; y enviar solo a quien aceptó recibir.
Otros lenguajes: Python, Node.js, PHP, Go. Todos los endpoints están en la referencia de la API.
Resumen rápido
- Net::HTTP de la biblioteca estándar basta: no hace falta ninguna gem.
- Define open_timeout y read_timeout y lanza error con estado y cuerpo.
- En Rails, envía por Active Job con retry_on para fallos transitorios.
- En el webhook, ignora fromMe y responde 200 antes de procesar.
Sigue leyendo
Lee también
Preguntas frecuentes
¿Puedo usar Faraday o HTTParty en lugar de Net::HTTP?
Sí. La API es REST: los encabezados x-api-key y x-api-secret y el JSON con number y text son los mismos en cualquier cliente HTTP.
¿Cómo guardo las credenciales en Rails?
En credentials cifradas o variables de entorno. Nunca en el repositorio ni en el front-end.
¿Se ejecutó el ejemplo contra la API?
El código usa los endpoints y campos de la referencia de la API. Ejecuta el cURL del inicio en tu instancia y luego el Ruby, y revisa la respuesta antes de pasar a producción.
¿Usar una API no oficial puede llevar al bloqueo del número?
Puede. Una API no oficial va contra los términos de WhatsApp y el número puede ser limitado o bloqueado. Opt-in, ritmo humano, calentamiento y lista limpia reducen el riesgo, pero no hay garantía. Para un canal homologado, usa la API oficial de Meta.
Ejecuta Ruby en tu instancia
Crea la cuenta, conecta un número y envía el primer mensaje. R$15 por número activo al mes, sin cobro por mensaje.
