WhatsApp Business API para solicitud de confirmación del cliente
El escenario envía al cliente una plantilla de WhatsApp personalizada pidiendo confirmar o cancelar una acción planificada.
Descripción del caso de uso
El escenario envía al cliente una plantilla de WhatsApp personalizada pidiendo confirmar o cancelar una acción planificada. El mensaje incluye el nombre del cliente, etiqueta de la acción, fecha y hora, y dirección. Los botones quick-reply «Confirmar» y «Cancelar» enrutan la elección del cliente vía webhook a tu backend.
Ejemplo de plantilla
Hola, {{1}}! Por favor confirma {{2}}. Fecha y hora: {{3}}. Dirección: {{4}}. Si necesitas ayuda — responde a este mensaje y te asistiremos.
[Confirmar]
Las etiquetas de botones quick-reply están fijadas en la plantilla de Meta — solo las variables del cuerpo se envían vía API; los toques regresan vía webhook.
Variables y propósito
{{1}}— nombre del cliente{{2}}— acción o asunto a confirmar (p. ej. reserva de consulta, entrega a domicilio){{3}}— fecha y hora (p. ej. 15 de julio, 14:00){{4}}— dirección o ubicación (p. ej. dirección o punto de retiro)
Ejemplo completado
Hola, Andrés! Por favor confirma cita de consulta. Fecha y hora: 15 de julio, 2:00 PM. Dirección: Calle Ejemplo 10. Si necesitas ayuda — responde a este mensaje y te asistiremos.
[Confirmar]
Cuándo usarlo
- citas y servicio de campo
- logística
- agencias
Valor para el negocio
- Back office or workflow triggers a confirmation request event
- System resolves recipient phone and confirmation details
- Template message is built with four body variables and two quick-reply buttons
- Client receives the WhatsApp confirmation request
- Confirm or cancel tap is processed via webhook to update the booking or process
Flujo de trabajo
- El sistema de agenda, CRM o flujo de trabajo emite un evento de solicitud de confirmación.
- El sistema obtiene el teléfono del destinatario y los campos de confirmación.
- Se construye un mensaje de plantilla personalizado con cuatro variables en el cuerpo y dos botones quick-reply.
- El cliente recibe la solicitud de confirmación en WhatsApp.
- Cuando el cliente toca Confirmar o Cancelar, el webhook entrega el evento para procesamiento en el backend.
- El progreso de entrega se reporta de forma asíncrona — típicamente
sent, luegodelivered(o failed/undelivered). - Tu sistema recibe el estado vía webhook (
hooks[]) o consultaGET …/hookInfo?messageId=<id>y maneja fallos si es necesario.
Implementación técnica
Requisitos previos
- Cuenta 1MSG con WhatsApp Business API conectada y plantilla de mensaje aprobada con dos botones quick-reply.
- Número de teléfono del cliente en formato internacional (sin
+ni espacios). - Payload de confirmación: nombre del cliente, etiqueta de acción, fecha/hora, dirección.
- Endpoint webhook HTTPS para recibir eventos de botones quick-reply.
Ejemplos de código
Node.js
#!/usr/bin/env node
// === Configuration (replace "___" placeholders) ===
const API_BASE_URL = "https://api.1msg.io"; // production 1MSG API base URL
const CHANNEL_ID = "___"; // channel ID from 1MSG dashboard
const API_TOKEN = "___"; // channel JWT token (Bearer)
const TEMPLATE_NAME = "___"; // approved template name
const TEMPLATE_NAMESPACE = "___"; // template namespace (422 without it)
const TEMPLATE_LANGUAGE = "___"; // template language code, e.g. "en"
// === Test data ===
const TEST_PHONE = "___"; // client phone in international format
const TEST_CUSTOMERNAME = "___"; // {{1}} customer name
const TEST_ACTION = "___"; // {{2}} action name
const TEST_DATE = "___"; // {{3}} date or time
const TEST_ADDRESS = "___"; // {{4}} address or location
function normalizePhone(phone) {
return String(phone).replace(/\D/g, "");
}
function assertConfigured(values) {
for (const [key, value] of Object.entries(values)) {
if (value === "___" || value === "" || value === undefined || value === null) {
throw new Error(`Missing configuration value: ${key}`);
}
}
}
async function sendTemplateMessage({ phone, customerName, action, date, address }) {
assertConfigured({
CHANNEL_ID,
API_TOKEN,
TEMPLATE_NAME,
TEMPLATE_NAMESPACE,
TEMPLATE_LANGUAGE,
phone,
customerName,
action,
date,
address,
});
const url = `${API_BASE_URL}/${CHANNEL_ID}/sendTemplate`;
// params carries body and button blocks (dynamic buttons).
const requestBody = {
phone: normalizePhone(phone),
template: TEMPLATE_NAME,
namespace: TEMPLATE_NAMESPACE,
language: {
policy: "deterministic",
code: TEMPLATE_LANGUAGE,
},
params: [
{
type: "body",
parameters: [
{ type: "text", text: String(customerName) }, // {{1}} customer name
{ type: "text", text: String(action) }, // {{2}} action name
{ type: "text", text: String(date) }, // {{3}} date or time
{ type: "text", text: String(address) }, // {{4}} address or location
],
},
],
};
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_TOKEN}`,
},
body: JSON.stringify(requestBody),
});
const raw = await res.text();
let data;
try {
data = JSON.parse(raw);
} catch {
data = null;
}
if (!res.ok || !data || data.sent !== true) {
console.error("Send failed. API response:");
console.error(raw);
process.exit(1);
}
console.log("Message sent to client.");
console.log("API response:", raw);
return data;
}
function handleIncomingMessage(message) {
const text = (message && (message.text || message.body || "")).trim();
// Meta quick_reply label (from template.txt) → route key (code_contract.ref) "confirm"
if (text === "Подтвердить") {
console.log("Reply received: confirm");
return { status: "confirm" };
}
// Meta quick_reply label (from template.txt) → route key (code_contract.ref) "cancel"
if (text === "Отменить") {
console.log("Reply received: cancel");
return { status: "cancel" };
}
console.log("Free-text reply — handle separately.");
return { status: "other" };
}
if (require.main === module) {
sendTemplateMessage({
phone: TEST_PHONE,
customerName: TEST_CUSTOMERNAME,
action: TEST_ACTION,
date: TEST_DATE,
address: TEST_ADDRESS,
}).catch((err) => {
console.error("Execution failed:", err.message);
process.exit(1);
});
}
module.exports = { sendTemplateMessage, handleIncomingMessage };
Respuesta inmediata de la API (síncrona)
- HTTP 2xx y JSON
"sent": truesignifican que 1MSG aceptó el mensaje para envío — no que ya llegó al teléfono del cliente. - Guarda el campo `id` de la respuesta (valor tipo
wamid.…). Úsalo para correlacionar callbacks de entrega o polling. - La respuesta también puede incluir
messageydescription— solo informativos.
Estado de entrega (asíncrono)
- Registra un webhook (
POST …/webhook) para que 1MSG envíe actualizaciones de entrega a tu endpoint HTTPS en un payload `hooks[]` separado (sent,delivered,read, o failed/undelivered cuando aplique). - Opcionalmente consulta:
GET {base}/{channel}/hookInfo?messageId=<id de sendTemplate>. - En la práctica, la entrega suele completarse en pocos segundos — pero eso no está garantizado por el contrato de la API.
Errores frecuentes
- Número de teléfono inválido o no normalizado
- Nombre de plantilla / namespace no aprobado o ausente
- Sin opt-in del cliente para mensajes comerciales de WhatsApp
- Cantidad de variables de plantilla incorrecta (422 de la API)
- Fallo de entrega — revisa el webhook de estado y la política de reintentos
Preguntas frecuentes
- ¿Necesito una plantilla aprobada? Sí — los mensajes cold-start de WhatsApp requieren una plantilla aprobada por Meta.
- ¿Puedo personalizar el texto? Las variables del cuerpo son dinámicas; el texto fijo y las etiquetas de botones se definen en la plantilla de Meta.
- ¿Cómo verifico la entrega?
sent: truesolo confirma aceptación. Rastrea la entrega vía webhookhooks[]oGET …/hookInfo?messageId=<id>. - ¿Qué pasa si no se entrega? Registra el hook failed/undelivered, verifica opt-in y estado de la plantilla, luego reintenta o usa otro canal.
- ¿Puedo conectarlo a mi CRM o backend? Sí — dispara la llamada a la API desde el webhook de tu plataforma o manejador de eventos.
CTA
¿Listo para usar solicitud de confirmación del cliente? Conecta tu canal 1MSG y ejecuta los ejemplos de código de arriba.
Recursos relacionados
Build WhatsApp automation in minutes
Use 1MSG to automate this workflow and try it with our free demo.
