WhatsApp Inbox
WhatsApp conversations, inbound requests, message templates and channel settings.
Modules are installed from your Hub's marketplace. Sign up for a Hub to access this module.
Plans
- ✓ 30 billable messages/month
15-day free trial
- ✓ 200 billable messages/month
15-day free trial
- ✓ 800 billable messages/month
15-day free trial
- ✓ 270 billable messages/month
- ✓ +0.0750 € per extra unit
15-day free trial
Módulo whatsapp_inbox — bandeja de WhatsApp y peticiones
Bandeja compartida de un canal de WhatsApp Business: conversaciones por contacto con sus mensajes, peticiones estructuradas extraídas de la conversación (pedido, reserva, cita, presupuesto…) con su flujo de revisión, plantillas aprobadas por Meta y la configuración del canal.
🔴 Lo que NO funciona, y hay que saberlo antes de venderlo:
- Cumplir una petición NO crea nada en otro módulo — es el propósito central del módulo y está bloqueado: el runtime prohíbe que el handler de un módulo escriba en otro, así que la rama de dispatch devuelve
cross_module_dispatch_unsupportedy lo único que ocurre es el cambio de estado afulfilled.- No se puede ENVIAR un mensaje desde el módulo: no hay command de envío y el manifest no declara
capabilities, así que el runtime tampoco llegaría a Meta. Quien contesta es el pasonotifyde un flujo (hub#821), por el outbox y el proxy del SaaS — donde viven las credenciales. El permisosend_messagese retiró en whatsapp_inbox#29: no gateaba nada.- No hay pantalla de ajustes del canal (whatsapp_inbox#6).
- Las auto-respuestas se configuran y no se envían. Las plantillas guardan su estado en Meta y nadie lo sincroniza.
✅ Lo que SÍ funciona y antes no: los mensajes entran solos (evento core
hub.whatsapp.message_received, #27) y exactamente una vez (índice único parcial sobre(hub_id, wa_message_id), whatsapp_inbox#30); y la conversación se abre y se lee desde la bandeja (whatsapp_inbox#29).
Module id:
whatsapp_inbox. Depende de:customers(referencia blanda, por queries públicas, sin FK). Módulo híbrido: SQL + handler WASM parcial (fulfill_request,parse_inbound_message). ⚠️ Su doc de arquitectura sigue enarchitecture/_frozen/modules/whatsapp_inbox.md— el módulo salió de_frozen/(pm#112, ADR-0283) pero el.mdno se ha movido todavía.
Documentación de usuario — docs/
Viaja dentro del módulo y se versiona con él: el asistente del hub (ADR-0282) la indexa por versión instalada y cita la de TU versión, no la de la última publicada. En inglés (idioma fuente).
| Fichero | Para qué |
|---|---|
docs/overview.md |
Qué hace, qué NO hace y la limitación central |
docs/screens.md |
Inbox / Requests / Templates y los ajustes del canal |
docs/concepts.md |
Cumplir no crea nada, no hay envío, el request_schema lo aporta el CALLER (no autoritativo), tipo desconocido → custom, permisos muy admin |
docs/limits.md |
Tabla de qué funciona y qué no, permisos por acción y diagnóstico |
Permisos: muy cargados hacia admin
| Rol | Puede |
|---|---|
admin |
todo |
manager |
ver conversaciones y peticiones; aprobar/rechazar/cumplir. No puede asignar conversación, ni ver plantillas, ni ver/guardar ajustes, ni ingerir, ni borrar |
employee |
solo leer conversaciones y peticiones |
⚠️ send_message ya no existe (whatsapp_inbox#29): no había command detrás, y un permiso que no
gatea nada contesta «sí» a una auditoría que debería decir que no.
Qué expone hoy
| Tipo | Nombre | Permiso |
|---|---|---|
| query | conversations.list / .get · messages.list |
view_conversation |
| query | requests.list / .get |
view_request |
| query | templates.list · settings.get |
manage_settings (solo admin) |
| command | requests.approve / .reject / .fulfill (WASM) |
change_request |
| command | requests.delete (rechaza si ya está fulfilled) |
delete_request |
| command | conversations.assign · templates.create/update/delete · settings.upsert |
manage_settings |
| command | messages.ingest · requests.ingest (WASM) |
manage_connections |
| emite | message.received, request.created/approved/rejected/fulfilled/deleted, conversation.assigned, template.*, settings.updated |
— |
| escucha | hub.whatsapp.message_received (core) · appointments.booking_request.fulfilled / .failed |
— |
Navegación: erp-whatsapp-inbox-inbox, -requests, -templates.
Layout
module.json # manifest (contrato técnico)
migrations/ # esquema §2.5 + contador atómico de reference_number (ADR-0008)
queries/*.sql # lecturas declarativas (:hub_id inyectado)
commands/*.sql # escrituras declarativas (las `_` son intenciones del WASM)
schemas/*.json # JSON Schemas de input (draft 2020-12)
handler/ # WASM Tier 2 parcial → dist/handler.wasm
ui/ # Web Components (Lit/Ionic/OutfitKit)
docs/ # documentación de usuario + corpus del asistente
Estado y trabajo abierto
Ya no está congelado (pm#112, ADR-0283): es el caso estrella del kernel de automatización.
El «bloqueo» del dispatch cross-módulo de fulfill_request no es un pendiente: está prohibido a
propósito y la prohibición se reforzó (hub#659, ADR-0283 §7). Reaccionar ejecutando el command de
otro módulo es territorio de un flujo con grant explícito —auditable y revocable—, no de un
handler. Ver la tabla de decisiones al principio de WASM-TODO.md, donde tres piezas
de esa lista quedaron descartadas por la misma razón.
Abierto: pantalla de ajustes del canal (whatsapp_inbox#6) y el emit condicional del runtime (hub#1076).
Doc de arquitectura: architecture/_frozen/modules/whatsapp_inbox.md (pendiente de mover).
This module requires the following modules to be installed:
Sign in to leave a review
Share your experience with this module
User Reviews
No reviews yet
Be the first to review this module
chore(release): v2.1.30
chore(release): v2.1.29
chore(release): v2.1.28
chore(release): v2.1.27
chore(release): v2.1.26
chore(release): v2.1.25
chore(release): v2.1.24
chore(release): v2.1.23
chore(release): v2.1.22
chore(release): v2.1.21
chore(release): v2.1.20
chore(release): v2.1.19
chore(release): v2.1.18
chore(release): v2.1.17
fix(sql): cualifica la autorreferencia del contador — `requests.ingest` vuelve a funcionar (#23) `commands/_bump_request_counter.sql` escribía la autorreferencia del upsert SIN cualificar: ON CONFLICT (hub_id, day) DO UPDATE SET last_number = last_number + 1; Dentro de un `ON CONFLICT ... DO UPDATE SET`, un nombre de columna sin cualificar en el lado derecho es AMBIGUO en PostgreSQL entre la tabla destino y la pseudo-tabla `excluded`. No es estilo: la sentencia no parsea. Reproducido contra Postgres 18 real: ERROR: column reference "last_number" is ambiguous LINE 3: ...ONFLICT (hub_id, day) DO UPDATE SET last_number = last_numbe... Falla ya en el PRIMER insert (es error de parseo, no hace falta que haya conflicto), y como `_bump_request_counter` es la primera intención que devuelve el handler WASM `parse_inbound_message` —las dos van en la misma transacción— se llevaba por delante `whatsapp_inbox.requests.ingest` entero: el módulo no podía crear ni una sola request. Semántica elegida: `whatsapp_inbox_request_counter.last_number + 1` (el valor YA persistido), no `excluded.last_number`. `excluded` siempre vale el literal 1 del VALUES, así que congelaría el contador y todas las requests del día compartirían `WA-YYYYMMDD-0001`. Verificado contra Postgres 18: tres ingests seguidos dan 0001/0002/0003 y otro hub el mismo día arranca en 0001 (el índice único es (hub_id, day)); con `excluded` el contador se queda clavado en 1. Los otros dos upserts del módulo (`message_ingest_conv.sql`, `settings_upsert.sql`) ya cualifican con `excluded.` en todas sus asignaciones — el bug NO estaba repetido. `erplora validate` pasa de rojo a verde. Comentarios del fichero traducidos a inglés (regla de boy-scout) y bump de patch 2.1.15 → 2.1.16 en `module.json` y `package.json`: la publicación del marketplace es create-only por versión. Closes #22
chore(release): v2.1.15
fix(manifest): `events.emit` → `events.emits` — el módulo recupera el modo estricto (#21) El manifest declaraba `events.emit` (singular) con 11 eventos. El campo del contrato es `events.emits` (`hub/schemas/module.schema.json`), y como `properties.events` no lleva `additionalProperties:false`, serde lo ignoraba SIN UN SOLO AVISO: el módulo creía declarar sus eventos y el runtime lo trataba como si no declarara ninguno, perdiendo el modo estricto de `validate_handler_event` (`crates/runtime/src/commands.rs`, regla 4). Verificados los 11 nombres uno a uno contra los `emit:` de los commands y contra `handler/src/lib.rs`: la lista es EXACTA (ni sobra ni falta ninguno), así que activar el modo estricto no rompe ningún command. Bump de patch (2.1.13 → 2.1.14) en `module.json` y `package.json`: la publicación del marketplace es create-only por versión y sin bump no republica. Closes #20
chore(release): v2.1.13
feat: drop sqlite dialect (ADR-0154) (#16) Removes migrations/sqlite + manifest sqlite keys; declares migrations.postgres (existían en disco sin referenciar, bug parity 2026-07-05). Postgres is the single dialect. Version bump for republish. Refs ERPlora/pm#30
chore(release): v2.1.11