TL;DR: Suscríbase a calls.post, responda HTTP 200 en menos de dos segundos y gestione su CRM con estados JSON planos (initiated, ringing, answered, missed, canceled). Elimine duplicados con id más timestamp. Obtiene el ciclo de vida observable completo sin WebRTC; la duración de la llamada dependerá de un webhook terminate futuro.
Whapi calls.post entrega un ciclo de vida observable de llamadas mediante webhooks JSON planos, tanto en llamadas 1:1 como grupales, sin WebRTC ni SDP. Su backend en Node.js reacciona a cada timbre y respuesta desde un handler HTTPS normal. El límite es claro: esta superficie de API no tiene evento terminate, así que la duración de la llamada no está disponible hoy.
Los equipos que conectan primero webhooks de mensajes y omiten eventos de llamada pierden visibilidad en el CRM en cuanto la voz llega a WhatsApp Web.
Llamadas grupales de WhatsApp en Web: por qué importan los webhooks ahora
Las llamadas de voz y video grupales en WhatsApp Web se despliegan por canales beta, con reportes de hasta 32 participantes, timbre selectivo y enlaces de llamada compartibles.
WABetaInfo siguió el despliegue de llamadas grupales en Web como función del cliente. Los sistemas backend aún necesitan estado del lado del servidor para llamadas grupales de WhatsApp en Web: quién timbró, quién contestó, quién perdió la llamada, entregado a un endpoint que usted controla. Para eso sirve calls.post: eventos de señalización observables, no flujos de medios.
Un equipo clínico con el que trabajamos conectó eventos missed a un flujo de respuesta por texto en un día, una vez habilitado calls.post, sin stack WebRTC.
calls.post de Whapi en 60 segundos
Habilite calls.post en la URL del webhook de su canal, acepte payloads POST, responda rápido y persista cada fila de llamada antes de ejecutar efectos secundarios.
Webhooks JSON planos, no WebRTC SDP. Cada entrega envuelve uno o más objetos bajo un array calls de nivel superior. Los campos se mapean directamente desde el esquema OpenAPI CallEvent: sin descripción de sesión, sin candidatos ICE, sin negociación de medios en su servidor.
Forma típica del sobre:
{
"event": { "type": "calls", "event": "post" },
"channel_id": "YOUR-CHANNEL-ID",
"calls": [
{
"id": "3EB0C767F26DEECBBE",
"chat_id": "[email protected]",
"status": "ringing",
"from": "[email protected]",
"timestamp": 1721641200,
"group_call": false,
"video_call": true,
"offline_call": false,
"latency": 842
}
]
}
Configure la URL en ajustes del canal, suscríbase a eventos de llamada y pruebe desde un dispositivo vinculado. Consulte la referencia del formato de webhooks entrantes para campos compartidos del sobre. Las respuestas distintas de 200 activan reintentos.
Ciclo de vida de la llamada: initiated → ringing → answered / missed / canceled
Initiated, ringing, answered, missed, canceled: observables sin medios. OpenAPI lista los cinco estados en CallEvent.status. Su handler debe tratar cada POST como una transición de estado, no como una notificación aislada.
Estado: initiated
Se dispara cuando se crea el objeto de llamada, antes de que suene el dispositivo del destinatario. Úselo para reservar una fila en el CRM o incrementar contadores de intento.
{
"calls": [{
"id": "3EB0A1B2C3D4E5F6",
"chat_id": "[email protected]",
"status": "initiated",
"from": "[email protected]",
"timestamp": 1721641180,
"group_call": false,
"video_call": false,
"offline_call": false,
"latency": 120
}]
}
Estado: ringing
Indica timbre activo en el lado del destinatario. Combínelo con initiated para medir latencia de timbre o activar screen-pop para agentes.
{
"calls": [{
"id": "3EB0A1B2C3D4E5F6",
"chat_id": "[email protected]",
"status": "ringing",
"from": "[email protected]",
"timestamp": 1721641184,
"group_call": false,
"video_call": false,
"offline_call": false,
"latency": 310
}]
}
Estado: answered
Ruta terminal de éxito. Detenga temporizadores de reintento, marque la conversación como activa y pase al canal de voz que su producto use fuera de este webhook.
{
"calls": [{
"id": "3EB0A1B2C3D4E5F6",
"chat_id": "[email protected]",
"status": "answered",
"from": "[email protected]",
"timestamp": 1721641192,
"group_call": false,
"video_call": true,
"offline_call": false,
"latency": 905
}]
}
Estado: missed
Sin respuesta antes del timeout. Los webhooks de llamada perdida activan flujos de respuesta por texto en el CRM: mensajes de seguimiento, creación de tickets o inserción en cola de devolución de llamada.
{
"calls": [{
"id": "3EB0A1B2C3D4E5F6",
"chat_id": "[email protected]",
"status": "missed",
"from": "[email protected]",
"timestamp": 1721641240,
"group_call": false,
"video_call": false,
"offline_call": true,
"latency": 2100
}]
}
canceled también aparece en OpenAPI cuando el llamante cuelga antes de la respuesta o se retira la invitación. Ejemplos antiguos del help desk a veces listan solo cuatro estados; confíe en el esquema y trate canceled como rama terminal propia en su máquina de estados.
Webhooks de llamadas grupales: diferencias de payload 1:1 vs grupo
group_call:true cambia la forma del payload a nivel booleano. El mismo enum de estados aplica, pero chat_id apunta a un JID de grupo y la lógica de enrutamiento debe distribuirse a varios agentes.
Ejemplo derivado del esquema OpenAPI (valores ilustrativos hasta que su canal registre una llamada grupal en vivo). Para patrones de mensajería grupal saliente, consulte la visión general de WhatsApp Groups API.
{
"calls": [{
"id": "3EB0GROUPCALL001",
"chat_id": "[email protected]",
"status": "ringing",
"from": "[email protected]",
"timestamp": 1721641300,
"group_call": true,
"video_call": true,
"offline_call": false,
"latency": 640
}]
}
| Campo | Llamada 1:1 (group_call: false) |
Llamada grupal (group_call: true) |
|---|---|---|
chat_id |
JID individual (@s.whatsapp.net) |
JID de grupo (@g.us) |
from |
ID de contacto del llamante | ID de contacto del llamante (mismo campo) |
Enum status |
initiated → ringing → answered / missed / canceled | Mismo enum; el timbre selectivo puede generar varios POST ringing |
video_call |
Voz vs video de la invitación | Igual; video grupal en Web usa la misma bandera |
| Enrutamiento CRM | Mapee chat_id a un responsable |
Mapee membresía del grupo o cola compartida; evite respuestas por texto duplicadas por participante |
Whapi vs Cloud API oficial: mapeo de estados
En la WhatsApp Business API oficial, los tutoriales de llamadas de Cloud API se centran en flujos WebRTC connect, intercambio SDP y payloads terminate con duración. En Whapi.Cloud obtiene webhooks solo de señalización en sockets de sesión web, sin empujar negociación de medios a su servidor.
Whapi mapea ringing y answered; Meta usa nombres de estado distintos en su modelo de webhook VoIP. Meta Terminate expone duración; Whapi no tiene evento terminate. Planifique facturación y lógica de cierre en consecuencia. Para un marco más amplio oficial vs no oficial, consulte por qué la API oficial a menudo no encaja con equipos pequeños.
| Estado de llamada Cloud API de Meta (referencia) | status de Whapi calls.post |
Notas |
|---|---|---|
| Connect / RINGING | ringing |
Ambos señalan timbre activo; solo difiere el nombre |
| ACCEPTED | answered |
El destinatario contestó; los medios permanecen del lado del cliente en Whapi |
| REJECTED | missed o canceled |
Whapi separa rechazo explícito vs timeout en su enum |
| Terminate (incluye duración) | no disponible | Sin webhook de fin de llamada en esta superficie hoy |
| -- | initiated |
Señal temprana extra antes del timbre; útil para preparar el CRM |
Esperar duración al estilo Terminate solo de calls.post es una brecha de cobertura de API, no una mala configuración del webhook.
Construir un handler de eventos de llamada: patrón de máquina de estados
Modele cada call.id como una fila en un registro de estado de llamadas. Avance solo cuando el status entrante supere el rango almacenado y el timestamp sea más reciente.
Responda webhooks rápido con 200; deduplique por id y timestamp. Encole efectos secundarios del CRM de forma asíncrona. El mismo patrón evento-reacción aplica aquí: recibir, persistir, reaccionar. Handlers lentos provocan reintentos y filas duplicadas.
// Returning 500 here retries the webhook and can double-send CRM text-backs
app.post('/webhooks/whapi', express.json(), async (req, res) => {
res.sendStatus(200); // ACK first; process after response
for (const call of req.body.calls ?? []) {
const key = `${call.id}:${call.status}:${call.timestamp}`;
if (await seen(key)) continue;
const prev = await db.getCall(call.id);
const next = rank(call.status); // initiated<ringing<answered|missed|canceled
if (prev && (next < prev.rank || call.timestamp < prev.timestamp)) continue;
await db.upsertCall({ ...call, rank: next });
if (call.status === 'missed') {
await queueTextBack(call.chat_id, call.from);
}
}
});
Pseudocódigo para la guarda de transición:
STATE ranks: initiated=1, ringing=2, answered=3, missed=3, canceled=3
ON calls.post:
ACK 200 immediately
FOR each call in payload:
IF dedupe_key(id, status, timestamp) exists: SKIP
IF stored.rank > new.rank: SKIP
IF stored.timestamp > call.timestamp: SKIP
UPSERT call row
IF status == missed AND policy allows: ENQUEUE text-back job
IF status == answered: CANCEL pending retry timers
Procesar antes del ACK 200 a menudo duplica efectos secundarios de llamadas perdidas en reintento. Whapi usa sockets de sesión web, así que los canales se mantienen lo bastante estables para automatizar eventos de llamada. Consulte el tutorial de bot WhatsApp en Node.js para un starter completo de webhooks.
Lo que calls.post no puede decirle
La duración de la llamada no está disponible en esta superficie de webhook. El soporte de Whapi confirma que no hay evento terminate ni de fin de llamada para calcular segundos en línea.
Meta Terminate expone duración; Whapi no tiene evento terminate. No infiera tiempo de conversación desde timestamps de answered. El aprovisionamiento en Meta App Dashboard y la depuración SDP pertenecen a tutoriales VoIP de Cloud API, no a ajustes de canal Whapi.
Campos del payload: video_call, offline_call, latency
video_call, offline_call, latency explican contexto de entrega, no medios. No reemplazan una API de grabaciones ni metadatos de stream.
video_call marca invitaciones de voz vs video; offline_call indica entrega offline; latency reporta milisegundos de señalización (no calidad RTP).
Combine las tres banderas con status al priorizar devoluciones de llamada de agentes: un video_call missed con latency alta y offline_call: true a menudo significa que el destinatario nunca vio el timbre en Web.
Próximos pasos: iniciación de llamadas en Whapi
Hoy observa llamadas desde clientes WhatsApp vinculados; llamadas salientes programáticas y webhooks de fin de llamada están en la hoja de ruta a corto plazo.
POST /calls y webhooks de fin de llamada en la hoja de ruta. El endpoint create-call ya existe para creación de eventos; espere superficies más ricas de iniciación y cierre conforme maduren las llamadas grupales en Web.
# POST https://gate.whapi.cloud/calls - create call event (start_time required today)
curl -X POST "https://gate.whapi.cloud/calls" \
-H "Authorization: Bearer $WHAPI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"start_time":"1721641200"}'
Whapi extiende activamente las API de llamadas conforme los clientes Web ganan voz y video grupales. Suscríbase a calls.post, construya el registro de estado e incorpore duración cuando lleguen eventos terminate. Siga lanzamientos en el changelog del producto. El ciclo de vida observable completo ya llega como JSON plano para llamadas 1:1 y grupales; la duración espera al próximo tipo de webhook.









