WhatsApp Bot
The WhatsApp bot is the platform's primary inbound/outbound channel. Guests message your tenant's WA number; the bot persists the message, runs language + mood analysis, and surfaces it to you. Outbound — task notifications, check-in instructions, pre-checkout reminders — flows out through the same number.
Tenant configuration
Each tenant has its own WhatsApp Cloud API setup:
whatsappPhoneNumberId— Meta-assigned phone-number ID; the routing key.whatsappBusinessAccountId— Meta WABA the number belongs to.whatsappVerifyToken— per-tenant verify token for the webhook handshake.whatsappAccessToken(secret) — the access token the platform uses to call the Cloud API.
Configure under Settings → Integrations.
⚠ If whatsappPhoneNumberId is NULL on the tenant row, every inbound webhook is silently rejected. That was the #218 bug. Fix shipped May 2026; admins should verify the value is populated after onboarding.
Inbound webhook
Meta delivers inbound messages to:
POST https://flatsbratislava.com/api/webhooks/wa/t/<tenant-slug>
The URL is tenant-scoped — the <tenant-slug> in the path is the routing key. This is the canonical multi-tenant webhook pattern (see project_webhook_multitenant_pattern in memory). The HMAC signature on the request is verified against the tenant's whatsappAccessToken — see #71.
On a verified inbound:
- Message persisted to
messagestable. - LLM language detection writes to
reservation.guest.languageif higher confidence than current. - LLM mood + comm-type analysis writes to
reservation.guestMood,reservation.guestCommunicationType. - Task seeder re-evaluates any auto-resolve conditions that depend on these fields.
- The message shows up in:
- The reservation's thread tab
- The
/v28/wa-commoperator view - The Pulse "recent messages" card (if the AI couldn't confidently reply)
Outbound messages
Free-text (inside 24h window)
A teammate's reply, an AI suggestion accepted, or a task action that sends a custom message — all use the free-text path when the recipient messaged us in the last 24h.
Template (outside 24h window OR for compliance-sensitive sends)
Pre-approved Meta templates with {{1}} style placeholders. The platform substitutes values at send time. Examples:
checkin_instructions— Send Check-in fallback.task_notification_v2— outbound task notification with up-to-3 buttons.task_resolved— fallback for closed-window operator notifications.
Interactive buttons
Templates can carry up to 3 button replies. When the recipient taps a button, the response comes in as a regular inbound message tagged with the button payload. The platform routes it back to the right task — see handleButtonResponse in whatsappService.ts.
Silent delivery failure (closed window)
A free-text send into a closed window can return HTTP 200 from Meta and silently fail to deliver. The platform now always falls back to a template path when free-text is rejected; the fix landed alongside the cleaning-walkthrough rollout.
If a guest insists they got no message, check:
- Admin → Activity Log — was the send recorded?
- Admin → Webhook Logs — did Meta return 200 + a
messages[]array?
Inbound from teammates
Cleaners and hosts message the same number with status updates and photos. The platform identifies them by phone number → teammate row → routing.
The cleaner-WA walkthrough flow is its own multi-step conversation — see Team & Access → Cleaner Walkthrough.
Related issues
- #218 — WA bot dead (
whatsappPhoneNumberIdNULL bootstrap). - #181 — admin-photo flood + admin-chat visibility on
/v28/wa-comm. - #71 — webhook signature verification (Hospitable, WhatsApp, Beds24).
- #80 — webhook payload trust boundary (tenant-spoofing fallback removed).
- #212 — cleaner WA free-text + photo reports → LLM intent classification.