Skip to content

WhatsApp

Ed Mozley edited this page Jun 27, 2026 · 8 revisions

WhatsApp channel

Let customers chat with an analyst over WhatsApp β€” every message becomes a normal FreeITSM ticket. It sits alongside email as another inbound channel and reuses the whole ticket pipeline: the reading-pane thread, multi-tenancy routing/isolation, origins and the requester record.

Status: Phase 1 β€” Twilio end-to-end (inbound β†’ ticket, in-window replies, AI summary/suggest), plus a Meta Cloud provider behind the same interface. Templates (for replying outside the 24h window), media download, and the hosted relay are later phases.

How it works

A messaging channel is the channel equivalent of an email mailbox β€” one WhatsApp number wired to a provider. When a customer messages it:

  1. The provider (Twilio or Meta) pushes the message to this install's webhook: api/messaging/webhook.php?channel=<id>.
  2. FreeITSM verifies the request, finds an open conversation for that sender (or opens a new ticket), and stores the message.
  3. The ticket is tagged with the WhatsApp origin and shows in the inbox like any other ticket.
  4. The analyst replies from an inline composer in the reading pane; the reply goes back out over WhatsApp.

Inbound and outbound messages live in the same emails table as email (with a channel column), so the reading-pane conversation thread just works.

Why a webhook (and what that means for self-hosting)

Email works by FreeITSM polling a mailbox. WhatsApp is the opposite: the provider pushes inbound messages to a public HTTPS URL β€” there is no polling option. So the install must be reachable from the internet on that URL.

  • Hosted / public server: point the provider at https://yourdomain/api/messaging/webhook.php?channel=<id>. Done.
  • Local / behind a firewall (dev or self-host): run a tunnel and use the public URL it gives you:
    ngrok http 80
    
    Then the webhook is e.g. https://abc123.ngrok-free.app/freeitsm-app/api/messaging/webhook.php?channel=<id>.

Tip β€” set the Public base URL once. At the top of Settings β†’ Messaging there's a Public base URL field. Put your public domain there (or your ngrok address while testing) and every channel's webhook URL shown below becomes copy-paste-ready β€” no hand-editing localhost. Leave it blank to fall back to whatever address you're browsing from.

Providers

Choose per channel under Tickets β†’ Settings β†’ Messaging:

Provider Best for Notes
Twilio Getting started / testing Has a WhatsApp sandbox β€” you can send/receive end-to-end with no Meta business verification. Recommended first build.
Meta WhatsApp Cloud API Production, direct Cheapest long-term (no middleman), but needs Meta business verification before it works.

Both implement the same MessagingProvider interface (includes/messaging/), so switching is just a channel setting.

Credentials

  • Twilio: Account SID + Auth Token. The channel's WhatsApp number is the "From".
  • Meta: Phone number ID + Access token + App secret, plus a Verify token you choose (used for Meta's webhook subscription handshake).

Credentials are encrypted at rest (AES-256-GCM, same as mailbox secrets).

Inbound delivery: direct vs relay

Each channel has an ingress mode:

  • Direct β€” the provider hits this install's webhook URL directly. Authenticated by the provider's own request signature (Twilio's X-Twilio-Signature, Meta's X-Hub-Signature-256).
  • Relay β€” a hosted relay forwards the verbatim request to this install. Authenticated by a shared secret (X-FreeITSM-Relay-Secret). For self-hosters who can't expose a public endpoint. (The relay component itself is a later phase; the endpoint already accepts relayed requests.)

Routing to a company (multi-tenancy)

Channel routing mirrors email routing exactly, but keyed on the sender's phone number (numbers have no domain):

  1. Single-company install β†’ the Default company.
  2. Pinned channel (assigned to a company) β†’ that company; sender ignored.
  3. Shared channel β†’ the exact sender number is mapped to a company β†’ that company; otherwise triage (surfaces under Default until filed).

On a single-company install none of this is visible β€” it behaves exactly as today.

The 24-hour service window

WhatsApp only allows free-text replies within 24 hours of the customer's last message. Outside that window, providers block free text and require a pre-approved template message.

FreeITSM tracks this per conversation (tickets.last_inbound_at). Inside the window, the composer works normally. Once it closes, the composer is disabled with a notice β€” template support is a later phase.

AI helpers

From the composer on a WhatsApp ticket:

  • Suggest β€” drafts a concise reply for you to review/edit before sending (nothing is sent automatically).
  • Summarise β€” summarises the conversation and saves it as an internal note on the ticket.

Both reuse the Tickets β†’ Settings β†’ Reply Cleanup AI key/provider, so usage shows on that billing line.

Setting up a channel (Twilio sandbox, quickest)

  1. In Twilio, open the WhatsApp sandbox and note your Account SID, Auth Token, and sandbox number.
  2. In FreeITSM: Tickets β†’ Settings β†’ Messaging β†’ Add. Provider = Twilio, enter the number + SID + token, save.
  3. Copy the channel's webhook URL from the list. If testing locally, start ngrok http 80 and swap the host for your ngrok host.
  4. Paste that URL into the Twilio sandbox's "When a message comes in" field.
  5. Join the sandbox from your phone (send the join code), then message it β€” a ticket appears in the inbox.
  6. Open the ticket and reply from the composer; the reply lands on your phone.

Files

  • includes/messaging/ β€” MessagingProvider (interface), TwilioProvider, MetaCloudProvider, messaging.php (factory + helpers), ingest.php (message β†’ ticket).
  • api/messaging/ β€” webhook.php (inbound), send_message.php (analyst reply), ai_summary.php, ai_suggest_reply.php, get_channels.php / save_channel.php / delete_channel.php.
  • Tables: messaging_channels, tenant_channel_senders; plus emails.channel / emails.channel_id and tickets.last_inbound_at.

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally