Recordatorio de cita en WhatsApp por API: rutina, confirmación y LGPD
Una inasistencia es un horario perdido. El recordatorio 24 horas antes, con respuesta 1 para confirmar y 2 para reprogramar, es la automatización de menor esfuerzo para reducir faltas. Esta receta lo arma a partir de la agenda de tu propio sistema, sin depender de una plataforma de terceros, y cuida lo que suele salir mal: envío doble, mensaje a deshoras y datos sensibles en la conversación.
Publicado el · 4 min de lectura
Este artículo también está disponible en: Português · English
De dónde salen los horarios
La fuente de la verdad es tu agenda: una base de datos con la tabla de citas, un ERP o Google Calendar (mira la integración con Google Calendar). Esta receta supone una tabla appointments con estado, horario, nombre, teléfono, un campo consent_whatsapp (el paciente aceptó recibir recordatorios) y reminder_sent_at (nulo hasta el envío). Si tu agenda es un SaaS cerrado, el camino suele ser un webhook o una exportación; revisa qué ofrece antes de prometer el recordatorio.
El campo de consentimiento no es adorno. Los datos de agendamiento de salud son datos personales, y casi siempre sensibles por el contexto. Recoge el consentimiento en el registro o al agendar ('¿puedo recordarte la cita por WhatsApp?') y consulta el campo en el filtro del envío.
La rutina: ventana, seguro e intervalo
La rutina corre cada 15 minutos y busca citas confirmadas que empiecen entre 23 y 24 horas adelante, aún sin recordatorio y con consentimiento. La ventana de una hora, sumada al filtro reminder_sent_at, hace que cada cita se tome una sola vez. Antes de enviar, la rutina 'reclama' la fila con un UPDATE condicional: si dos ejecuciones se superponen, solo una logra marcar el envío. Si el envío falla, la marca se deshace para que la próxima ejecución lo intente de nuevo.
El intervalo aleatorio de unos segundos entre mensajes evita ráfagas. Para los recordatorios de una clínica el volumen es bajo, pero el principio vale: un ritmo humano reduce el riesgo. El envío usa POST /api/v1/{instanceId}/send/text con number y text, y ZapZap además aplica retraso y el indicador de escribiendo por defecto (antibloqueo). Verifica antes si el número tiene WhatsApp con la ruta chat/check, descrita en la referencia.
Node.js (cron a cada 15 minutos)
// Runs every 15 minutes (cron, queue worker or n8n schedule). Appointments come from YOUR system.
async function remindAppointments() {
const rows = await db.query(`
SELECT id, patient_name, patient_phone, starts_at
FROM appointments
WHERE status = 'confirmed' AND reminder_sent_at IS NULL AND consent_whatsapp = TRUE
AND starts_at BETWEEN now() + interval '23 hours' AND now() + interval '24 hours'`);
for (const a of rows.rows) {
// claim before sending: a second run cannot pick the same row
const claim = await db.query("UPDATE appointments SET reminder_sent_at = now() WHERE id = $1 AND reminder_sent_at IS NULL", [a.id]);
if (!claim.rowCount) continue;
const when = new Date(a.starts_at).toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "short", timeZone: "America/Sao_Paulo" });
try {
await sendWhatsApp(a.patient_phone, "Oi, " + a.patient_name.split(" ")[0] + ". Lembrete da sua consulta amanhã, " + when + ". Responda 1 para confirmar ou 2 para remarcar.");
} catch (e) {
await db.query("UPDATE appointments SET reminder_sent_at = NULL WHERE id = $1", [a.id]); // let the next run retry
console.error("reminder failed", a.id, e.message);
}
await new Promise((r) => setTimeout(r, 4000 + Math.random() * 6000)); // human-like gap between sends
}
}
// Shared helper: one send through the ZapZap API
async function sendWhatsApp(number, text) {
const r = await fetch(`https://api.zapzapapi.com/api/v1/${process.env.ZAPZAP_INSTANCE}/send/text`, {
method: "POST",
headers: {
"x-api-key": process.env.ZAPZAP_API_KEY,
"x-api-secret": process.env.ZAPZAP_API_SECRET,
"Content-Type": "application/json",
},
body: JSON.stringify({ number, text }),
});
if (!r.ok) throw new Error("ZapZap " + r.status + ": " + (await r.text()));
}Recibir el 1 o el 2
Para que la respuesta funcione, la instancia debe entregar los mensajes recibidos a tu servidor: registra la URL con PUT /api/v1/instances/{id}/webhook y el evento de mensajes (configurar webhook). Descarta los mensajes fromMe, o tu propia respuesta automática puede volver a entrar al flujo y crear un bucle. Revisa el formato exacto del payload con un mensaje real antes de escribir el código; el ejemplo de abajo muestra solo la lógica.
Trata con cuidado las respuestas fuera de lo esperado: 'ok', 'sí' y 'confirmo' también son confirmaciones. Mapea algunas variaciones y, si no entiendes, no respondas solo: deriva a recepción. Para validar el encabezado X-ZapZap-Signature y descartar entregas repetidas, sigue la guía de webhooks en producción.
Node.js (Express)
// Receive the patient's "1" or "2" (message webhook registered with PUT /api/v1/instances/{id}/webhook, event "messages")
app.post("/zapzap", express.json(), async (req, res) => {
res.sendStatus(200);
const m = req.body; // check the exact shape with a real message first (see the webhooks reference)
if (m.fromMe) return; // never react to your own messages, or an automatic reply answers itself in a loop
const text = String(m.text || "").trim();
if (text === "1") await markConfirmed(m.from);
else if (text === "2") await askReschedule(m.from);
});El texto: qué decir y qué NO decir
Di lo esencial: el nombre del paciente, el día y la hora, el nombre de la clínica y cómo confirmar o reprogramar. No escribas el procedimiento, el nombre del médico en una especialidad sensible, el resultado de un examen ni el diagnóstico. WhatsApp es un canal en el que el dispositivo puede estar con otra persona, y un dato de salud en una notificación es una filtración esperando a ocurrir. 'Cita con la Dra. Ana, mañana a las 14 h' es suficiente.
Si la clínica tiene exigencias del colegio profesional sobre la comunicación con pacientes, sigue la norma de tu colegio. Esta receta es técnica y no sustituye la orientación jurídica ni ética.
Límites y riesgos
El recordatorio de cita para quien agendó contigo es el uso de menor riesgo que existe. Aun así, ZapZap conecta números por un canal no oficial y gestionado, y ningún recurso garantiza la ausencia de bloqueo. Mantén el ritmo, no envíes a quien pidió parar y no aproveches el recordatorio para difundir promociones.
Prueba primero con citas ficticias y con tu número. Revisa la zona horaria (el ejemplo usa America/Sao_Paulo), la ventana de 23 a 24 horas y, sobre todo, que correr la rutina dos veces seguidas no genere un segundo mensaje. Para segmentos específicos, mira las páginas de clínicas y dentistas.
Resumen rápido
- Rutina cada 15 minutos, ventana de 23 a 24 horas y UPDATE condicional para no enviar nunca dos veces.
- Consentimiento en el registro y campo consultado en el filtro del envío: los datos de salud piden cuidado.
- Mensaje sin procedimiento ni diagnóstico; respuesta 1 o 2 por webhook, ignorando fromMe.
- Prueba la idempotencia corriendo la rutina dos veces y comprobando que sale un solo mensaje.
Sigue leyendo
Lee también
Preguntas frecuentes
¿Cuál es el mejor momento para recordarle al paciente?
Un recordatorio 24 horas antes suele dar tiempo de reprogramar. Algunos negocios agregan un segundo, pocas horas antes. Prueba con tu agenda; la receta usa la ventana de 23 a 24 horas.
¿Necesito consentimiento para recordar una cita?
Los datos de agendamiento de salud son datos personales y suelen ser sensibles. Recoge el consentimiento en el registro o al agendar, regístralo y consúltalo antes de cada envío. Ante la duda, consulta a tu asesoría jurídica.
¿Cómo evito enviar el recordatorio dos veces?
Con un campo de control y un UPDATE condicional que reclama la fila antes del envío. Si dos ejecuciones se superponen, solo una logra marcar. Si el envío falla, deshaz la marca para reintentar.
¿Puedo mencionar el procedimiento en el mensaje?
No lo recomendamos. El dispositivo puede estar con otra persona, y un dato de salud en una notificación se filtra con facilidad. Usa nombre, día, hora, clínica y cómo confirmar o reprogramar.
¿Cómo recibo la respuesta del paciente?
Registra la URL de tu servidor como webhook de la instancia con el evento de mensajes, ignora los mensajes fromMe y trata el 1 y el 2. Revisa el formato del payload con un mensaje real antes de programar.
¿Esto puede bloquear el número de la clínica?
Puede. ZapZap usa un canal no oficial y ningún recurso elimina el riesgo. Un recordatorio para quien agendó, con ritmo humano y salida para parar, es el uso de menor riesgo, pero no es riesgo cero.
Reduce las faltas con un recordatorio simple
Conecta el número de la clínica y corre la rutina de la receta. R$15 por número activo al mes, con crédito de bienvenida sujeto a la campaña vigente.
