Entorno de código y hosting
WhatsApp en Replit: receptor de webhook con firma, Secrets y Reserved VM
Replit es un entorno de desarrollo en el navegador con hosting. Su documentación (leída el 11/11/2026) explica que cada Secret se vuelve una variable de entorno del proyecto y que la publicación puede ser Autoscale, Static, Reserved VM o Scheduled. Para ZapZap, el punto fuerte es correr un servidor que recibe webhooks y responde mensajes.
Actualizado el
Por qué conectar
Replit es bueno para el lado que las herramientas de frontend no cubren: un servidor siempre disponible que recibe cada evento de ZapZap, verifica la firma, evita procesar el mismo evento dos veces y activa tu lógica o un modelo de IA. ZapZap conecta números por un canal no oficial: WhatsApp puede limitar o bloquear cualquier número, así que el ritmo, el consentimiento y el calentamiento importan. Cuesta R$15 por número activo al mes, prepago, sin cobro por mensaje, y empiezas con un crédito de bienvenida al verificar el teléfono, sujeto a la campaña vigente.
Paso a paso
- 1
Crea el proyecto y registra los Secrets
En el editor del proyecto, abre Tools > Secrets (en Setup), haz clic en New Secret y crea ZAPZAP_API_KEY y ZAPZAP_API_SECRET (de Settings > API en app.zapzapapi.com). Quedan disponibles como variables de entorno. La documentación advierte que quien tiene acceso al proyecto, y los colaboradores, pueden ver los valores: no invites a quien no deba verlos.
- 2
Levanta el receptor de webhook
Pega el servidor del ejemplo en server.mjs y ejecútalo. Lee el cuerpo bruto, valida X-ZapZap-Signature (t=<unix>,v1=<hmac_hex>, HMAC-SHA256 sobre t + punto + cuerpo, tolerancia de 5 minutos), responde 200 rápido y guarda el delivery_id para ignorar repeticiones.
- 3
Elige el tipo de publicación
Para un receptor que debe estar siempre encendido, la documentación de Replit describe Reserved VM como recursos constantes para una app que corre de forma continua. Autoscale ajusta los recursos al uso: verifica si tu volumen de eventos tolera el tiempo de arranque.
- 4
Registra el webhook en la instancia
Llama a PUT https://api.zapzapapi.com/api/v1/instances/{id}/webhook con webhook_url (HTTPS) y events (por ejemplo, messages). La respuesta trae el webhook_secret (whsec_...): guárdalo como Secret ZAPZAP_WEBHOOK_SECRET. Prueba con POST /instances/{id}/webhook/test.
- 5
Responde por la API
Para responder, llama a POST /{instanceId}/send/text con number y text después de haber respondido 200 al webhook. El envelope trae delivery_id, event_id, instance_id, instance_name, event y data.
- 6
Depura las entregas
GET /instances/{id}/webhook/deliveries muestra las entregas recientes. Si tu servidor falla, ZapZap reintenta con intervalos crecientes, por eso importa deduplicar por delivery_id.
Ejemplo
// server.mjs: receives the ZapZap webhook, checks the signature and ignores repeated deliveries.
// Replit Secrets: ZAPZAP_WEBHOOK_SECRET (the whsec_ value returned when you set the webhook)
import { createServer } from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";
const seen = new Set();
function valid(raw, header) {
const p = Object.fromEntries(String(header || "").split(",").map((kv) => kv.split("=")));
if (!p.t || !p.v1 || Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const mac = createHmac("sha256", process.env.ZAPZAP_WEBHOOK_SECRET).update(p.t + "." + raw).digest("hex");
return mac.length === p.v1.length && timingSafeEqual(Buffer.from(mac), Buffer.from(p.v1));
}
createServer((req, res) => {
let raw = "";
req.on("data", (c) => (raw += c));
req.on("end", () => {
if (req.method !== "POST" || !valid(raw, req.headers["x-zapzap-signature"])) {
res.writeHead(401).end();
return;
}
const env = JSON.parse(raw);
res.writeHead(200).end("ok"); // answer fast, work afterwards
if (seen.has(env.delivery_id)) return;
seen.add(env.delivery_id);
console.log("event", env.event, "delivery", env.delivery_id);
});
}).listen(process.env.PORT || 3000, "0.0.0.0");Ilustrativo. Los campos y endpoints exactos están en la documentación.
Tipos de publicación de Replit, según la documentación (11/11/2026)
| Tipo | Qué dice la documentación | ¿Sirve para recibir webhooks? |
|---|---|---|
| Reserved VM | Recursos constantes para una app que corre de forma continua | Sí, es el encaje natural |
| Autoscale | Ajusta los recursos según el uso | Puede servir; prueba el tiempo de arranque |
| Static | Sitios que no cambian según la entrada del usuario | No: no corre un servidor |
| Scheduled | Corre en los horarios que elijas | No para webhooks; sirve para rutinas |
Preguntas frecuentes
¿Cómo llegan los Secrets de Replit al código?
Como variables de entorno, leídas con process.env en Node.js. La documentación leída el 11/11/2026 también advierte que los colaboradores con acceso pueden ver los valores.
¿Debo validar la firma?
Sí, si la URL es pública. Sin eso, cualquiera puede simular eventos. La validación usa el webhook_secret devuelto al configurar el webhook.
¿Por qué el mismo evento puede llegar dos veces?
Si tu servidor no responde con éxito, ZapZap reintenta. Guarda el delivery_id e ignora las repeticiones.
¿Replit sirve para campañas masivas?
El entorno sirve, pero ZapZap es una solución no oficial y los envíos masivos y los mensajes idénticos en secuencia aumentan el riesgo de bloqueo. Envía solo a quien tiene relación contigo.
¿Cuánto cuesta ZapZap?
Cuesta R$15 por número activo al mes, prepago, sin cobro por mensaje, y empiezas con un crédito de bienvenida al verificar el teléfono, sujeto a la campaña vigente.
Empieza ahora
Crea tu cuenta, conecta un número en minutos y prueba la API con calentamiento y anti-bloqueo. R$15 por número activo/mes, prepago, sin permanencia.
