verifyChallenge()
y eventos POST con ingest() (ver el ejemplo de servidor
en el resumen de canales).
Instalación
Uso
Configuración en Meta: crea una app con el producto WhatsApp en developers.facebook.com, genera un token de usuario del sistema permanente conwhatsapp_business_messaging, copia el phone number id del número
emisor y, en WhatsApp → Configuración, pon la URL del webhook y un verify token
que elijas, y suscríbelo al campo messages (trae tanto los mensajes como
los estados de entrega).
- Botones de respuesta nativos (máximo 3: con más rechaza con
UnsupportedContentError, envía una lista), listas, plantillas y formularios con WhatsApp Flows. Un botón conurlsale como botón de enlacecta_url(uno por mensaje: varios enlaces, o un enlace mezclado con botones de respuesta, rechazan). Unacardconserva su imagen (como media o como cabecera del mensaje interactivo) y sus botones. Los límites de Graph se comprueban antes de enviar: sin botones, ids o títulos de botón repetidos (comparados tras el corte a 20 caracteres), una lista sin filas, con más de 10 filas en total o con ids de fila repetidos rechazan conUnsupportedContentError. Las etiquetas se cortan por grafemas, así que un emoji se conserva entero o se quita, nunca se parte. - Si falta
accessToken,phoneNumberIdoverifyToken, el constructor falla con un error que nombra el campo (phoneNumberIdtambién puede ser un entero seguro). Cada petición a Graph tiene un límite de tiempo (requestTimeoutMs, 60 s por defecto); las subidas de media tienen el suyo (uploadTimeoutMs, 5 min por defecto). - Una respuesta interactiva que no es la pulsación de un botón o una lista
(por ejemplo
call_permission_reply) llega como mensajeunknown, con el payload crudo enctx.ref.native. - Versión de la Graph API:
apiVersiontoma por defectoDEFAULT_GRAPH_API_VERSIONde@botmesh/graph-core(v26.0, publicada el 29-07-2026; Messenger e Instagram usan el mismo valor). Meta retira cada versión unos dos años después de publicarla, así que el valor por defecto sigue a la actual y cambia en una versión menor, tras revisar los payloads contra el changelog de Meta. FijaapiVersion(por ejemplo'v24.0') para quedarte en una versión hasta migrar. Un estadoplayed(se escuchó una nota de voz) llega comoreadconplatformData.played: true. - Los usuarios con username de WhatsApp pueden llegar con un BSUID en lugar
del teléfono; el provider mantiene un id estable por persona
(
identityMap). - Fuera de la ventana de 24 h los envíos libres rechazan con
OutsideSessionWindowError. El gestor opcionalsessionWindowreabre el chat con una plantilla y reintenta: ver Ventana de 24 h y costos. - La API no tiene grupos, indicador de escritura ni encuestas, ni borrado para
todos: el provider no tiene
typing()nideleteMessage(), así quectx.typing()yctx.delete()no hacen nada (ver el contrato común de los canales). - El core divide en varios mensajes el texto de más de 4096 caracteres.
- Con el bot parado,
ingest()responde503, así que Meta reenvía el webhook tras el reinicio. publishForm()fija en el Flow la etiqueta de envío por defecto de un formulario en ellangdel provider (por defecto'en'; ponle elBotConfig.langde tu bot), o enpublishForm(def, { lang })para un Flow. ElsubmitLabeldel propio formulario siempre gana.
Siguientes pasos
- Canales: elegir un canal, la matriz de capacidades y el contrato común de los canales.
- Ventana de 24 h y costos: reabre un chat con una plantilla y sigue el uso.
- Formularios: WhatsApp Flows nativos.
- Servidor de webhooks: monta el provider con
@botmesh/server. - README del paquete