Pasar al contenido principal
osky
Cómo registrar motivos de deals perdidos en SendPulse CRM y disparar flujos en tu WhatsApp Bot
Tema SendPulse CRM + WhatsApp Bot

Registra motivos de deals perdidos en SendPulse CRM y conecta tu WhatsApp Bot

SendPulse lanzó algo que parece menor pero que en la práctica vale oro para cualquier equipo de ventas que opere con bots: ahora puedes marcar un deal como perdido, elegir un motivo predefinido y agregar un comentario libre. Esos datos quedan guardados en el CRM, aparecen en los exports y —lo que más nos interesa aquí— puedes usarlos como disparadores para flujos automáticos en tu bot de WhatsApp.

Diagrama de flujo de webhook conectando CRM con bot de WhatsApp

Este tutorial asume que ya tienes SendPulse activo con CRM habilitado y un bot de WhatsApp corriendo en la misma cuenta. Si no tienes el bot levantado, primero ve a Chatbots > Crear bot > WhatsApp y conecta tu número. Aquí nos enfocamos en la integración entre el CRM y el bot.


Requisitos

  • Cuenta SendPulse con plan que incluya CRM y Chatbots (plan Standard o superior en 2026)
  • WhatsApp Business conectado vía SendPulse (número verificado)
  • Deals existentes en tu pipeline
  • Acceso a la API de SendPulse (Client ID y Client Secret desde Cuenta > API)
  • Node.js 20+ o Python 3.11+ si vas a usar webhooks externos
  • Ngrok o un servidor con HTTPS para recibir webhooks en desarrollo

Paso 1: Configura los motivos de pérdida en el CRM

Antes de automatizar nada, define tus catálogos de motivos. Hazlo bien desde el inicio porque cambiarlos después rompe filtros y reportes.

  1. Ve a CRM > Configuración > Motivos de pérdida
  2. Crea los motivos que apliquen a tu negocio. Ejemplos reales:
    • precio_alto
    • eligio_competencia
    • no_respondio
    • fuera_de_presupuesto
    • proyecto_cancelado
  3. Guarda. Estos slugs son los que vas a usar en tus condiciones de automatización.

Cuando marques un deal como perdido, el sistema te muestra un dropdown con estos motivos y un campo de comentario libre. Ese comentario queda almacenado como lost_comment en el objeto del deal.


Paso 2: Activa el webhook de CRM en SendPulse

SendPulse permite disparar un webhook cuando el estado de un deal cambia. Esto es lo que conecta el CRM con tu lógica externa.

  1. Ve a CRM > Configuración > Webhooks
  2. Agrega una URL de destino (tu servidor o ngrok en desarrollo): https://tudominio.com/webhooks/crm
  3. Selecciona el evento: Deal status changed
  4. Guarda y copia el Webhook Secret que te genera SendPulse

El payload que recibirás tiene esta estructura:

{
  "event": "deal_status_changed",
  "deal": {
    "id": "deal_abc123",
    "name": "Paquete DJ Bodas - Cliente Ramírez",
    "status": "lost",
    "lost_reason": "precio_alto",
    "lost_comment": "Dijo que encontró algo más barato en Guadalajara",
    "contact": {
      "id": "contact_xyz789",
      "name": "Carlos Ramírez",
      "phone": "521234567890"
    },
    "pipeline_id": "pipe_001",
    "updated_at": "2026-06-06T14:32:00Z"
  }
}

Paso 3: Servidor webhook que valida y enruta

Aquí está el núcleo. Un servidor Express sencillo que recibe el webhook, valida la firma y decide qué flujo de WhatsApp disparar según el motivo.

// webhook-crm.js
import express from 'express';
import crypto from 'crypto';
import axios from 'axios';

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.SENDPULSE_WEBHOOK_SECRET;
const SP_CLIENT_ID = process.env.SP_CLIENT_ID;
const SP_CLIENT_SECRET = process.env.SP_CLIENT_SECRET;

// Mapa de motivo -> ID de flujo en SendPulse Chatbot
const FLOW_MAP = {
  precio_alto: 'flow_reactivacion_precio',
  eligio_competencia: 'flow_diferenciadores',
  no_respondio: 'flow_ultimo_intento',
  fuera_de_presupuesto: 'flow_plan_economico',
  proyecto_cancelado: 'flow_recontacto_futuro',
};

function validarFirma(req) {
  const signature = req.headers['x-sendpulse-signature'];
  const body = JSON.stringify(req.body);
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(body)
    .digest('hex');
  return signature === expected;
}

async function obtenerToken() {
  const res = await axios.post('https://api.sendpulse.com/oauth/access_token', {
    grant_type: 'client_credentials',
    client_id: SP_CLIENT_ID,
    client_secret: SP_CLIENT_SECRET,
  });
  return res.data.access_token;
}

async function dispararFlujo(phone, flowId, variables) {
  const token = await obtenerToken();
  await axios.post(
    'https://api.sendpulse.com/whatsapp/contacts/sendFlow',
    {
      phone,
      flow_id: flowId,
      variables,
    },
    { headers: { Authorization: `Bearer ${token}` } }
  );
}

app.post('/webhooks/crm', async (req, res) => {
  if (!validarFirma(req)) {
    return res.status(401).json({ error: 'Firma inválida' });
  }

  const { event, deal } = req.body;

  if (event !== 'deal_status_changed' || deal.status !== 'lost') {
    return res.status(200).json({ ok: true, skipped: true });
  }

  const flowId = FLOW_MAP[deal.lost_reason];
  if (!flowId) {
    console.warn(`Motivo sin flujo asignado: ${deal.lost_reason}`);
    return res.status(200).json({ ok: true, no_flow: true });
  }

  await dispararFlujo(deal.contact.phone, flowId, {
    nombre: deal.contact.name,
    motivo: deal.lost_reason,
    comentario: deal.lost_comment || '',
    deal_nombre: deal.name,
  });

  console.log(`Flujo ${flowId} disparado para ${deal.contact.phone}`);
  res.status(200).json({ ok: true });
});

app.listen(3000, () => console.log('Webhook escuchando en :3000'));

Paso 4: Crea los flujos en SendPulse Chatbot

Para cada motivo, crea un flujo en Chatbots > Flujos. Usa las variables que pasaste ({{nombre}}, {{motivo}}, {{comentario}}) en los mensajes.

Ejemplo de mensaje para precio_alto:

Hola {{nombre}}, sabemos que el precio fue un factor. Tenemos un plan de pagos que quizás no conocías. ¿Te cuento en 2 minutos?

Agrega botones de respuesta rápida: Sí, cuéntame y No gracias. Según la respuesta, bifurca a un agente humano o cierra con una despedida digna.


Paso 5: Exporta y analiza los datos

Los motivos quedan en el export CSV del CRM. Para automatizar reportes semanales puedes usar la API:

curl -X GET "https://api.sendpulse.com/crm/deals?status=lost&date_from=2026-06-01" \
  -H "Authorization: Bearer TU_TOKEN" | jq '[.[] | {motivo: .lost_reason, contacto: .contact.name}]'

Mete ese JSON a una hoja de Google Sheets con Apps Script o a un dashboard de Metabase. En 2026 ya no hay excusa para no tener esto automatizado.


Advertencias de producción

  • No dispares flujos si el contacto ya está en otro flujo activo. Revisa el estado del contacto en la API antes de llamar sendFlow. SendPulse puede silenciar mensajes si detecta spam interno.
  • El token de OAuth expira en 3600 segundos. Cachéalo con Redis o en memoria con timestamp, no lo pidas en cada request.
  • Valida siempre la firma del webhook. En producción llegan requests basura. Sin validación, cualquiera puede disparar flujos en tu bot.
  • Responde 200 rápido. Si tu lógica tarda más de 5 segundos, SendPulse reintenta el webhook. Procesa en background con una cola (BullMQ, por ejemplo) y responde inmediatamente.
  • Cuidado con duplicados. Si un deal se marca como perdido, se edita y se vuelve a marcar, recibirás el webhook dos veces. Guarda el deal.id + timestamp en Redis con TTL de 60 segundos para deduplicar.

Resultado esperado

Cuando tu equipo marca un deal como perdido con motivo precio_alto, en menos de 10 segundos el contacto recibe un WhatsApp personalizado con una oferta alternativa. Sin intervención humana. Sin que nadie se acuerde de hacer el seguimiento. Y con datos limpios en tu CRM para saber exactamente por qué estás perdiendo negocio.

Eso es lo que hace la diferencia entre un bot de WhatsApp decorativo y uno que realmente trabaja.