Two-way Jira worklog sync: how to verify it

What two-way Jira worklog sync means, how to test edits and conflicts, and how Planim Time reconciles Jira with its local offline store.

“Two-way sync” can mean anything from importing an issue list to reconciling edits to the same worklog. The label is not enough; the useful test is whether an edit made in Jira comes back into the tracker without being overwritten later.

Marketing pages use “sync” for several different operations: importing issue names, creating Jira worklogs, or reconciling edits to an existing entry. What people usually need to know is narrower: if a worklog gets edited inside Jira’s own UI, does that change come back into the tracker, and what happens if both copies changed before the next sync?

That’s the failure mode we set out to fix in Planim Time, and it took us about four months longer than we’d planned.

What “sync” usually means in practice

Here’s the path most trackers ship. You start a timer. You stop a timer. The tracker creates a worklog entry locally and, when you click Push (or on a schedule), POSTs it to Jira’s worklog endpoint. Done. From the tracker’s perspective, the worklog is now Jira’s problem.

The hidden assumption in that design is that nothing else ever touches the worklog after it lands in Jira. In a single-engineer setup that’s mostly fine. The moment you’re on a team with a tech lead or a PM who corrects worklogs in Jira directly (bumping a 3h to a 4h because they know you also covered the standup, fixing a typo in the comment, reassigning to a sub-task), the assumption breaks.

The next time you edit anything about that entry in the tracker and push, the tracker does an UPDATE on Jira’s record using its own copy as the source of truth. Your lead’s edit gets silently overwritten. They re-edit. You push again. Round two. Whoever ran the loop last wins, and one of you spends a Tuesday morning trying to reconcile a sprint’s worth of worklogs by hand.

Marketplace apps can solve the problem differently. Tempo operates inside Jira’s installed-app model. Clockify’s current Marketplace app explicitly documents two-way changes between Jira worklogs and Clockify entries, which is materially different from its older extension-led workflow. We dig into the remaining trade-offs on Planim Time vs Tempo and Planim Time vs Clockify.

Why true two-way is hard to ship

The reason most trackers don’t do this isn’t laziness. It’s that Jira’s REST API is genuinely awkward for the use case.

Jira can emit webhooks for worklog events, but Cloud’s webhook system is designed around installed apps. Planim Time deliberately avoids a Marketplace tenant install and authenticates as the user through OAuth or an API-token fallback. We poll instead of requiring an installed webhook app. The separate no-admin guide explains where site authorization and token policy can still create an admin gate.

Polling worklogs is fine in principle and awkward in detail. There’s no clean “give me everything that changed since timestamp T” endpoint scoped to one person: worklog/updated returns the IDs of every worklog touched anywhere on the site since a cursor and leaves you to batch-fetch the actual fields, so on a busy site most of what comes back is other people’s entries. We ended up not using it at all. The pull described below runs a JQL search for your own worklogs inside a date window and reads each matching issue’s worklog list, which is more requests than a delta feed, but every one of them is about you, which matters because Jira Cloud’s rate limits are not generous.

The other awkward bit is conflict resolution. If a worklog was edited locally and remotely between two pulls, both copies have a real updated timestamp and a real author and real content, and you need a deterministic rule for which one wins. We considered last-write-wins (simple, surprises the user when their five seconds of typing get overwritten) and merge-by-field (clever, almost impossible to make legible). We ended up with something blunter than either.

How we actually do it

The pattern that ended up working has three rules.

The pull is scoped by date, not by cursor. Opening a day in the Work Logs tab, or a week or month in the calendar, runs one JQL search for the issues where you logged time in that window (worklogDate in range, worklogAuthor = currentUser(), with two days of slack on each side because Jira evaluates worklogDate in your Atlassian profile’s timezone, not your machine’s), then reads each issue’s worklog list. Only entries under your own account are reconciled; teammates’ hours are a separate read-only view. The Work Logs tab repeats that pull about once a minute for the day on screen. The calendar repeats it whenever it opens or changes period (the calendar is Pro), and its Sync button forces a pull at any moment.

Jira wins for anything Jira already has. A synced row carries the updated timestamp Jira gave it. If a pull brings back a different timestamp, the local copy is overwritten: start time, duration, comment, all of it. A synced worklog missing from Jira’s answer is deleted locally, but only when the search itself succeeded and stayed under the app’s own cap of 1,000 issues, so a flaky or oversized answer cannot wipe a week. Entries that have never been pushed are never touched by a pull; they stay on your side until you push them.

Local edits do not wait for a pull. Change a synced entry, drag it to another hour or pull its edge, and the app writes that change to Jira immediately through your account, showing the result optimistically and rolling it back if Jira refuses. The response carries a fresh updated timestamp, which becomes the row’s new high-water mark. One guard sits between the two paths: a pull compares against the timestamp it snapshotted before fetching, so if you edited that very entry while the fetch was in flight, the stale answer is skipped instead of moving your block back, and the next pull brings the truth.

There is no three-way merge because there is nothing to merge: Jira holds the one copy, your edit goes straight into it, and the next pull shows whatever Jira holds. The honest corollary is that an edit you make against a copy that is a minute stale overwrites whatever your lead changed in that minute; you see their version first only if the pull got there before you did. In practice that is the property we wanted: if your tech lead bumps a 3h to a 4h in Jira, you’ll see it in the Work Logs tab within a minute, or on the calendar the next time you open it or move to another week, without lifting a finger, and your next edit starts from their number.

Where this still goes wrong

We’re not going to pretend it’s airtight.

The pull window means you can briefly see stale data. Alt-tab to Jira, edit a worklog there, alt-tab back, and the calendar keeps the old version until you change the period, reopen it, or press Sync; the Work Logs tab catches up on its next minute. Most people don’t press Sync, so they sometimes notice the lag.

Entries outside the dates on screen are invisible to the pull. The search only covers the window you are looking at, so a worklog someone moved in Jira from this week to last month vanishes from this week’s view at once and only appears on last month’s view when you open it. Nothing is lost; it just isn’t fetched until a view asks for those dates.

Deletes are the awkward ones. A worklog deleted in Jira shows up as an absence: the pull expected it inside the window and didn’t find it, so the local row goes. That check runs only when the search came back complete. If the search hits the app’s own 1,000-issue cap for one window, deletions are skipped for that pull rather than guessed at; if a single issue’s worklog fetch fails, they are skipped for that issue. We would rather keep a stale row for a minute than delete a real one on a half-answer. Delete an entry that was never pushed and nothing happens in Jira, because Jira never had it.

What it means for you

If you work alone, or on a team where worklogs are write-only (you set them, nobody else touches them), push-only is genuinely fine. You don’t need two-way sync, and a tracker that only pushes will work for you indefinitely.

If you work on a team where leads, PMs, or finance edit worklogs in Jira directly, whether for accounting, for sprint review, or for any reason, push-only trackers cost you real time. Every week you’ll spend ten or twenty minutes reconciling discrepancies you didn’t know existed. The math on that compounds quickly: an engineer-hour a fortnight is more than what most of these tools cost.

That’s the case Planim Time for Jira is built for. If you’re in it, download Planim Time and point it at a real instance with real worklogs being edited by real teammates for a sprint. The two-way machinery either earns its keep in the first week or it doesn’t, and you’ll know which.

(We wrote separately about why the desktop tracker exists at all. That’s the why; this post is the how.)

Frequently asked questions

What is two-way worklog sync in Jira?
Two-way sync means changes flow in both directions: the tracker pushes worklogs to Jira, and edits made inside Jira's own UI come back into the tracker. The test that matters is narrow: if someone edits a worklog directly in Jira, does the tracker pick that change up, or does its next push overwrite it with a stale local copy? Product labels vary, so test the same entry in both directions.
Why do most Jira time trackers only push one way?
Because round-trip sync is genuinely awkward to build on Jira's REST API. A desktop client that authenticates as the user cannot rely on the same tenant app webhook architecture as a Connect or Forge app, so polling is often the reliable path. The worklog/updated endpoint returns changed IDs that still have to be fetched and merged under rate limits. Push-only avoids that work.
What happens when a worklog is edited in both Jira and the tracker?
A sync engine needs a deterministic rule. Planim Time's is blunt: an edit you make to a synced entry is written to Jira immediately through your account, and on every pull Jira's copy overwrites the local one for entries Jira already has. Entries you have not pushed yet are never touched by a pull. A pull never drops a local change; if Jira refuses an edit, the app rolls it back and tells you.
Do I need two-way sync if I work alone?
No. If nobody else ever edits your worklogs in Jira, a push-only tracker will work for you indefinitely. Two-way sync starts paying for itself on teams where a lead, PM, or finance person corrects worklogs in Jira directly; without it, each of their edits gets silently overwritten on your next push and someone spends time reconciling by hand.
Does Jira send webhooks when a worklog changes?
Jira Cloud can emit worklog events, but the webhook system is designed around installed apps. Planim Time is a desktop client with no Marketplace tenant install. It authenticates as the user through OAuth or an API-token fallback, then pulls your own worklogs for the dates on screen: a JQL search on worklogAuthor and worklogDate, followed by each issue's worklog list. OAuth authorization and user API tokens can still be restricted by organization policy.