Time tracking¶
Issues carry an original estimate and a remaining estimate, and people log work against them. Time spent is the sum of an issue's worklogs. Estimates and time show in the issue view, on board cards, in searches, in the sprint report (in hours) and on the timesheet.
Durations¶
Durations are written the way people say them: 30m, 2h, 1h 30m, 1.5h, 1d, 1w 2d 3h.
Units are w (weeks), d (days), h (hours) and m (minutes); full words (2 days, 3 hours)
work too, units may come in any order and a bare number is minutes. A day and a week are the
working day and week:
| Variable | Default | |
|---|---|---|
WFM_TIME_HOURS_PER_DAY |
8 |
hours in 1d (more than 0, at most 24; 7.5 is fine) |
WFM_TIME_DAYS_PER_WEEK |
5 |
days in 1w (more than 0, at most 7) |
So with the defaults 1w 2d is 56 hours. Changing the settings changes how stored durations are
displayed, not the durations themselves: everything is stored in seconds. The UI reads the
settings from GET /api/v1/time-tracking/settings; the parser and formatter exist in Go
(internal/timetrack) and TypeScript (web/src/lib/duration.ts) with the same rules and tests.
Estimates¶
Set them in the issue's Time tracking panel (click a value to edit it), or through the API as
originalEstimate and remainingEstimate (seconds) on POST /issues and PATCH /issues/{key}
(null clears one). Setting an original estimate on an issue without a remaining estimate sets
the remaining estimate to the same value. Changes are recorded in the issue history.
The panel's bar shows time logged (blue) and remaining (grey) against the original estimate. When logged plus remaining goes past the estimate, the part beyond it turns red, the estimate is marked on the bar and the panel says by how much the issue runs over.
Logging work¶
Log work (in the panel or the Work log tab of the activity section) asks for the time spent, when the work was done (default now, at most a day ahead), an optional description, and what to do with the remaining estimate:
| Option | Remaining estimate |
|---|---|
| Adjust automatically (default) | goes down by the time logged, never below zero; unchanged if the issue has none |
| Set to | the value you enter |
| Leave unchanged | as it was |
Editing a worklog's time with automatic adjustment moves the remaining estimate by the
difference; deleting a worklog puts its time back (?adjustEstimate=leave keeps it, and
?remainingEstimateSeconds= sets it).
Permissions: whoever can edit the issue can log work on it (issue.edit for issues you
reported or are assigned to, issue.edit_others for the rest). You can change or delete your own
worklogs while you can work on the project's issues; project admins can change anyone's.
Automation rules cannot log work.
Every worklog added, changed or deleted is recorded in the history (worklog, with the
durations) along with any remaining estimate change, bumps the issue's version and fires an
issue.updated event with fields: ["timeSpent"] (plus "remainingEstimate" when it changed), so
boards and open issue views refresh live and automation rules on field changes can react.
Where it shows¶
- Board cards: add the Time tracking card field (board settings, or Configure board on
the board). Cards show time logged of the original estimate (
3h / 1d, red once over), else the remaining estimate (5h left) or the time logged. - Search (search.md):
originalEstimate,remainingEstimateandtimeSpentcompare with durations (remainingEstimate > 4h,timeSpent >= "1d 2h",originalEstimate is EMPTY) and can be ordered by;worklogAuthor = currentUser()andworklogDate >= startOfWeek()find issues by their worklogs. - Workflows and automation: CEL conditions see
issue.originalEstimate,issue.remainingEstimateandissue.timeSpentin seconds (0when unset), e.g. a validatorissue.timeSpent > 0on a Done transition. - AI assistants:
get_issueshows the time tracking andlog_worklogs work (mcp.md).
Sprint report in hours¶
The sprint report (Reports page, and the sprint burndown, burnup and health dashboard gadgets) has an Hours measure next to story points and issues:
- Committed — the remaining estimate of the sprint's open issues when it started.
- Remaining each day — the remaining estimate of the issues in the sprint that are not done, replayed from the estimate history. Completing an issue burns what was left of its estimate.
- Logged — time logged on the sprint's issues while they were in the sprint, counted when it was logged (the moment the remaining estimate moves with it).
- Scope (burnup) — logged plus remaining, so it moves only when work is re-estimated, added or taken out, not when work is logged with the estimate adjusted.
- Added / removed — the remaining estimate of issues that joined or left after the start, at the time they did.
GET /api/v1/sprints/{id}/report returns these as committedSeconds, completedSeconds,
addedSeconds and removedSeconds, and per day remainingSeconds, completedSeconds,
scopeSeconds and idealSeconds.
Timesheet¶
Timesheet (in the sidebar, or from a project's Reports page) totals the time logged per person per day for a week or a month, in hours, on every project you can browse or on one, for everyone or one person. Open a person's row to see the issues. Days are counted in your browser's time zone, by when the work was done. Export CSV downloads the sheet: a row per person and issue with hours per day, a total row per person and a grand total.
GET /api/v1/worklogs?from=2026-10-19&to=2026-10-25&projects=PLAT,OPS&user=<id>&tz=Europe/London
returns {dates, dayTotals, total, people: [{user, days, total, issues: [{issue, days, total}]}]}
(seconds), covering at most 366 days. Worklogs on issues you cannot browse are left out.
API¶
| Endpoint | |
|---|---|
GET /time-tracking/settings |
{hoursPerDay, daysPerWeek} |
GET /issues/{key}/worklogs |
the issue's worklogs, in the order the work was done; each says whether you can change it (canEdit) |
POST /issues/{key}/worklogs |
{timeSpentSeconds, startedAt?, comment?, adjustEstimate?: auto\|leave, remainingEstimateSeconds?} |
PATCH /worklogs/{id} |
change time, start or comment, with the same estimate options |
DELETE /worklogs/{id}?adjustEstimate=&remainingEstimateSeconds= |
delete; the time goes back on the estimate unless left |
GET /worklogs?from&to&projects&user&tz |
timesheet |
Every issue in the API has timeTracking: {originalSeconds?, remainingSeconds?, spentSeconds}.