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. 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. 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. 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. 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. 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. 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)

TipoQué dice la documentación¿Sirve para recibir webhooks?
Reserved VMRecursos constantes para una app que corre de forma continuaSí, es el encaje natural
AutoscaleAjusta los recursos según el usoPuede servir; prueba el tiempo de arranque
StaticSitios que no cambian según la entrada del usuarioNo: no corre un servidor
ScheduledCorre en los horarios que elijasNo 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.