Measuring the time on a ticket and billing that time are two different problems. One is about team productivity: how much real time the technician spent. The other is about the contract: how many hours the client has consumed and how many remain. Confusing the two is the mistake we see most often in sustainment - and the choice between ActualTime and Contract Hours starts exactly there.
The problem: three planes of "time" in GLPI
Before picking a plugin, it helps to separate three things GLPI treats differently:
- Estimated time - the duration you planned for the task.
- Real time - how long the technician actually took, timed.
- Billable time - what goes on the contract, already rounded and with commercial rules.
The task's native Duration field (stored in seconds in actiontime) covers the basics, but requires manual entry and knows nothing about the contract. That is where the two plugins come in.
ActualTime (TICGAL)
ActualTime is a free, open source plugin by TICGAL. It adds play, pause and stop buttons to the ticket task and times the real time the technician spends on it. When you stop, it writes that measured time to the task duration. The focus is honest and narrow: turning "I think it took about 40 minutes" into a measured number. It has no notion of contract, balance or billing - and it does not need one, because its goal is effort metrics, not charging.
Contract Hours (NexTool)
Contract Hours is a licensed NexTool module aimed at those who sell hour packages. The timer sits on the ticket timeline, but what it produces is different: on stop, the time is rounded up to the configured billable minimum (default 15 minutes), becomes an automatic private TicketTask, and debits the balance of the contract linked to that ticket. It carries what ActualTime does not: a monthly quota per contract, a consumption statement, an "Hours Consumption" dashboard for the client on the simplified interface, alerts for expired or exceeded contracts, and, since version 3.6, the concept of non-billable (courtesy) hours - time that is recorded but does not deduct from the balance.
Side-by-side comparison
| Criterion | Native duration (manual) | ActualTime (TICGAL) | Contract Hours (NexTool) |
|---|---|---|---|
| How time is captured | Manual entry | Timer on the task | Timer on the timeline |
| Measures real time | No (whatever you type) | Yes | Yes |
| Billable rounding | No | No | Yes (configurable minimum, multiple of 5) |
| Contract link | No | No | Yes (by entity; group AND category) |
| Hours balance / quota | No | No | Yes (monthly quota + alerts) |
| Non-billable (courtesy) hours | No | No | Yes (per-task toggle) |
| Consumption statement / report | Native (limited) | Basic | Detailed by cycle / period |
| Client-facing dashboard | No | No | Yes ("Hours Consumption") |
| Where time is stored | actiontime | actiontime | actiontime (source of truth) + balance |
| License | Native | Free (GPL) | Licensed |
| GLPI supported | 10 and 11 | 10 and 11 | 11 only |
The trade-off that matters in operation
In sustainment, we have run both: ActualTime for clients whose pain was internal productivity, Contract Hours where the hour contract was the heart of billing. The detail that only shows up while operating is that both end up touching the same native field, the task's actiontime. ActualTime writes the measured real time there; Contract Hours treats the task's actiontime as the source of truth (since 3.4.2) and creates a private task with the already-rounded time. Running both on the same ticket, without a rule about who "owns" the task, produces two competing durations - the technician's raw measurement fighting the rounded billable entry. That is why we decided to separate the planes: ActualTime as an internal metric (never billable) and Contract Hours as the contractual plane, and never let both create a task on the same ticket. The reason is simple: the number that goes on the client's invoice must be the rounded, contract-aware one, not the raw seconds - and the raw seconds are precisely what management wants for capacity planning. Two audiences, two numbers, two tools.
The common mistake is expecting ActualTime to answer "how many hours are left on the contract?". It has no such concept. Every client who tried to build that by exporting ActualTime data into a spreadsheet saw the number drift from reality within weeks. And there is the rounding gap: Contract Hours rounds up to the billable minimum (15 minutes by default, a multiple of 5 for GLPI compatibility). A 3-minute task bills 15. In a month with many short tickets, the difference between the real total (ActualTime) and the billed total (Contract Hours) is material - and that difference is exactly what you negotiate with the client, or absorb as courtesy through the non-billable toggle.
Measure the gap yourself
Because both plugins write to the native actiontime field (in seconds), you can quantify the logged time straight from the database, without depending on a plugin table. Sum it per technician for the month and compare it with the billed total shown in the Contract Hours statement:
-- Hours logged per technician in the month (native actiontime field, in seconds)
SELECT u.name AS technician,
ROUND(SUM(tt.actiontime) / 3600, 2) AS logged_hours
FROM glpi_tickettasks tt
JOIN glpi_users u ON u.id = tt.users_id_tech
WHERE tt.date BETWEEN '2026-06-01' AND '2026-06-30 23:59:59'
AND tt.actiontime IS NOT NULL
GROUP BY tt.users_id_tech
ORDER BY logged_hours DESC;
If this total matches the real measured time but diverges from what was billed, the difference is the rounding plus the courtesy hours - and that is negotiation information, not an error.
Verdict by scenario
- Use ActualTime when the question is "is my team productive?". You want to measure real effort, feed internal indicators, and you do not charge by the hour. Zero cost, simple install.
- Use Contract Hours when you sell hour packages and the client asks "how much have I used and how much is left?". You need balance, statement, billable rounding, a client dashboard and an overrun alert. It is an investment, but it pays for itself on the first contract you stop under-billing.
- Use both when you want the internal real time AND the external billable one, as long as they are on separate planes: ActualTime as a metric (non-billable) and Contract Hours as billing, with a clear rule that only one creates a task per ticket.
- Stay on the native field when volume is low and manual duration already solves it. Do not install a plugin for a problem you do not have.
Cost and effort
ActualTime is free (GPL): the cost is the usual one for a plugin - validating the build for your GLPI version and including it in the upgrade window. Contract Hours is licensed, but the real deployment effort is not in the timer, it is in the contract model: defining the quota, mapping the group and category that resolve each contract (the rule is AND, not OR), configuring the billable minimum and who sees each contract on the dashboard. It is half an hour of clicking and a few hours of commercial decisions. The return shows up on the first month-end when the statement comes out ready for the client, with no spreadsheet.
At NexTool we deploy and sustain both models on the GLPI of B2B clients. If your bottleneck is turning ticket hours into defensible billing - with balance, statement and a dashboard the client understands - the Contract Hours module of the NexTool plugin solves exactly that. If you are still unsure which plane makes sense for your operation, talk to us.
Reviewed by the NexTool Solutions team.