-
Notifications
You must be signed in to change notification settings - Fork 15
CRM and Service Catalogue
Status: blue sky. Explored, not committed. See Blue sky thinking for what that means.
The idea in one line: FreeITSM already knows who you serve, what they run, what breaks, and how much of your week it eats β it just never joins those four things up, so nobody can answer "is this account actually worth it?"
This page argues that the useful version of "CRM" here is not a sales tool, and that a service catalogue is not the request catalogue we already have. It also argues that most of the value is wiring, not new modules β which is the same conclusion the iTop gap analysis reached about the CMDB.
FreeITSM knows far more about the companies it buys from than the companies it serves.
| Suppliers (who we buy from) | Customers (who we serve) | |
|---|---|---|
| Entity |
suppliers β 19 columns
|
tenants β 6 columns
|
| What's recorded | legal name, trading name, company reg number, VAT number, type, status, full address, due-diligence questionnaire issued/received dates, comments | id, name, slug, is_default, is_active, created |
| Contacts |
contacts β first/last name, email, job title, mobile, direct dial, switchboard
|
users β email, display name, password, theme preference |
| Commercial |
contracts β value, currency, start, end, notice period, notice date, payment schedule, cost centre, owner, DPIA status |
nothing |
Read the bottom two rows again. We store a supplier contact's switchboard number and a customer contact's theme preference. There is no job title on a customer contact, no phone number, no manager, and no commercial record of any kind attached to a customer.
This isn't an oversight so much as an accident of order: suppliers and contracts arrived as a procurement feature, and tenants arrived as a multi-tenancy primitive β a scoping key, not a customer record. Nobody ever designed the customer.
So "add a CRM" is the wrong framing. The honest framing is: the customer side of the model is a stub, and the supplier side already shows what a filled-in version looks like.
Before adding a service catalogue, note that FreeITSM currently has three unrelated ideas of what a service is, none of which know about each other:
| Where | What it is | On the dev install |
|---|---|---|
| CMDB | objects of the user-defined Service class β real things in the estate, with relationships, owners and a blast radius |
6 objects |
| Service status page |
status_services β a hand-kept list of names for the public status page |
6 rows |
| Self-service portal | portal-visible forms β things a person can request
|
1 form |
These almost certainly overlap in name and definitely don't in data. Someone maintaining a status page types "Email" by hand; a CI called "Email Relay" already exists in the CMDB with a documented dependency chain; and neither is connected to the form somebody fills in to ask for a mailbox.
π A service catalogue's first job is to reconcile those three, not to become a fourth. If the outcome of this work is a services table sitting alongside all of them, it has failed. The interesting version makes the CMDB's Service objects the spine and teaches the other two to point at it β the status page publishes a CI, the portal request is for a CI, and the blast radius already tells you what underpins it.
This is the distinction that sinks most ITSM catalogue projects, so it's worth being blunt.
- A request catalogue answers "what can I ask for?" β a laptop, a mailbox, access to a system. It is a menu for end users. FreeITSM has this: portal-visible forms, with approvals (#928) and lookup fields that search real records.
- A service catalogue answers "what do we actually run, for whom, to what standard, and at what cost?" β Email, Payroll, the customer-facing website. It is a management instrument. FreeITSM has nothing like this.
They are related β a request is usually against a service β but they have different owners, different lifecycles and different readers. Merging them produces the classic failure: a catalogue that is 90% "order a mouse" with the three services that actually matter buried among them.
What a service record would carry that nothing today does:
- an owner (a person accountable, not a queue)
- who consumes it β which tenants are entitled to it
- the standard it's held to β which SLA calendar and targets apply
- what it costs to run, and what it's charged at
- what it depends on β which is the one part already built, in the CMDB
- its state β live, degraded, retiring β which is what the status page is already hand-maintaining
There is no version of this where FreeITSM competes with a real CRM, and trying produces a module nobody uses. Specifically, avoid:
- Pipeline theatre. Deals, stages, weighted forecasts, probability percentages. This is the single most abandoned feature category in the category. If nobody is paid to keep it current, it is stale within a month and then actively misleading.
- Marketing anything. Campaigns, sequences, lead scoring. Wrong product, wrong user, and a compliance surface (see Β§7) out of all proportion to the benefit.
- Being the system of record for a sales team. If a customer already runs HubSpot or Salesforce, FreeITSM should be able to hand it facts β renewal dates, account health, cost-to-serve β not ask a salesperson to type into two places.
What is genuinely missing is the thing an ITSM tool is uniquely placed to hold: the operational truth about a customer relationship.
A CRM built on ticket data can answer questions a sales CRM structurally cannot:
"This client pays Β£2,000 a month. They logged 61 tickets last quarter, we breached SLA nine times, their CSAT is 2.9, and we spent 143 analyst hours on them. We are losing money and they are unhappy β those are the same fact."
No sales CRM knows that. FreeITSM already holds every input.
Ordered by value Γ· effort, most worthwhile first.
Buildable now. ticket_time_entries already records time_spent_minutes per ticket per analyst. Group by tenant, multiply by a cost rate, compare with contract value.
The only genuinely missing input is an analyst cost rate, which is one nullable column and a settings field. Everything else is a query over data you already have.
Why it's first: it's the number that changes behaviour. Every MSP suspects it has one or two accounts eating the margin from the rest, and almost none can prove which. This turns a suspicion into a sorted list.
Mostly built, pointed the wrong way. contracts already has contract_end, notice_period_days, notice_date and contract_value, and the workflow engine already fires a contract.expiring trigger.
But contracts.supplier_id means every contract in the system is one we signed as a buyer. There is no customer-side contract. Two options, and they are genuinely different bets:
-
A direction flag on
contracts(counterparty_type= supplier | customer). Cheap, reuses the whole module including terms, DPIA tracking and the expiry trigger. Risk: "supplier" is baked into column names, the UI and the permissions, and a flag that half the code ignores is worse than a separate table. - A separate customer-agreement entity. Clean, but duplicates a lot of the contract module and splits "when does this end?" across two places.
Neither is obviously right. That's one reason this page is parked rather than scheduled.
A single per-tenant view built entirely from existing tables: ticket volume and trend, SLA breach count (sla_notifications_sent), CSAT (ticket_csat_responses), reopen rate, oldest open ticket, time logged.
The discipline that makes this useful rather than a vanity dashboard: every number must be clickable through to the tickets that produced it. A health score nobody can interrogate gets ignored the first time it disagrees with somebody's gut β which is precisely the moment it was most likely to be right.
Once services exist and tenants can be subscribed to them, three things fall out almost free:
-
The portal shows only what you're entitled to. Today portal visibility is a single
is_portal_visibleflag on a form β global, not per-customer. - Tickets can be routed and prioritised by service, not just by keyword.
- "Am I paying for something I don't use?" becomes answerable, in both directions.
This is the piece that makes the service catalogue earn its place rather than being documentation.
The CMDB's blast radius (cmdbBlastRadius()) already computes what breaks when a CI fails. Attach services to CIs and the same walk answers a commercial question:
"This server is being decommissioned on Friday. That takes out Payroll, which four customers are subscribed to, worth Β£11k a month between them."
That is a genuinely novel sentence β the existing engine, asked a question it wasn't built for, and one no CRM or monitoring tool can answer because neither knows the dependency graph.
Deliberately unambitious, in the order that keeps each step useful alone:
-
Fill in the customer. Extend
tenantswith the fieldssuppliersalready has and nobody regretted: account owner, status, address, notes. No new module, no new UI paradigm β just stop the customer being a stub. -
Give customer contacts a job title and a phone number.
userscurrently has neither. It's embarrassing next tocontacts.switchboard. - Cost-to-serve as one report. Blended rate in settings, one page, sorted descending by "minutes per pound".
- Only then, decide the contract-direction question, and only then think about services.
Steps 1β3 are small, independently useful, and commit to nothing. Step 4 is where the real design decision lives β which is exactly why it should not be first.
-
Two sources of truth for "company."
tenantsis load-bearing for multi-tenancy β it is a security boundary, not a CRM record. Anything added must extend it, never sit beside it. A separateaccountstable that "mostly" maps to tenants is how isolation bugs get born, and the CMDB case study already shows how unforgiving that boundary is. -
A fourth idea of "service." See Β§2. If this ships a
servicestable that doesn't reconcile CMDB objects,status_servicesand portal forms, it has made the problem worse and called it progress. - Sales users inside an analyst RBAC model. FreeITSM's permissions are built around analysts and modules (Admin & access control). An account manager who should see commercials but not ticket contents is a shape the current model doesn't express. That's a real piece of design work, not a checkbox.
- Personal data creeping in sideways. The moment "CRM" appears, so do notes about individuals, call logs and relationship history. Suppliers already carry DPIA fields precisely because somebody thought about this once; a customer-side CRM inherits the same duty and currently has none of the scaffolding. Data retention needs answering before the first free-text "notes about this person" box, not after.
- Making the portal feel commercial. The self-service portal is used by people with a broken laptop. Anything that makes it read like a storefront β upsell prompts, "your plan doesn't include this, upgrade?" β costs more goodwill than it earns. Entitlement should quietly show fewer options, never advertise the ones withheld.
- Cost-to-serve as a stick. The number is for deciding which contracts to renegotiate. Pointed at individuals it becomes a productivity surveillance tool, and analysts will respond by not logging time β which destroys the only input the feature has.
- Building it because it's a category. "ITSM tools have CRMs" is not a reason. The specific reasons above (margin visibility, renewal dates, entitlement) are. If those aren't the goal, none of this is worth the maintenance.
- Customer contracts: flag or separate entity? Β§5.2. Genuinely undecided, and it gates most of the rest.
-
Is the customer a
tenant, always? On a single-company install, everything is one tenant β so an MSP with 40 clients uses tenants as accounts, while an internal IT department has one tenant and many departments. Does "account" mean tenant, or something below it? These are different products and the answer changes the schema. - Where do services live? CMDB objects (reuses the graph, ties services to a module some installs don't use) versus their own table (independent, but re-creates relationships and re-splits the truth).
- Does any of this belong in the REST API v1 on day one? Handing facts to an existing CRM is arguably the primary use case, not an afterthought β which would make the API the first surface rather than the last.
Not because it's a bad idea β Β§5.1 and Β§5.2 are among the more obviously valuable things on any list β but because:
- the customer-side model is a stub, so this is foundation work wearing a feature's clothes, and foundations are where the expensive mistakes are made;
- question 2 above is unanswered, and it changes the schema rather than the UI;
- and the failure mode is unusually bad: a half-built CRM is not a partly-useful CRM, it is a second place to look that is always slightly out of date.
The pieces most worth stealing early are cost-to-serve and filling in the customer record β neither of which requires deciding what a CRM is.
Part of Blue sky thinking. Related: Contracts Β· Self-service portal Β· CMDB Β· Multi-tenancy Β· Forms
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)