Raphael Serafim· Publicado el 10 de septiembre de 2026· 9 min de lectura

Varios agentes en el mismo número de WhatsApp: cómo hacerlo por la API

Un número, una conversación por cliente y un equipo entero atendiendo — sin que dos personas respondan lo mismo. Cómo distribuir, bloquear la conversación por agente, transferir y pasar del bot al humano sin que el cliente repita todo.

Ver como Markdown

La aplicación de WhatsApp no fue hecha para equipos. Tiene límite de dispositivos conectados, no reparte conversaciones, no registra quién respondió qué y no impide que dos personas contesten lo mismo.

Por la API el problema cambia de naturaleza: los mensajes llegan a tu sistema, y decides quién atiende.

El diseño

Cliente  →  WhatsApp  →  webhook  →  tu sistema
                                        ↓
                                   distribución
                                        ↓
                             agente 1, 2, 3…
                                        ↓
                            respuesta  →  API  →  Cliente

Para el cliente existe una sola conversación, con un solo número. Toda la estructura del equipo es invisible — y así debe ser.

Las tablas mínimas

CREATE TABLE conversaciones (
  id             BIGSERIAL PRIMARY KEY,
  numero         VARCHAR(20) UNIQUE NOT NULL,
  agente_id      BIGINT,                        -- NULL = en la cola
  estado         VARCHAR(20) DEFAULT 'abierta', -- abierta|pendiente|cerrada
  cola           VARCHAR(30) DEFAULT 'general',
  asignada_en    TIMESTAMPTZ,
  ultima_en      TIMESTAMPTZ
);

CREATE TABLE mensajes (
  id                BIGSERIAL PRIMARY KEY,
  conversacion_id   BIGINT NOT NULL,
  direccion         VARCHAR(10) NOT NULL,    -- entrada|salida
  agente_id         BIGINT,                  -- quién respondió
  texto             TEXT,
  message_id        VARCHAR(80) UNIQUE,      -- idempotencia
  creado_en         TIMESTAMPTZ DEFAULT NOW()
);

El numero UNIQUE garantiza una conversación por cliente. El message_id UNIQUE evita mensajes duplicados cuando el webhook se reentrega.

Recibir y distribuir

async function alRecibir(msg) {
  const conv = await obtenerOCrearConversacion(msg.from);

  await db.mensajes.crear({
    conversacion_id: conv.id,
    direccion: 'entrada',
    texto: msg.text?.body,
    message_id: msg.id,          // el duplicado falla acá, y está bien
  });

  // Una conversación ya asignada sigue con quien la está atendiendo.
  // Cambiar de agente a la mitad es una pésima experiencia.
  if (conv.agente_id) {
    return notificarAgente(conv.agente_id, conv.id);
  }

  const agente = await elegirAgente(conv.cola);
  if (!agente) {
    await db.conversaciones.actualizar(conv.id, { estado: 'pendiente' });
    return avisarFueraDeHorario(msg.from);
  }

  await asignar(conv.id, agente.id);
}

El bloqueo que evita la respuesta duplicada

Esta es la parte que da problemas en producción si se hace de forma ingenua:

// MAL: dos agentes pasan por el "if" al mismo tiempo
if (!conv.agente_id) {
  await db.conversaciones.actualizar(conv.id, { agente_id: miId });
}

// BIEN: la condición va dentro del propio UPDATE, y lo resuelve la base
async function asignar(conversacionId, agenteId) {
  const r = await db.query(
    `UPDATE conversaciones
        SET agente_id = $1, asignada_en = NOW(), estado = 'abierta'
      WHERE id = $2 AND agente_id IS NULL
      RETURNING id`,
    [agenteId, conversacionId],
  );
  return r.rowCount > 0;   // false = otro agente la tomó primero
}

El WHERE agente_id IS NULL dentro del UPDATE es lo que hace la operación atómica. Sin eso, dos agentes abren la misma conversación y el cliente recibe dos respuestas — el reclamo número uno de quien arma multiagente a las apuradas.

Repartir con criterio

async function elegirAgente(cola) {
  const disponibles = await db.query(
    `SELECT a.id, COUNT(c.id) AS abiertas
       FROM agentes a
       LEFT JOIN conversaciones c
              ON c.agente_id = a.id AND c.estado = 'abierta'
      WHERE a.online = true AND $1 = ANY(a.colas)
      GROUP BY a.id
     HAVING COUNT(c.id) < a.limite_simultaneo
      ORDER BY abiertas ASC, a.ultima_asignacion ASC NULLS FIRST
      LIMIT 1`,
    [cola],
  );
  return disponibles.rows[0] ?? null;
}

Dos cosas importan más que el algoritmo:

limite_simultaneo — un agente con 15 conversaciones abiertas no atiende bien ninguna. Un techo por persona protege la calidad.

Desempate por ultima_asignacion — sin él, quien cierra rápido recibe todo y quien se demora queda libre.

Transferir sin que el cliente repita

async function transferir(conversacionId, deId, paraId, motivo) {
  await db.conversaciones.actualizar(conversacionId, {
    agente_id: paraId,
    asignada_en: new Date(),
  });

  // Lo que evita la peor frase de la atención: "¿me lo cuentas otra vez?"
  const resumen = await db.mensajes.ultimos(conversacionId, 20);
  await notificarAgente(paraId, { conversacionId, resumen, motivo });

  await enviar(conv.numero,
    'Te paso con quien lleva este tema, ya con todo lo que hablamos acá. 👍');
}

Avisarle al cliente y entregarle el historial al nuevo agente. Si falta cualquiera de las dos, la transferencia se vuelve la experiencia que todos detestan.

Del bot al humano

El diseño más eficiente combina ambos: el bot hace el triaje y resuelve lo repetitivo; la persona entra cuando hace falta.

async function alRecibir(msg) {
  const conv = await obtenerOCrearConversacion(msg.from);

  // ¿Ya está con un humano? El bot no vuelve a intervenir. Punto.
  if (conv.agente_id) return derivarAlAgente(conv, msg);

  const r = await bot.responder(conv.id, msg.text.body);

  if (r.quiereHumano) {
    const agente = await elegirAgente(r.cola ?? 'general');
    if (agente) {
      await asignar(conv.id, agente.id);
      await notificarAgente(agente.id, {
        conversacionId: conv.id,
        resumen: r.resumenDeLaConversacion,   // el bot entrega el contexto
      });
      return enviar(msg.from, 'Ya te comunico con alguien del equipo. 👋');
    }
    await db.conversaciones.actualizar(conv.id, { estado: 'pendiente' });
    return enviar(msg.from, 'Están todos ocupados ahora. Apenas se libere alguien te escribo.');
  }

  await enviar(msg.from, r.texto);
}

La primera línea es la regla más importante: una vez que la conversación pasó a un humano, el bot no vuelve a responder. Un bot que interrumpe una atención humana es peor que no tener bot.

Cómo armar el bot está en chatbot de IA con la API de OpenAI — y el [HUMANO] de ese artículo es exactamente el quiereHumano de acá.

Horario y cola de espera

async function avisarFueraDeHorario(numero) {
  if (dentroDelHorario()) {
    return enviar(numero, '¡Recibimos tu mensaje! Ya te respondo. 😊');
  }
  return enviar(numero,
    '¡Recibimos tu mensaje! Atendemos de lunes a viernes, de 8 a 18 h. ' +
    'Te respondo apenas abramos.');
}

Una expectativa clara vale más que una respuesta rápida. Un cliente que sabe cuándo lo van a atender no manda cinco mensajes más preguntando si hay alguien.

Métricas que dicen algo

NúmeroQué revela
Tiempo hasta la primera respuestaLo que siente el cliente
Tiempo hasta resolverLa eficiencia real
Conversaciones por agenteSi el reparto es justo
Tasa de transferenciaSi el triaje está fallando
Pendientes en el picoSi falta gente

Una tasa de transferencia alta casi siempre significa colas mal diseñadas, no agentes malos.

Conclusión

El multiagente no es una función de WhatsApp: es tu sistema decidiendo quién responde. Las piezas son pocas — una conversación por número, asignación atómica, transferencia con historial y un bot que se retira cuando entra el humano.

La que más retrabajo ahorra es el bloqueo atómico. Es una línea de SQL, y sin ella el cliente recibe dos respuestas distintas de la misma empresa.

¿Listo para automatizar tu WhatsApp?

Crea tu cuenta gratis y empieza a enviar mensajes por la API en minutos.

Empezar gratis

Preguntas frecuentes

¿Cómo pongo varios agentes en el mismo número de WhatsApp?+

Por la API. Los mensajes llegan a un webhook y tu sistema decide quién atiende, repartiendo las conversaciones entre los agentes conectados. Todos responden por el mismo número y, para el cliente, existe una sola conversación — algo que la app común no permite.

¿Cuál es el límite de agentes por número?+

Por la API no aplica el límite de dispositivos de la aplicación, porque quien conversa con WhatsApp es tu sistema y no los aparatos. El límite práctico pasa a ser el volumen de mensajes que soporta el número y la capacidad de tu equipo.

¿Cómo evito que dos agentes respondan la misma conversación?+

Con un bloqueo de asignación en la base de datos. La conversación se asigna a un agente en una operación atómica, y la interfaz solo permite responder a quien la tiene. Sin eso, dos personas abren la misma conversación a la vez y el cliente recibe dos respuestas distintas.

¿Cómo transfiero una conversación entre agentes?+

Cambiando la asignación y preservando el historial. Lo importante es pasar también un resumen de lo ya tratado, para que el cliente no tenga que repetir. Una transferencia que obliga a contar todo de nuevo es el reclamo más común de la atención en cualquier canal.

¿Puede el bot atender primero y pasar al humano?+

Sí, y es el diseño más eficiente. El bot hace el triaje y resuelve lo repetitivo; cuando detecta que hace falta una persona, marca la conversación para atención humana y le entrega el historial al agente. El error es que el bot insista después de que la persona ya pidió un humano.

Sigue leyendo