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.
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 tú 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úmero | Qué revela |
|---|---|
| Tiempo hasta la primera respuesta | Lo que siente el cliente |
| Tiempo hasta resolver | La eficiencia real |
| Conversaciones por agente | Si el reparto es justo |
| Tasa de transferencia | Si el triaje está fallando |
| Pendientes en el pico | Si 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 gratisPreguntas 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
API de Instagram y Messenger en la misma API de WhatsApp: una instancia, un estándar
Tres canales oficiales de Meta casi siempre significan tres integraciones. Con una sola instancia, el mismo envío y el mismo sobre de webhook, solo cambia un campo: provider. Cómo funciona y qué te ahorra.
API no oficial de WhatsApp: qué es, si es segura y cómo usarla (2026)
Entiende qué es una API no oficial de WhatsApp, cómo funciona la conexión por código QR, si es segura y legal, cuál es el riesgo real de bloqueo y cuándo conviene usarla en lugar de la API oficial.
API oficial vs no oficial de WhatsApp: ¿cuál elegir? (comparativa 2026)
Comparativa completa entre la API oficial de WhatsApp (Cloud API de Meta) y la API no oficial: costo, aprobación, límites de envío, plantillas, soporte y riesgo de bloqueo. Descubre cuál tiene sentido para tu caso.