Why twelve, and why one page
Most field service businesses don't lack numbers. They have a job system full of visits, a CRM full of quotes and an accounting package full of invoices, and each one comes with its own dashboard. What the managing director usually lacks is a short list that is read every Monday, argued over, and acted on before Friday.
Twelve is a working limit rather than a magic number. It is enough to cover the three things that sink a contractor (the work isn't getting done, the money isn't coming in, the next quarter's work isn't being won) and few enough that every line can have a named owner who expects to be asked about it. If a KPI has no owner, or nobody can say what they would do if it moved, it belongs in an appendix, not the pack.
The test for each line is simple: does a change in this number lead to a decision this week? Utilisation falling leads to a conversation about scheduling. Unbilled work rising leads to finance chasing engineers for job sheets. A total-revenue-this-year figure rarely leads to anything on a Monday, so it stays out.
The twelve KPIs and how they balance
The set below is grouped by the question it answers. Six are about operations, four about cash and margin, two about sales. Some are leading indicators (they move before the money does) and some are lagging (they confirm what already happened). A pack made only of lagging numbers tells you about problems a month late; a pack made only of leading ones never connects to the bank balance.
| # | KPI | Group | Leading or lagging | What it pairs with |
|---|---|---|---|---|
| 1 | Engineer utilisation | Operations | Leading | First-time fix: high utilisation with falling fix rates usually means rushed visits |
| 2 | PPM completion | Operations | Lagging | Open jobs over 30 days |
| 3 | Reactive SLA attendance | Operations | Leading | Utilisation: when engineers are full, reactive response slips first |
| 4 | First-time fix rate | Operations | Leading | Jobs completed: a return visit inflates the job count without adding revenue |
| 5 | Jobs completed | Operations | Lagging | Unbilled completed work |
| 6 | Open jobs over 30 days | Operations | Leading | PPM completion |
| 7 | Unbilled completed work | Cash | Leading | Jobs completed |
| 8 | Aged debt over 60 days | Cash | Lagging | Cash collected |
| 9 | Cash collected | Cash | Lagging | Aged debt |
| 10 | Contract gross margin | Cash | Lagging | Utilisation and first-time fix |
| 11 | Quote conversion rate | Sales | Lagging | Open quoted value |
| 12 | Open quoted value | Sales | Leading | Quote conversion |
The pairs matter more than the individual lines. A pack that shows jobs completed going up looks like good news until you read the unbilled line beside it. That is the reason to put them on the same page rather than in three departmental reports.
Each of these has more depth than a pack line can hold. How you define available hours is covered in the engineer utilisation template; matching completed jobs to invoices is in the unbilled jobs report; what counts as "done" for a planned visit is in the PPM compliance report template. This page is about the set and how it fits together.
The one-page layout
One row per KPI, grouped, with the same five things on every row: the number, a seven-week trend, the target, the formula in words, and the exact field it comes from. Writing the source field on the page sounds pedantic. In practice it ends most of the Monday arguments about whose number is right, because anyone can see which system owns it.
KPI definition sheet
26 October – 1 November 2026 · W44
Weekly MD pack: 12 numbers, one page
Fictional fire and security contractor, 42 engineers. Each line shows the number, its seven-week trend, the target, the formula and the field it is read from.
8 on target4 to watch
Operations · 6
01Engineer utilisation
Owner: Operations manager
77.0%
On target · target ≥ 75.0%
- Formula
- hours booked to jobs ÷ available hours
- Source
- timesheet.hours (job_id not null); engineer.contracted_hours − leave.hours
- vs last wk
- −0.3 pts
02PPM completion
Owner: Contracts manager
92.1%
Watch · target ≥ 95.0%
- Formula
- PPM visits done by due date ÷ PPM visits due
- Source
- job.type = PPM; job.due_date; job.completed_at in the FSM
- vs last wk
- −0.7 pts
03Reactive SLA attendance
Owner: Service desk lead
89.8%
Watch · target ≥ 95.0%
- Formula
- reactive jobs attended within SLA ÷ reactive jobs with SLA due
- Source
- job.priority → sla.response_hours; job.logged_at; visit.arrived_at in the FSM
- vs last wk
- −0.9 pts
04First-time fix rate
Owner: Operations manager
81.0%
On target · target ≥ 80.0%
- Formula
- reactive jobs closed on first visit ÷ reactive jobs closed
- Source
- visit.job_id (count per job); job.follow_on_of in the FSM
- vs last wk
- +1.5 pts
05Jobs completed
Owner: Operations manager
312
On target · target ≥ 300
- Formula
- count of jobs with completed_at in the week
- Source
- job.completed_at in the FSM; job.status = complete
- vs last wk
- −3
06Open jobs over 30 days
Owner: Service desk lead
11
Watch · target ≤ 10
- Formula
- open jobs where today − logged_at > 30 days
- Source
- job.status ≠ complete; job.logged_at in the FSM
- vs last wk
- −1
Cash · 4
07Unbilled completed work
Owner: Finance manager
£27,450
Watch · target ≤ £20,000
- Formula
- Σ job value where job complete and no invoice line linked
- Source
- job.completed_at, job.value in the FSM; invoice_line.job_ref in accounting
- vs last wk
- −£1,510
08Aged debt over 60 days
Owner: Finance manager
£88,300
On target · target ≤ £90,000
- Formula
- Σ invoice.amount_due where today − invoice.date > 60 days
- Source
- invoice.amount_due; invoice.date in accounting
- vs last wk
- +£3,400
09Cash collected
Owner: Finance manager
£118,400
On target · target ≥ £105,000
- Formula
- Σ payment.amount received in the week
- Source
- payment.amount; payment.date in accounting
- vs last wk
- +£7,700
10Contract gross margin (4 wks)
Owner: Managing director
38.5%
On target · target ≥ 35.0%
- Formula
- (contract revenue − labour − materials − subcontract) ÷ contract revenue
- Source
- invoice_line.amount by contract_id; timesheet.hours × engineer.cost_rate; bill.amount by job_ref
- vs last wk
- +0.6 pts
Sales · 2
11Quote conversion (4 wks)
Owner: Sales lead
35.9%
On target · target ≥ 35.0%
- Formula
- quotes won ÷ quotes decided (won + lost)
- Source
- deal.stage = won / lost; deal.closed_at in CRM
- vs last wk
- +1.3 pts
12Open quoted value
Owner: Sales lead
£348,200
On target · target ≥ £300,000
- Formula
- Σ deal.amount where deal.stage = quoted
- Source
- deal.amount; deal.stage in CRM
- vs last wk
- +£9,500
A few layout rules that hold up:
- Trend, not just this week. A single week is noise for most of these. Seven weeks is enough to see direction without needing a chart.
- Status against the business's own target, set by the MD, not borrowed from someone else's benchmark. Two states are enough: on target, or watch.
- Rolling four weeks for slow numbers. Margin and quote conversion swing wildly week to week because a single large job or a single lost tender moves them. Label the window on the page.
- No colour without a decision. If something is marked "watch", the owner should arrive with a reason and an action.
Which system owns each number
Every KPI has one home system. Where a KPI needs two systems, one owns the event and the other is joined to it. These are the fields the pack reads, using generic names; Uptick, Simpro, ServiceMax, Xero, Sage and HubSpot all hold equivalents under their own labels (the Uptick integration page covers that tool's field names).
| System | Table | Fields used | Feeds KPIs |
|---|---|---|---|
| FSM (job management) | job | job_id, type, priority, site_id, contract_id, logged_at, due_date, completed_at, status, value, follow_on_of | 2, 3, 4, 5, 6, 7 |
| FSM | visit | visit_id, job_id, engineer_id, arrived_at, left_at | 3, 4 |
| Timesheets | timesheet | engineer_id, date, hours, job_id | 1, 10 |
| HR or payroll | engineer, leave | engineer_id, contracted_hours, cost_rate; leave hours by date | 1, 10 |
| Accounting | invoice, invoice_line, payment, bill | invoice.date, invoice.amount_due, invoice_line.job_ref, invoice_line.contract_id, payment.amount, payment.date, bill.job_ref | 7, 8, 9, 10 |
| CRM | deal | deal_id, stage, amount, closed_at, site_id | 11, 12 |
And the joins that make the cross-system KPIs work:
| Join | Key | Used for | What breaks it |
|---|---|---|---|
| job → invoice_line | job.job_id = invoice_line.job_ref | Unbilled work, margin | Invoices raised by hand without the job reference |
| job → timesheet | job.job_id = timesheet.job_id | Utilisation, labour cost | Hours booked to a generic "admin" job |
| job → contract → invoice_line | job.contract_id = invoice_line.contract_id | Contract margin | Contracts renamed in one system and not the other |
| deal → job | deal.site_id = job.site_id, plus the quote reference on the job | Following won quotes into delivery | Sites created twice with slightly different names |
| bill → job | bill.job_ref = job.job_id | Materials and subcontract cost | Supplier bills coded to a cost centre, not a job |
Most of the work in a reliable pack is in that last column. The formulas are easy; keeping the reference fields filled in is not.
Worked example: one week, end to end
Illustrative and fictional. A made-up fire and security contractor with 42 engineers, week 44 (26 October to 1 November 2026). These are the same numbers as the one-pager above.
| # | KPI | Inputs | Calculation | Result |
|---|---|---|---|---|
| 1 | Engineer utilisation | 42 engineers × 40 contracted hours = 1,680; less 112 hours of leave and training = 1,568 available; 1,207 hours booked to jobs | 1,207 ÷ 1,568 | 77.0% |
| 2 | PPM completion | 214 PPM visits due; 197 done by their due date | 197 ÷ 214 | 92.1% |
| 3 | Reactive SLA attendance | 88 reactive jobs with an SLA deadline in the week; 79 attended in time | 79 ÷ 88 | 89.8% |
| 4 | First-time fix rate | 84 reactive jobs closed; 68 needed only one visit and had no follow-on job | 68 ÷ 84 | 81.0% |
| 5 | Jobs completed | 203 PPM + 84 reactive + 25 quoted works | 203 + 84 + 25 | 312 |
| 6 | Open jobs over 30 days | 146 jobs open at Sunday close; 11 logged more than 30 days ago | count | 11 |
| 7 | Unbilled completed work | 22 reactive jobs £9,870; 9 quoted works £14,300; 7 chargeable PPM extras £3,280 | 9,870 + 14,300 + 3,280 | £27,450 (38 jobs) |
| 8 | Aged debt over 60 days | Total debtors £612,400; invoices older than 60 days £88,300 | sum | £88,300 (14.4% of debtors) |
| 9 | Cash collected | Payments received in the week | sum | £118,400 |
| 10 | Contract gross margin (4 weeks) | Revenue £402,000; labour 3,920 hours × £35 = £137,200; materials £68,900; subcontract £41,300 | (402,000 − 247,400) ÷ 402,000 | 38.5% |
| 11 | Quote conversion (4 weeks) | 64 quotes decided; 23 won | 23 ÷ 64 | 35.9% |
| 12 | Open quoted value | 71 open quotes at stage "quoted" | sum of deal.amount | £348,200 |
Two details in that table are worth slowing down for. First, 203 PPM visits were completed but only 197 of the 214 due were done on time: six of the completions were overdue visits from earlier weeks. Both numbers are right. They answer different questions, and a pack that uses one figure for both will either flatter PPM or understate output. Second, the 312 completed jobs and the £27,450 unbilled are linked: 38 completed jobs, some from this week and some from earlier weeks, still have no invoice line. Read on its own, 312 is a good week. Read next to line 7, it is a good week that hasn't been paid for.
Against this business's own targets, four lines are on "watch": PPM completion (target 95%), reactive SLA attendance (95%), open jobs over 30 days (no more than 10) and unbilled work (no more than £20,000). The other eight are on target. Notice that utilisation at 77% is above target while SLA attendance is below it: engineers are full, and reactive work is what gives.
The traps
Double counting across systems. If revenue is taken from the job system's job values and also from invoices, the same work appears twice. Pick one owner per number: work done from the FSM, money from accounting, sales from the CRM. Cross-system KPIs join to the owner; they never add the two together.
Definitions drifting. "Completed" in the job system might mean the engineer has left site, or that the office has closed the paperwork. If someone changes a status workflow, PPM completion can jump five points with no change on the ground. Write the formula on the pack and keep a dated log of definition changes.
Weeks that don't line up. The FSM may run Monday to Sunday while payroll runs to Friday and the accounting period ends on the last working day of the month. Cut every number on the same boundary or say on the page where it doesn't.
KPIs nobody acts on. If a line has sat on "watch" for six weeks and no owner has changed anything, either the target is wrong or the KPI is. Replace it rather than let the page fill with numbers people have learned to skip.
Manual collation. When the pack is assembled by copying exports into a spreadsheet, each step is a chance to paste last week's column or filter out the wrong region. It also means the pack arrives on Wednesday, by which point half the week's decisions have already been made without it.
Automating the pack with IVP.ai
IVP.ai connects the job system, timesheets, accounting package and CRM into the IVP.ai Data Layer, a governed warehouse where the joins above are defined once and the formulas are written down where everyone can read them. The pack is then a report or agent that runs on the same definitions every week. With Deskless by IVP.ai, an agent can go further: list the 38 unbilled jobs for finance, or flag the reactive jobs that missed their SLA to the service desk lead, rather than waiting for Monday's meeting. More on how the platform and Deskless fit together, and on field service reporting generally.
A national fire safety and security company did this with HubSpot, Uptick and its finance spreadsheets, saving more than 10 hours a week on reporting; leadership now sees performance by service line, region and team without anyone collating it (case study).
To see your own twelve on real data, start with the free 48-hour build: we connect up to three of your sources, clean the data, and have one report or agent running within 48 hours of read-only access. You get 14 days of access, then choose a plan or walk away.