Agentes del bot (orquestación)
En vez de un solo bot que lo sabe todo, una organización tiene agentes especializados: ventas, soporte, licencias, agenda… cada uno con su prompt, su modelo, sus herramientas y su parte de la memoria. Esta guía explica cómo se crean, quién atiende cada conversación y cómo se pasan la conversación entre ellos sin que el cliente lo note.
Referencia de endpoints en Bot · Agentes especializados. Permisos: bot:read para listar y ver estadísticas, bot:ai-config para crear, editar o borrar agentes, conversations:write para fijar el agente de una conversación.
Endpoints
| Ruta | Qué hace |
|---|---|
| GET /organizations/{orgId}/bot/agents | Lista los agentes. ?phoneNumberId= deja solo los que atienden ese canal. |
| POST /organizations/{orgId}/bot/agents | Crea un agente (name obligatorio; el resto tiene defaults). |
| GET · PATCH · DELETE /organizations/{orgId}/bot/agents/{id} | Detalle, edición parcial y borrado. |
| GET /organizations/{orgId}/bot/agents/capabilities | Catálogo de capacidades activables (label, módulo que requiere). |
| GET /organizations/{orgId}/bot/agents/modelos | Proveedores con sus modelos elegibles y si la organización tiene clave. |
| GET /organizations/{orgId}/bot/agents/stats?dias= | Rendimiento por agente en los últimos N días. |
| POST /organizations/{orgId}/bot/agents/{agentId}/probador | Conversa con el agente sin enviar nada al cliente: devuelve la respuesta y las herramientas que usaría. |
| POST /organizations/{orgId}/bot/agents/{agentId}/configurador | Propone cambios al prompt conversando; no guarda nada. |
| PATCH /organizations/{orgId}/conversations/{id}/agent | Fija a mano qué agente atiende esa conversación (agentId null = que decida el enrutador). |
| POST /organizations/{orgId}/conversations/{id}/devolver-al-bot | Reactiva el bot en una conversación traspasada, conservando el contexto. |
Campos de un agente
| Campo | Para qué sirve |
|---|---|
| name, description | Nombre visible en el panel y en las estadísticas; la descripción es la que lee el router IA para decidir a quién entregar. |
| systemPrompt | Instrucciones del agente (hasta 32 000 caracteres). Los precios y enlaces van mejor en el catálogo de servicios que aquí. |
| provider, model | Proveedor (anthropic · openai · openrouter · groq · meta) y modelo. GET …/bot/agents/modelos lista los elegibles y si hay clave configurada. |
| temperature, maxTokens | Parámetros de generación. Los modelos que los deprecaron (Claude 5.x, gpt-5.x) los ignoran sin fallar. |
| enabledTools | Herramientas que el agente puede invocar (transfer_to_human, set_contact_attribute, transfer_to_agent…). |
| enabledCapabilities | Capacidades de negocio (ventas, agenda, licencias, soporte…). Catálogo en GET …/bot/agents/capabilities; las que el plan no incluye dan 400 al activarlas. |
| routeTags | Etiquetas de conversación o contacto que hacen que este agente atienda. Una etiqueta solo puede pertenecer a un agente. |
| knowledgeDocIds, knowledgeTopK, knowledgeMinSimilarity | Qué documentos de la memoria lee (vacío = toda la biblioteca), cuántos fragmentos por turno y el parecido mínimo. knowledgeTags sigue vigente con menor precedencia. |
| phoneNumberIds | Canales en los que atiende: ids de números reales o virtuales (web chat, Instagram). Vacío = todos los canales de la organización. phoneNumberId (uno solo) es el atajo legado. |
| isDefault | Agente que toma la conversación cuando nada más decide. Solo puede haber uno por canal: si otro ya es el predeterminado de ese número, la API responde 400. |
| enabled, sortOrder | Un agente apagado no recibe conversaciones nuevas ni traspasos. sortOrder ordena la lista y desempata entre etiquetas. |
curl -X POST "https://api.mosend.dev/organizations/${ORG_ID}/bot/agents" \
-H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{
"name": "Soporte",
"description": "Resuelve dudas de uso, garantías y devoluciones de clientes que ya compraron.",
"systemPrompt": "Eres el asesor de soporte de Tienda X. Responde corto y en español neutro…",
"provider": "anthropic",
"model": "claude-sonnet-4-5",
"enabledTools": ["transfer_to_human", "transfer_to_agent"],
"routeTags": ["soporte", "garantia"],
"knowledgeDocIds": ["<docId-politicas>", "<docId-faq>"],
"phoneNumberIds": [],
"isDefault": false,
"enabled": true
}'Canales
phoneNumberIds dice en qué canales atiende el agente. Los ids son los de phone-numbers, incluidos los números virtuales que representan un canal de web chat o una cuenta de Instagram. Con la lista vacía el agente atiende en todos. Un agente que no está en el canal de la conversación no puede recibirla: ni por etiqueta, ni por traspaso, ni por el router.
Quién atiende: precedencia del enrutamiento
En cada turno el bot decide el agente en este orden; el primero que aplica gana:
- Agente activo de la conversación (
activeAgentId): lo fijó un traspaso, el router o un asesor a mano. Se respeta mientras siga habilitado y en ese canal. - Etiquetas: si la conversación o el contacto tiene una etiqueta que algún agente reclama en
routeTags, ese agente empieza a atender. Sin distinguir mayúsculas; gana el primero porisDefault→sortOrder→ fecha de creación. - Router IA: solo en el primer contacto y con dos o más agentes en el canal. Un modelo lee el mensaje y las descripciones de los agentes y elige uno.
- Agente por defecto del canal (
isDefault) si nada de lo anterior decidió.
- Cuando el agente cambia queda un evento
AGENT_ROUTEDen bot-events convia: tag | router | default. - La etiqueta decide solo al empezar o cuando el activo dejó de estar vigente: no deshace un traspaso. Antes sí lo hacía y la conversación rebotaba entre dos agentes en el mismo mensaje.
- Una etiqueta → un agente. Si intentas dar a un agente una
routeTagque otro ya tiene, la API responde 400: «La etiqueta «vip» ya activa al agente «Ventas». Quítasela a uno de los dos.»
Traspasos entre agentes
Con la herramienta transfer_to_agent habilitada, un agente puede entregar la conversación a un hermano (por ejemplo, ventas detecta un reclamo y lo pasa a soporte). El traspaso es invisible para el cliente: el agente destino responde en el mismo turno, sin "te paso con…" ni silencio. Reglas:
- El destino debe ser un agente real, distinto, habilitado y presente en el canal; cualquier otra cosa se ignora y el agente actual sigue respondiendo.
- Máximo un traspaso por mensaje (evita el rebote A → B → A).
- Queda registrado como
AGENT_TRANSFERREDy cuenta en las estadísticas como traspaso saliente de uno y entrante del otro.
Fijar o soltar el agente a mano
Desde el inbox (o tu propio sistema) puedes decidir quién atiende, sin esperar al enrutador:
# Que Soporte atienda esta conversación desde ahora
curl -X PATCH "https://api.mosend.dev/organizations/${ORG_ID}/conversations/${CONVERSATION_ID}/agent" \
-H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{ "agentId": "<agentId-soporte>" }'
# Soltar: que el enrutador vuelva a decidir en el próximo mensaje
curl -X PATCH "https://api.mosend.dev/organizations/${ORG_ID}/conversations/${CONVERSATION_ID}/agent" \
-H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{ "agentId": null }'
# Tras un traspaso a humano, devolver la conversación al bot con su contexto
curl -X POST "https://api.mosend.dev/organizations/${ORG_ID}/conversations/${CONVERSATION_ID}/devolver-al-bot" \
-H "X-Api-Key: ${API_KEY}"Fijar el agente no reactiva un bot silenciado por handoff: para eso está devolver-al-bot. Y al revés, devolver-al-bot conserva el agente activo que había.
Entregar a un agente desde un flujo
El paso transfer_to_ai de los flujos termina el flujo y deja la conversación en manos de la IA. Con agentId eliges cuál; sin él, el que corresponda por la precedencia de arriba.
{
"type": "transfer_to_ai",
"message": "Te dejo con nuestro asesor de ventas.",
"agentId": "<agentId-ventas>"
}Rendimiento por agente
GET /organizations/{orgId}/bot/agents/stats?dias=30 atribuye a cada agente lo que hizo en el período:
{
"data": {
"desde": "2026-08-08T…", "dias": 30,
"agentes": [
{
"agentId": "…", "name": "Ventas", "enabled": true, "isDefault": true,
"channels": [ { "phoneNumberId": "…", "displayPhoneNumber": "+57 3xx…" } ],
"conversaciones": 212, "respuestas": 1480,
"traspasosSalientes": 31, "traspasosEntrantes": 4, "escalamientosAHumano": 18,
"ventas": 27, "citas": 0, "fallos": 2, "fallosDeModelo": 3,
"tokensInput": 1900000, "tokensOutput": 210000, "costUsd": 9.4, "chargedUsd": 14.1,
"latenciaPromedioMs": 2900,
"porCanal": [ { "phoneNumberId": "…", "label": "+57 3xx…", "respuestas": 1480 } ],
"ultimosFallos": [ { "at": "…", "conversationId": "…", "error": "…" } ]
}
],
"sinAtribuir": { "agentId": null, "name": "Sin atribuir (router, flujos o anterior a la medición)", "respuestas": 40, "…": "…" }
}
}sinAtribuiragrupa lo que no tiene agente: el router IA, los flujos y las filas anteriores a que existiera la atribución. Esnullsi no hay nada huérfano.costUsdes el costo real del proveedor;chargedUsdlo que se descontó del saldo (con margen).
Memoria por agente
La biblioteca de documentos es una por organización; cada agente lee la parte que le asignes con knowledgeDocIds. Cómo cargar fuentes, probar la búsqueda y cerrar vacíos está en Memoria del bot.
Flujo típico
- Crea el agente general y márcalo
isDefaulten el canal principal. - Crea los especializados con una
descriptionclara (es lo que lee el router) y susrouteTags:soporte,reseller,vip… - Reparte la memoria: cada agente con sus
knowledgeDocIds. - Habilita
transfer_to_agenten los que deban derivar y prueba los cruces conPOST …/{agentId}/probador. - Etiqueta contactos desde tu CRM (tags) para que el enrutamiento por etiqueta haga el trabajo sin pasar por el router.
- Mira
stats?dias=7cada semana: muchosescalamientosAHumanoofallosen un agente señalan un prompt o una capacidad que falta.
Notas
- El agente resuelve solo en los modos con IA del número (
AI_AGENToRULES_PLUS_AI_FALLBACKen bot-config). - Fuera del horario de atención, un pedido de asesor que nace del bot no traspasa: crea una tarea para el equipo y el agente sigue atendiendo. Ver Handoff a humano y Tareas.