-
Notifications
You must be signed in to change notification settings - Fork 15
Slack
Turn a message in a Slack channel into a ticket, and answer it from the FreeITSM inbox without either party leaving the thread.
Set up under System β Integrations β Slack.
Slack sits under Integrations because that is where people look for "connect FreeITSM to X". Underneath it is not a third issue tracker. Jira and Azure DevOps share an engine because they are the same shape of thing: raise a work item, watch its state, pull comments back. Slack has no work items and no states.
It is a messaging channel β the same engine as WhatsApp and Web chat β so its connections live in messaging_channels, not integration_connections, and it reuses the existing webhook endpoint, ingest and reply path.
That split is why the provider registry carries a kind. Anything asking "can we raise a work item in it?" must use integrationsTrackerProviders(), or Slack gets offered as an escalation target and fails at the moment somebody tries.
There is no FreeITSM-hosted Slack app, and there will not be one. You create an app in your own workspace from a manifest FreeITSM generates, and it talks straight to your server.
This is a deliberate rejection of the publish-one-app model that a SaaS product would use. Publishing one would mean this project ran a server that every customer's Slack traffic passed through β permanently, on the project's liability β and became a data processor for other people's companies. For a free, self-hosted product that is the wrong trade.
The cost is about five minutes of setup per install. What you get back:
- your ticket contents never touch anyone else's infrastructure
- no central service that can be down, rate-limited or discontinued
- nothing to trust beyond Slack and your own server
The catch, stated plainly: for Slack to send anything, it must reach your install over the internet on https://. An install that is only reachable inside your own network can post out to Slack (that already works through webhooks) but cannot receive. messaging_channels.ingress_mode has a relay value reserved for solving that later; it is not built.
The in-app guide at System β Integrations β Slack β How to set up Slack is the step-by-step version, written for someone who has never opened Slack. The shape of it:
- Add the workspace in FreeITSM first. The manifest embeds the row's own webhook address, so the row has to exist before the app can be created.
- Create the Slack app from the generated manifest (
api.slack.com/appsβ From a manifest). - Copy the signing secret and bot token back.
-
/invitethe app to the channel it should watch.
Reinstall after creating from a manifest. Slack grants an app its permissions at install time. An app created from a manifest holds a token missing most of its scopes until you click the yellow Reinstall to Workspace banner. Nothing appears broken β tickets still arrive, they just say "Slack user @U0A1B2C3" instead of a name, because reading profiles was one of the permissions that never made it. A requester's name now corrects itself on their next message once the lookup starts working.
An app cannot read a channel it is not in. No error, no warning β messages simply never arrive. /invite @YourApp in the channel.
The Slack admin approving the app sees this list, and an unexplained scope is a reason to refuse.
| Scope | Why |
|---|---|
channels:history |
Read messages in public channels the app is invited to β this is how a message becomes a ticket |
groups:history |
The same, for private channels |
im:history |
The same, for direct messages to the app |
chat:write |
Post the analyst's reply back into the thread |
users:read |
Turn a Slack user id into a person's name on the ticket |
users:read.email |
Match that person to an existing FreeITSM user |
files:read |
Download a shared screenshot and attach it |
Deliberately absent: channels:manage, users:write, and anything that writes files. Those belong to features that do not exist.
A thread is a ticket. This is the one place Slack deliberately differs from WhatsApp. On WhatsApp a person has one running conversation with the service desk, so the sender identifies it. In Slack the same person can have five unrelated threads open at once β so the thread is the conversation. A new message in the channel starts a new ticket; a reply in a thread lands on the existing one.
Only watch one channel. Leave the setting blank and every channel the app is invited to raises tickets, which on a busy channel means a ticket per message. Name a dedicated #it-help channel instead.
Identity does not depend on an email match. Halo's Slack integration requires a person's Slack email to equal their Halo email, which fails for guests, contractors and anyone whose Slack is a personal address. FreeITSM looks the email up and uses it when it matches an existing user; when it does not, it falls back to a named requester β "Sam Okafor (Slack)", or "Slack user @U0A1B2C3" if even the name is unavailable. Never a blank requester, never a silent failure.
No 24-hour window. That is a WhatsApp Business rule. You can reply to a Slack thread from last year.
Multi-company works as it does for any channel: pin the workspace to one company, or leave it shared and let the sender decide. See Multi-Tenancy.
Colour key: π provider Β· π₯ inbound Β· π€ outbound Β· π₯οΈ UI Β· ποΈ schema Β· π docs
| π¨ | File | What it does |
|---|---|---|
| π | includes/messaging/SlackProvider.php |
The whole Slack side: signature check, event parsing, posting, profile lookup, file download |
| π | includes/messaging/slack_manifest.php |
Generates the app manifest and the scope list; warns if this install's URL cannot work |
| π | includes/messaging/messaging.php |
messagingProvider() dispatch, and normaliseChannelIdentifier() β which is channel-aware for a reason, see below |
| π₯ | api/messaging/webhook.php |
The public endpoint. Per-channel, signature-verified, handles Slack's one-time URL challenge |
| π₯ | includes/messaging/ingest.php |
Message β ticket. Holds the thread-is-a-conversation rule and resolveSlackRequester()
|
| π€ | api/messaging/send_message.php |
The analyst's reply, posted back into the thread |
| π₯οΈ | system/integrations/slack.php |
The settings page: workspaces, the manifest, the tests |
| π₯οΈ | system/integrations/help_slack.php |
The in-app setup guide |
| π₯οΈ | api/messaging/slack_manifest.php |
Serves the manifest to that page |
| ποΈ | messaging_channels |
provider='slack', channel_type='slack', channel_ref = the workspace id, credentials encrypted |
| π |
lang/en/system.php, lang/pt-BR/system.php
|
The slack_* keys |
Slack's read methods do not accept a JSON body. chat.postMessage does; users.info does not β given JSON it parses no arguments at all and answers user_not_found for a user that plainly exists. Nothing errors. Every call is form-encoded, which every method accepts. This survived a full unit-test suite because auth.test takes no arguments.
The setup deadlock. Slack verifies the request URL the instant the app is created β which is also the instant the signing secret first exists. Requiring a signature on that handshake makes setup impossible, not merely strict: no verified URL β no secret β no verified URL. The handshake is answered unsigned only while the channel has no stored secret. A real event_callback always requires a valid signature, so nothing can be injected through the gap.
The old sender normaliser was phone-shaped. It stripped every non-digit, turning U08ABCDEF into +08 β five distinct Slack users collapsed onto two requesters and would have read each other's tickets. Any future non-phone channel must pass its channel type.
Bot messages come back as events. Ingesting them makes the ticket talk to itself forever. Three separate guards, because Slack marks bot messages inconsistently across subtypes.
The webhook URL used to double the app path when the configured public base URL already contained it β producing /freeitsm-app/freeitsm-app/β¦. That string is what gets pasted into Slack, and the 404 fails verification with an error that mentions nothing about a path. It affected WhatsApp too.
Message shortcuts (raise a ticket from any message via the β¦ menu), /ticket slash commands, ticket cards with action buttons, incident channels created from a ticket, and any Slack chatbot surface. The shortcut and a /find card are the strongest candidates for the next slice; both need a second interactivity endpoint.
- WhatsApp channel Β· Web chat channel β the same messaging engine
- Issue trackers (Jira, Azure DevOps) β the engine Slack deliberately does not use
- Webhooks β posting out to Slack, which needs none of this
- Multi-Tenancy β pinning a workspace to one company
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
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ π οΈ 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)