Volver al blog
Desarrolladores

Indicador «escribiendo…» y marcar como leído en la API de WhatsApp (Waspy)

POST /conversations/:id/typing con status on muestra «escribiendo…». Meta lo apaga a los ~25 s. off no llama a Meta. Marcar leído es otro POST y no se cobra como mensaje.

· 11 min
Indicador «escribiendo…» y marcar como leído en la API de WhatsApp (Waspy)

Respuesta corta: el “escribiendo…” es POST /api/v2/conversations/:id/typing con { "status": "on" }. Marcar leído es otro pedido: POST /api/v2/conversations/:id/read, o POST /api/v2/messages/:waMessageId/read si tenés el id de WhatsApp del webhook. Los dos piden el scope conversations:write. Ninguno le manda un texto al cliente y ninguno se cobra como mensaje.

La referencia está en conversations y en messages. El listado de chats y el PATCH de estado están en conversaciones por API. Cómo conseguir el conversationId desde el CRM está en abrir el chat sin UUID.

Cuándo usarlo

Tiene sentido mientras una persona, o tu sistema, busca el pedido y la respuesta real tarda unos segundos. El cliente ve “escribiendo…” en vez de un “ya te respondo” que después hay que limpiar.

No lo uses como keepalive. Meta apaga el indicador a los ~25 segundos o cuando sale un mensaje. Si la consulta tarda más, podés mandar on otra vez. Un loop cada dos segundos no se lee mejor y le pega a la Cloud API al pedo.

Tampoco reemplaza el mensaje. Si la ventana de 24 horas está cerrada, el texto libre no sale (POST /api/v2/messages responde 403 SERVICE_WINDOW_EXPIRED). El typing no abre esa ventana. El camino sigue siendo una plantilla, explicada en la API para desarrolladores.

Si el último saliente lo escribió una persona del equipo (origin panel), tu integración no debería mostrar que “está escribiendo” ni contestar encima. El criterio está en handoff.

Escribiendo…

curl -sS -X POST "https://api.waspytech.com/api/v2/conversations/$CONV_ID/typing" \
  -H "Authorization: Bearer $WASPY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "on" }'

200: conversationId, status, deliveredToMeta y, si aplica, reason.

Waspy toma el último mensaje entrante con waMessageId y llama a la Cloud API con status: "read" y typing_indicator: { "type": "text" } sobre ese id. Por eso el cliente puede ver el doble tilde azul y el “escribiendo…” en el mismo paso. El unreadCount de la bandeja de Waspy no baja con este POST. Para bajarlo, marcá leído.

status: "off" se acepta y no llama a Meta. Si ya había un entrante para anclar y el canal existe, la respuesta trae deliveredToMeta: true y reason: "typing_off_noop". La página de docs dice que ese caso va con deliveredToMeta: false. El servidor manda true. Meta igual no ofrece un “dejar de escribir”: se apaga solo.

El off solo llega a ese no-op si antes encontró canal y entrante. Si no hay entrante, aunque pidas off, el reason es no_inbound_to_anchor.

reason (typing)deliveredToMetaQué pasó
(vacío)trueMeta aceptó el on
typing_off_nooptruePediste off. No se llamó a Meta
no_phone_numberfalseLa conversación no tiene canal de WhatsApp
no_inbound_to_anchorfalseNo hay un entrante reciente con waMessageId. Meta exige uno
phone_not_foundfalseEl canal no existe en esta cuenta
meta_rejectedfalseMeta rechazó el llamado (ventana, permiso, id inválido)
meta_unreachablefalseNo se pudo llegar a Graph (red o token)
Si el id de conversación no existe, 404. El mensaje es Conversation not found y el código es NOT_FOUND.

Marcar leído

curl -sS -X POST "https://api.waspytech.com/api/v2/conversations/$CONV_ID/read" \
  -H "Authorization: Bearer $WASPY_API_KEY"

Waspy pone unreadCount en 0 antes de hablar con Meta. Después manda status: "read" sobre el último entrante con waMessageId. Si Meta no lo acepta, el no leído local igual quedó en cero y deliveredToMeta viene false con un reason.

reason (read)Qué pasó
no_inbound_to_anchorNo hay entrante con waMessageId, o la conversación no tiene phoneNumberId. En el read, esos dos casos comparten este reason
phone_not_foundEl canal guardado no está en la cuenta
meta_rejectedMeta rechazó el recibo
meta_unreachableGraph no respondió, o el token no se pudo usar
No existe typing_off_noop ni no_phone_number en este recurso.

Desde el webhook, si ya tenés el waMessageId y todavía no el conversationId:

curl -sS -X POST "https://api.waspytech.com/api/v2/messages/$WA_MESSAGE_ID/read" \
  -H "Authorization: Bearer $WASPY_API_KEY"

Ese POST busca un mensaje entrante de tu cuenta con ese waMessageId. Si no está, o si es saliente, 404 NOT_FOUND (Inbound message not found). Si está, marca leída la conversación: el recibo que sale a Meta es el del último entrante, que puede ser más nuevo que el id que pasaste. markedMessageId en la respuesta te dice cuál se usó.

Los dos read comparten scope conversations:write, documentado en scopes. No aceptan Idempotency-Key: llamarlos dos veces puede pegarle a Meta dos veces. El header de idempotencia es de POST /api/v2/messages.

Una secuencia que no spamea texto

  1. Llega message.received. Guardás el waMessageId.
  2. Si tu sistema va a tardar (consulta de stock, de Andreani, del ERP), un solo typing con on.
  3. Cuando la respuesta está lista, POST /messages con el texto. Meta apaga el indicador al enviar.
  4. POST /read para que el contador de la bandeja quede en cero, si el typing no te alcanza porque no toca el unreadCount.

No mandes “un segundo”, “ya te digo” y “sigo acá” como tres textos. Cada uno, dentro de la ventana, es un mensaje de servicio: 1.000 por número y por mes desde el 1 de octubre de 2026 y, desde el 1.001, alrededor de ARS 37,68 (US$0,026) en Argentina. El typing y el leído no entran en esa cuenta. La rate card está en la documentación de precios de Meta.

Un asistente externo puede leer el chat por la API o por MCP. El “escribiendo…” y el mensaje al cliente los dispara tu sistema, con esta secuencia, o los confirma una persona. Waspy no contesta la tienda solo. La conexión está en Claude, ChatGPT y Cursor.

La clave está en Scale y Enterprise. El detalle está en Planes y en la API. Se puede probar 10 días sin tarjeta en el registro.

Tu equipo atendiendo por WhatsApp en minutos

CRM facil pero potente. En 10 minutos tenes a tu equipo trabajando — no necesitas hablar con nadie.