-
Notifications
You must be signed in to change notification settings - Fork 15
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: Live. Twilio end-to-end (inbound β ticket, in-window replies, AI summary/suggest) and the Meta WhatsApp Cloud API both work behind the same interface. Production firewall delivery is via Cloudflare Tunnel (direct mode). Templates (replying after the 24h window) and inbound media (images/files attached to the ticket) are supported, and an open channel ticket auto-refreshes every 15s. Still to come: an optional hosted relay.
A messaging channel is the channel equivalent of an email mailbox β one WhatsApp number wired to a provider. When a customer messages it:
- The provider (Twilio or Meta) pushes the message to this install's webhook:
api/messaging/webhook.php?channel=<id>. - FreeITSM verifies the request, finds an open conversation for that sender (or opens a new ticket), and stores the message.
- The ticket is tagged with the WhatsApp origin and shows in the inbox like any other ticket.
- 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.
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. Pick the option that matches your deployment:
| Your setup | How to expose the webhook |
|---|---|
| Hosted on a public server with a domain | Nothing extra β point the provider at https://yourdomain/api/messaging/webhook.php?channel=<id>. |
| Self-hosted behind a firewall/NAT (production) | Cloudflare Tunnel β a free, persistent public hostname, outbound-only (firewall-friendly). See below. |
| Local laptop, just testing |
ngrok β quickest, but the free URL changes on every restart, so it's dev-only: ngrok http 80. |
Whichever you use, set it once in the Public base URL field at the top of Settings β Messaging and every channel's webhook URL below becomes copy-paste-ready (no hand-editing localhost). Leave it blank to fall back to whatever address you're browsing from.
Unlike ngrok's rotating free URL, a Cloudflare Tunnel gives you a stable hostname and runs as a background service, so it survives reboots β which is what production needs. It connects outbound from your box to Cloudflare, so no inbound firewall holes or port-forwarding are required.
- Create a free Cloudflare account and add a domain (or use a free
*.trycloudflare.comquick tunnel for trials). - Install
cloudflaredon the server running FreeITSM. - Authenticate and create a named tunnel:
cloudflared tunnel login cloudflared tunnel create freeitsm cloudflared tunnel route dns freeitsm itsm.yourdomain.com - Point the tunnel at your local web server (port 80) in
~/.cloudflared/config.yml, then run it as a service:cloudflared tunnel run freeitsm # or: cloudflared service install - In FreeITSM, set Public base URL to
https://itsm.yourdomain.com, copy each channel's webhook URL, and paste it into your provider.
Because the hostname is stable, you set it once and never touch it again β the key advantage over ngrok.
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.
Tip β Test button. Each channel row has a Test button that runs three checks and shows a β /β report (no real message is sent): Credentials (read-only provider call), Webhook reachability (FreeITSM calls the channel's own public webhook URL and confirms it round-trips β catches a down tunnel or wrong Public base URL), and Inbound handling (a synthetic message is run through ingest to confirm it becomes a ticket, then removed). Run it after entering credentials and after setting the webhook to confirm the whole path works.
- 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).
Each channel has an ingress mode:
-
Direct (recommended) β the provider reaches this install's webhook URL directly, authenticated by the provider's own request signature (Twilio's
X-Twilio-Signature, Meta'sX-Hub-Signature-256). Combined with Cloudflare Tunnel (above), this is the recommended setup even for firewalled self-hosters: the tunnel gives you a public URL without exposing a port, and message content flows only between you and the provider. -
Relay β a small hosted service forwards messages on to installs that can't be reached at all. Authenticated by a shared secret (
X-FreeITSM-Relay-Secret). The webhook endpoint already accepts relayed requests, but the relay service itself is not built yet β it's a future option for the rare case a Cloudflare Tunnel won't do. Trade-off worth knowing: a relay means message content transits a third-party service, so direct + tunnel is preferred wherever possible.
Channel routing mirrors email routing exactly, but keyed on the sender's phone number (numbers have no domain):
- Single-company install β the Default company.
- Pinned channel (assigned to a company) β that company; sender ignored.
- 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.
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 swaps the free-text box for a template picker (see below) so the analyst can still re-open the conversation with an approved template.
A template is a message format pre-approved by Meta/Twilio. FreeITSM can't approve templates β you create and approve each one at your provider, then add its definition here so analysts can pick and fill it.
Manage them under Tickets β Settings β Messaging β Message templates:
| Field | Notes |
|---|---|
| Name | A label for analysts, e.g. "Ticket update nudge". |
| Provider | Twilio or Meta β must match the channel it'll be used on. |
| Reference | Twilio: the Content SID (HXβ¦) of the approved template. Meta: the approved template name. |
| Language | Used by Meta (e.g. en, en_US). Ignored by Twilio. |
| Body | The approved text, with {{1}}, {{2}} β¦ where the analyst fills variables. Must match what the provider approved. |
When a ticket's 24h window has closed, the reading-pane composer shows a template picker: the analyst chooses a template (only those matching the channel's provider are offered), fills the {{n}} values (with a live preview), and sends. The filled message is stored in the thread. The customer's reply re-opens the free-text window.
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.
- Inbound media (images, documents, voice notes, etc.) is downloaded from the provider and stored as a normal ticket attachment β it shows in the reading-pane attachment bar and downloads like any email attachment. Any caption becomes the message body. (Twilio fetches the media URL with account auth; Meta does the media-id β URL β bytes two-step.) If a download fails, the message still arrives with a clear marker.
- Live refresh: while you're viewing a channel ticket, the conversation polls for new inbound messages every 15 seconds, so replies appear without reloading. It won't disturb a reply you're typing.
- In Twilio, open the WhatsApp sandbox and note your Account SID, Auth Token, and sandbox number.
- In FreeITSM: Tickets β Settings β Messaging β Add. Provider = Twilio, enter the number + SID + token, save.
- Copy the channel's webhook URL from the list. If testing locally, start
ngrok http 80and swap the host for your ngrok host. - Paste that URL into the Twilio sandbox's "When a message comes in" field.
- Join the sandbox from your phone (send the join code), then message it β a ticket appears in the inbox.
- Open the ticket and reply from the composer; the reply lands on your phone.
Twilio's sandbox is ideal for trying the channel out, but for production direct from Meta (no middleman, cheapest per message) use the Cloud API. It needs a Meta developer app and business verification, so it's more setup than Twilio β do it once you're ready to go live.
- At developers.facebook.com, create an app of type Business, then add the WhatsApp product to it.
- In WhatsApp β API Setup, note the Phone number ID and a temporary access token. For production, generate a permanent token via a System User in Business Settings (a temporary token expires in 24h).
- Find your App Secret under App settings β Basic.
- In FreeITSM: Settings β Messaging β Add, Provider = Meta. Enter the Phone number ID, Access token, App secret, and a Verify token of your choosing (any random string). Set the channel's WhatsApp number to your business number. Save.
- Copy the channel's webhook URL (set your Public base URL first β a real domain or Cloudflare Tunnel host).
- Back in the Meta app, under WhatsApp β Configuration β Webhook, set the Callback URL to that webhook URL and the Verify token to the same string you entered in FreeITSM, then click Verify and save. Meta calls the URL with a
GEThandshake; FreeITSM echoes the challenge automatically. - Still under Webhook, subscribe to the
messagesfield. Inbound messages will now arrive.
The Graph API version defaults to a recent one (currently
v21.0) and Meta retires a version ~2 years after release. When that day comes you can move to a newer version without a code change: edit the Meta channel and set the optional Graph API version field (under the Meta credentials) to e.g.v22.0. Leave it blank to use the built-in default.
-
includes/messaging/βMessagingProvider(interface),TwilioProvider,MetaCloudProvider,messaging.php(factory + helpers),ingest.php(message β ticket). -
api/messaging/βwebhook.php(inbound),send_message.php(analyst reply),send_template.php,ai_summary.php,ai_suggest_reply.php,test_channel.php,get_channels.php/save_channel.php/delete_channel.php,get_templates.php/save_template.php/delete_template.php,save_base_url.php. - Tables:
messaging_channels,tenant_channel_senders,messaging_templates; plusemails.channel/emails.channel_idandtickets.last_inbound_at.
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
- MobileβFriendly
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)