Handoff a humano
Si tienes un bot externo que conversa con tus clientes vía la API, puedes escalarle una conversación a un asesor humano cuando el cliente lo pida. Un solo POST marca la conversación como "pendiente de asesor" y dispara toda la maquinaria que ya vive en el inbox: badge, push a los asesores, recordatorios y el webhook saliente.
El endpoint
POST /organizations/{orgId}/conversations/{conversationId}/request-handoff- Auth:
X-Api-Keycon scopeconversations:write(o una key sin scopes = acceso total). - Body opcional:
{ "detail": "texto libre" }— queda en el log y se incluye en el webhook saliente (ej."cliente pidió hablar con ventas"). - Idempotente: si la conversación ya estaba en handoff, no se vuelve a disparar (no duplica push ni mensajes).
- Se respeta a cualquier hora: el traspaso diferido de Fuera de horario aplica solo a los pedidos que nacen del bot.
Ejemplo
# Bash / curl
curl -X POST \
"https://api.mosend.dev/organizations/${ORG_ID}/conversations/${CONVERSATION_ID}/request-handoff" \
-H "X-Api-Key: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{ "detail": "el cliente pidió hablar con un asesor de ventas" }'
# Respuesta (200)
# { "data": { "ok": true }, "timestamp": "..." }// Node.js (18+)
await fetch(
`https://api.mosend.dev/organizations/${ORG_ID}/conversations/${conversationId}/request-handoff`,
{
method: 'POST',
headers: { 'X-Api-Key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ detail: 'el cliente pidió un asesor' }),
},
);# Python
import requests
requests.post(
f"https://api.mosend.dev/organizations/{ORG_ID}/conversations/{conversation_id}/request-handoff",
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
json={"detail": "el cliente pidió un asesor"},
timeout=15,
)Qué pasa al dispararlo
Una sola llamada activa, en cadena:
- La conversación queda marcada como pendiente de asesor: aparece la etiqueta/badge en el inbox y entra al filtro "Pendientes de asesor" en tiempo real.
- Se silencia cualquier automatización de Mosend sobre esa conversación (bot interno, auto-respuestas), para que no pise al asesor.
- La conversación queda sin asignar para que cualquier asesor disponible la pueda tomar.
- Se manda push destacado a los asesores, con recordatorios a los 2, 5 y 10 minutos si nadie la reclama.
- Si la org configuró un mensaje de handoff, se le envía al cliente automáticamente.
- Se dispara el webhook saliente
conversation.handoff_requested(si tienes uno suscrito) — útil si quieres además notificar a Slack, Teams, etc.
Flujo recomendado para un bot externo
- Tu bot conversa con el cliente vía
POST /messages. - Cuando detectas (con tu propia lógica/IA) que el cliente quiere un humano — por ejemplo escribe "quiero hablar con una persona" — llamas a
request-handoffsobre esa conversación. - Tu bot deja de responder en esa conversación (tú controlas eso de tu lado).
- Los asesores ven la solicitud en el inbox de Mosend y la atienden.
Fuera de horario
Si el cliente pide asesor cuando el equipo está cerrado, traspasar no sirve: nadie va a tomar la conversación y el bot quedaría mudo hasta la mañana. Por eso, cuando el pedido nace del bot (herramienta del agente IA, palabra clave, regla o flujo) y el horario está cerrado, no hay handoff: el bot crea una tarea para el equipo, le dice al cliente cuándo lo contactan y sigue atendiendo.
- El horario que manda es el del número (
BotConfig.outOfHoursSchedule) si lo tiene; si no, el de la organización (Organization.businessHoursSchedule). Sin ninguno se asume 24/7 y esto no aplica. - Los traspasos manuales (desde el inbox) y por API (
request-handoff) se respetan siempre, abierto o cerrado. - La tarea va a la cola del equipo (
origin: BOT, sin dueño) y vence a la próxima apertura más los minutos de gracia; si el cliente insiste esa noche, se anota en la misma tarea y no se le repite el aviso. Se completa sola cuando una persona responde. - Queda un evento
HANDOFF_DEFERREDen bot-events conreason,taskId,aperturay si se avisó al cliente. El webhookconversation.handoff_requestedno se dispara.
Se configura por número en PUT /organizations/{orgId}/bot/config/{phoneId}:
| Campo | Default | Qué hace |
|---|---|---|
| outOfHoursTaskEnabled | true | Activa el traspaso diferido. En false el handoff fuera de horario ocurre como siempre (bot mudo hasta que alguien lo tome). |
| outOfHoursTaskMessage | null | Texto al cliente. Admite {apertura} ("mañana a las 8:00 a. m."). null = texto por defecto; cadena vacía = no avisar nada. |
| outOfHoursTaskGraceMin | 30 | Minutos después de abrir en los que vence la tarea (ahí sale el recordatorio al equipo). |
curl -X PUT "https://api.mosend.dev/organizations/${ORG_ID}/bot/config/${PHONE_NUMBER_ID}" \
-H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{
"outOfHoursTaskEnabled": true,
"outOfHoursTaskMessage": "Ya dejé tu solicitud al equipo; te escriben {apertura}. Mientras tanto sigo aquí para lo que necesites.",
"outOfHoursTaskGraceMin": 30
}'Seguimiento por silencio
Cuando el bot responde y el cliente se queda callado, el bot puede retomar una sola vez y, si sigue el silencio, cerrar la conversación con una despedida. Apagado por defecto; se configura en el mismo PUT …/bot/config/{phoneId}:
| Campo | Default | Qué hace |
|---|---|---|
| followUpEnabled | false | Activa el seguimiento. |
| followUpAfterMin | 15 | Minutos de silencio tras la última respuesta del bot antes de retomar. |
| followUpCloseAfterMin | 60 | Minutos más de silencio, después del seguimiento, antes de cerrar la conversación. |
| followUpMode | "AI" | AI: la IA redacta la frase con el contexto y puede callar si no quedó nada pendiente. TEXT: envía followUpMessage tal cual. |
| followUpMessage | null | Texto del seguimiento en modo TEXT. |
| followUpCloseMessage | null | Despedida al cerrar. null = despedida por defecto; cadena vacía = cerrar sin enviar nada. |
- Nunca corre si la conversación tiene asesor asignado, un handoff pendiente, un flujo esperando respuesta, un contacto dado de baja (opt-out) o la ventana de 24 horas cerrada.
- Eventos en bot-events:
FOLLOWUP_SENT(se retomó),FOLLOWUP_SKIPPED(la IA decidió que no había nada pendiente,motivo: nada-pendiente) yFOLLOWUP_CLOSED(se cerró por silencio).
curl -X PUT "https://api.mosend.dev/organizations/${ORG_ID}/bot/config/${PHONE_NUMBER_ID}" \
-H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{
"followUpEnabled": true,
"followUpAfterMin": 15,
"followUpCloseAfterMin": 60,
"followUpMode": "TEXT",
"followUpMessage": "¿Sigues ahí? Si quieres, retomamos donde quedamos.",
"followUpCloseMessage": ""
}'Notas
- Necesitas el
conversationIdde Mosend. Lo obtienes deGET /conversations, del webhookmessage.new(campodata.conversationId), o de la respuesta dePOST /messages. - Si tu API key tiene scope por phone-number, solo puedes escalar conversaciones de los números permitidos.
- Para recibir la notificación de handoff en tu sistema (en vez de dispararla), suscríbete al evento
conversation.handoff_requesteden Webhooks salientes.