<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vladimir Letiagin</title>
    <description>The latest articles on DEV Community by Vladimir Letiagin (@jorrygo_dev).</description>
    <link>https://dev.to/jorrygo_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1938665%2F7788418f-fcca-4050-b8ab-871a7f432c88.png</url>
      <title>DEV Community: Vladimir Letiagin</title>
      <link>https://dev.to/jorrygo_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jorrygo_dev"/>
    <language>en</language>
    <item>
      <title>How to track meeting time in Jira (and see how much of the sprint it eats)</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Sun, 01 Nov 2026 18:12:03 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/how-to-track-meeting-time-in-jira-and-see-how-much-of-the-sprint-it-eats-3knn</link>
      <guid>https://dev.to/jorrygo_dev/how-to-track-meeting-time-in-jira-and-see-how-much-of-the-sprint-it-eats-3knn</guid>
      <description>&lt;p&gt;Every few months someone asks Atlassian Community how to track meeting time in Jira, and the thread fills with the same three answers. Jira has no notion of a meeting. Time exists only as worklogs, and a worklog must belong to an issue. So meeting time either gets an issue to live on, or it quietly disappears and the sprint looks like it had five more productive days than it did. Where to log it is the first half of this. The second half is reading the number and letting Planim Time fill it in from your calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three places meeting time can live
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A ceremonies issue per sprint.&lt;/strong&gt; Create &lt;code&gt;OPS-15 Sprint 42 ceremonies&lt;/code&gt; with the sprint, and standups, planning, refinement and retro all get logged there. The point is the report: at the end of the sprint the issue's time spent is your ceremony cost, one number, comparable sprint to sprint. Some teams make it one issue per quarter instead; the per-sprint version is easier to compare.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The issue the meeting was about.&lt;/strong&gt; A design review of the checkout flow is checkout work. A call with finance about refunds is refund work. Logged on those issues, the time shows up where it belongs in estimates versus actuals, and in anything billable. Standups don't belong here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not logging it, and cutting capacity instead.&lt;/strong&gt; Some teams plan 6 hours a day instead of 8 and never log a meeting. That works for sprint planning until someone asks for a timesheet or wants to know why the sprint underdelivered.&lt;/p&gt;

&lt;p&gt;Most teams end up with the first two together: ceremonies on the sprint issue, topic meetings on their issue. Write that rule down once, so nobody has to decide per meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the number
&lt;/h2&gt;

&lt;p&gt;Once meetings have a home, the questions become answerable.&lt;/p&gt;

&lt;p&gt;How much of the sprint went to ceremonies: open the ceremonies issue, time spent is the answer. For the trend, a JQL filter like &lt;code&gt;summary ~ "ceremonies" ORDER BY created DESC&lt;/code&gt; lists them, and a worklog report or a CSV export gives you the series.&lt;/p&gt;

&lt;p&gt;How much of a feature was meetings: on the feature issue, the worklog comments tell you. That only works if the comment names the meeting, like "Design review: checkout flow, 1h", and not just "meeting".&lt;/p&gt;

&lt;p&gt;Per person: Jira's built-in reports stop at per project and per version, so for a per-person split you need an external report or an export. Capacity planning off these numbers is its own topic, but teams that measure their meeting share for the first time usually find it larger than they planned for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the numbers without typing them
&lt;/h2&gt;

&lt;p&gt;The Community threads keep coming back because logging meetings means opening Jira six times a day, or rebuilding the week from the calendar on Friday. Every start time and duration is already in the calendar; you only have to add the issue.&lt;/p&gt;

&lt;p&gt;This is what Planim Time does. Connect Google Calendar, Microsoft 365 or, on a Mac, Apple Calendar, with read-only access, and your meetings appear next to your worklogs. An issue key in the title is matched directly. Pick an issue for a meeting once and the app remembers it for that series and that title, so the daily standup goes to the ceremonies issue from then on without asking. Other meetings get keyword suggestions against your issues, and a default issue catches what is left. One click per meeting, or Log all for the day, and each one lands in Jira as a normal worklog with the real start time, the duration, and the meeting title as the comment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8co7fvwylzf86t0n8bv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8co7fvwylzf86t0n8bv2.png" alt="Planim Time week calendar with meetings from the calendar shown as dashed blocks beside logged Jira time" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnglg134onh6css5wv04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnglg134onh6css5wv04.png" alt="The From calendar section on the Work Logs tab: three meetings, their issues and a Log all button" width="800" height="1164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Calendar events stay on your machine, and the Planim Time server is involved only in the sign-in. Private events are not read. The step-by-step, with the manual Log work procedure for comparison, is in &lt;a href="https://time.planim.app/blog/log-meeting-time-in-jira?ref=devto-track-meeting-time" rel="noopener noreferrer"&gt;How to log meeting time in Jira straight from your calendar&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A three-line team rule
&lt;/h2&gt;

&lt;p&gt;Put it in the team's working agreement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ceremonies go on this sprint's ceremonies issue.&lt;/li&gt;
&lt;li&gt;A meeting about a ticket goes on that ticket, with the meeting name in the comment.&lt;/li&gt;
&lt;li&gt;Log it the same day, from the calendar, not on Friday from memory.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>jira</category>
      <category>agile</category>
      <category>timetracking</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Google Calendar to Jira worklog: every way to get meeting time logged</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Sun, 01 Nov 2026 18:11:33 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/google-calendar-to-jira-worklog-every-way-to-get-meeting-time-logged-4b1h</link>
      <guid>https://dev.to/jorrygo_dev/google-calendar-to-jira-worklog-every-way-to-get-meeting-time-logged-4b1h</guid>
      <description>&lt;p&gt;Half of what people search for under "Google Calendar Jira integration" goes the other way: putting Jira due dates and sprints on a calendar. This is about the opposite direction. You have a Google Calendar full of meetings, your team logs time in Jira, and at the end of the week none of those meetings exist in Jira.&lt;/p&gt;

&lt;p&gt;That gap is bigger than most people think. Count the meetings in a normal week, multiply by their length, and you get a day or more that never became a worklog. The calendar already knows when each meeting happened and how long it ran. The only missing piece is which Jira issue it belongs to.&lt;/p&gt;

&lt;p&gt;Here is what works, from the cheapest option up: by hand, a free Marketplace app, Tempo or Clockwork if you already pay for them, and Planim Time, which remembers the issue per meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  By hand, from the calendar
&lt;/h2&gt;

&lt;p&gt;Open the event, note the start and the length. Open the issue in Jira, Log work, type the duration, change Date started from "now" to the meeting's start, paste the title into the comment. Save. Repeat for the next meeting.&lt;/p&gt;

&lt;p&gt;It works, and for two meetings a week it is fine. Past that, nobody does it on the day. It becomes a Friday job, and Friday memory rounds everything to the nearest hour and attaches half of it to the wrong ticket.&lt;/p&gt;

&lt;h2&gt;
  
  
  A free Marketplace app, with one condition
&lt;/h2&gt;

&lt;p&gt;There is a free app on the Atlassian Marketplace called Google Calendar Event to Worklog for Jira Cloud, by Move Work Forward. It reads your Google Calendar and offers events as worklogs. The condition is in the name of the event: it needs the issue key in the title, like &lt;code&gt;PAY-412 refunds sync&lt;/code&gt;, otherwise it has nothing to attach the time to.&lt;/p&gt;

&lt;p&gt;Its last release listed on the Marketplace is version 1.0.0 from October 2023, and it is Google only. A similar app, WiseLog, is marked unsupported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tempo and Clockwork, if you already pay for them
&lt;/h2&gt;

&lt;p&gt;Both big Jira time-tracking apps have Google Calendar connections, and both come as part of the paid product rather than as a standalone feature.&lt;/p&gt;

&lt;p&gt;Tempo Timesheets shows your Google events as cards in My Work. A card logs in one click when the event title contains the issue key; otherwise you pick the issue on each card. Private and "free" events are skipped, and the connection is one-way.&lt;/p&gt;

&lt;p&gt;Clockwork connects Google and Outlook calendars in its Pro plan. You click an event, pick the issue, and the worklog is prefilled with the meeting's time. There is no key matching documented, so it is a pick per event.&lt;/p&gt;

&lt;p&gt;If your organisation already runs one of these, the calendar part is in there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clockify and Toggl: calendar events as their own time entries
&lt;/h2&gt;

&lt;p&gt;Both show your Google Calendar next to their own timer. Clockify has "Copy as time entry" on each event; Toggl can create a time entry automatically a few minutes after a meeting starts. The entries land in Clockify or Toggl, not in Jira. Their Jira integrations are browser extensions that put a start button on the issue page, and nothing moves a calendar event into a Jira worklog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planim Time: pick the issue once
&lt;/h2&gt;

&lt;p&gt;Planim Time is a time tracker for Jira on Mac, Windows and Linux. It connects Google Calendar with read-only access, and meetings from the last 30 days and the next 7 show up next to your worklogs, in a calendar view and at the bottom of each day's list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8co7fvwylzf86t0n8bv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8co7fvwylzf86t0n8bv2.png" alt="Week calendar in Planim Time: Google Calendar meetings as dashed blocks next to logged Jira worklogs" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how the issue gets picked. A key in the title still works. On top of that, once you pick an issue for a meeting, the app remembers it for that series and for other meetings with the same title, so the daily standup gets its ceremonies issue once and never asks again. Meetings with neither a key nor a memory get up to three suggestions by keyword match against your issues, and you can set a default issue for whatever is left. Each meeting shows the reason next to the issue. One click logs it, and Log all logs the whole day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93x9ldqw9zwrnfo5v4zu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93x9ldqw9zwrnfo5v4zu.png" alt="A meeting popover in Planim Time: a design review matched to WEB-77 by the word checkout, with Log and Ignore buttons" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Events are downloaded straight from Google to your machine; the Planim Time server only brokers the sign-in. Private and confidential events are never read. The full walkthrough, including where meeting time should go in Jira in the first place, is in &lt;a href="https://time.planim.app/blog/log-meeting-time-in-jira?ref=devto-google-calendar" rel="noopener noreferrer"&gt;How to log meeting time in Jira straight from your calendar&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things to check before you pick
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The direction. "Jira to Google Calendar" is a different product, and most search results are about that one.&lt;/li&gt;
&lt;li&gt;Whether it needs the issue key in the event title. If it does, the whole team has to start naming meetings that way, or the integration covers only a fraction of your calendar.&lt;/li&gt;
&lt;li&gt;Where the event titles go. Some tools store them on their servers, some keep them on your machine. Check the vendor's privacy page.&lt;/li&gt;
&lt;li&gt;What lands in Jira. You want a worklog with the meeting's real start time and a comment; a time entry in another tool never shows up in Jira.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>jira</category>
      <category>productivity</category>
      <category>googlecalendar</category>
      <category>timetracking</category>
    </item>
    <item>
      <title>How to log Outlook and Teams meetings to Jira as worklogs</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Sun, 01 Nov 2026 18:10:58 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/how-to-log-outlook-and-teams-meetings-to-jira-as-worklogs-394k</link>
      <guid>https://dev.to/jorrygo_dev/how-to-log-outlook-and-teams-meetings-to-jira-as-worklogs-394k</guid>
      <description>&lt;p&gt;Someone asked Atlassian Community in 2019 how to log time from an Outlook calendar appointment to Jira. People are still asking in 2025, on the Toggl forum too, and the answers have not improved much: a Tempo link, an Outlook add-in that has since disappeared, and "create a Meetings ticket".&lt;/p&gt;

&lt;p&gt;The confusion starts with the word "integration". Atlassian's own Outlook add-in is about issues and email: create an issue from a message, see issue details in the sidebar. It does not log time. Most "Jira Outlook calendar" results are about getting Jira due dates and sprints into Outlook, again the wrong direction. What the question means is: I have an Outlook calendar full of Teams meetings, my hours live in Jira worklogs, how do I get one into the other. The options today are by hand, Tempo, Clockwork, and Planim Time connected to Microsoft 365; Toggl and Clockify show the calendar but stop before Jira.&lt;/p&gt;

&lt;h2&gt;
  
  
  The manual way, and why it fails
&lt;/h2&gt;

&lt;p&gt;Open the appointment, note start time and duration. Open the issue, Log work, type &lt;code&gt;45m&lt;/code&gt;, set Date started to when the meeting began (it defaults to now, which is how worklogs end up claiming you were in the morning standup at six in the evening), paste the subject as the comment. Save.&lt;/p&gt;

&lt;p&gt;For one meeting that is a minute. A week of them usually waits until Friday, and by then the times come from memory instead of the calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tempo, if your organisation runs it
&lt;/h2&gt;

&lt;p&gt;Tempo Timesheets has a Microsoft 365 calendar connection. Your Outlook events appear as cards in My Work. Events with an issue key in the subject log in one click; for the rest you choose the issue on the card. Two things to know from Tempo's own docs: it works with Microsoft 365 in the cloud, not with on-premises Exchange, and an organisation-wide setup needs admin consent for the Tempo app in your tenant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clockwork, Toggl, Clockify
&lt;/h2&gt;

&lt;p&gt;Clockwork connects Outlook calendars on its Pro plan. Choosing an event and then an issue gives you a worklog prefilled with the meeting's time.&lt;/p&gt;

&lt;p&gt;Toggl Track and Clockify both show your Outlook calendar inside their apps, cloud Outlook only. Toggl can turn each meeting into a time entry on its own; Clockify has a "Copy as time entry" button. Those entries stay in Toggl or Clockify. Their Jira connections are browser extensions that add a timer button to the issue page, and there is no path from a calendar event to a Jira worklog. The Toggl community thread asking for exactly that has no answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planim Time on Microsoft 365
&lt;/h2&gt;

&lt;p&gt;Planim Time is a time tracker for Jira on Windows, Mac and Linux. It connects to Microsoft 365 through the standard Microsoft sign-in with read-only calendar permission (&lt;code&gt;Calendars.Read&lt;/code&gt;), and from then on your Outlook events, Teams meetings included, show up next to your worklogs: in a week calendar and at the bottom of each day's list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnglg134onh6css5wv04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnglg134onh6css5wv04.png" alt="Planim Time Work Logs tab: a From calendar section lists the day's Outlook meetings with their matched Jira issues and Log buttons" width="800" height="1164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the subject has an issue key, the meeting goes to that issue. When you choose an issue by hand, the app keeps the choice for the whole series and for meetings with the same subject, so recurring meetings stop asking. Anything left gets up to three keyword suggestions from your issues or falls to a default issue. Declined meetings, all-day events and slots marked free are skipped unless you turn them on. Each meeting logs with one click, and Log all logs the day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqncuzue41iki2o5w9026.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqncuzue41iki2o5w9026.png" alt="Calendar settings in Planim Time with Google, Microsoft 365 and Apple Calendar accounts connected and the list of remembered meetings" width="799" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Events go from Microsoft Graph straight to your computer; the server only handles the sign-in. Private and confidential events are not read. The full walkthrough is in &lt;a href="https://time.planim.app/blog/log-meeting-time-in-jira?ref=devto-outlook-calendar" rel="noopener noreferrer"&gt;How to log meeting time in Jira straight from your calendar&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to decide before you connect anything
&lt;/h2&gt;

&lt;p&gt;Where ceremonies go. Standups and planning need an issue, because every Jira worklog does. Most teams keep one ceremonies issue per sprint and log topic meetings on the issue they were about. Decide that before you connect a tool, so logging a meeting doesn't start with working out where it goes.&lt;/p&gt;

&lt;p&gt;Who sees the meeting subjects. The subject of an internal meeting is often more sensitive than the hours. Read the privacy page of whatever you connect, and prefer tools that keep the events on your side.&lt;/p&gt;

</description>
      <category>jira</category>
      <category>outlook</category>
      <category>microsoft365</category>
      <category>timetracking</category>
    </item>
    <item>
      <title>How to log work in Jira for previous days: a whole week at once</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Tue, 13 Oct 2026 18:36:00 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/forgot-to-log-time-in-jira-all-week-rebuild-it-from-your-jira-activity-41hi</link>
      <guid>https://dev.to/jorrygo_dev/forgot-to-log-time-in-jira-all-week-rebuild-it-from-your-jira-activity-41hi</guid>
      <description>&lt;p&gt;Friday, somewhere after four. The timesheet reminder lands in Slack, and the week in Jira looks like this: Monday logged, Tuesday logged, then nothing. Three days, maybe twenty hours of actual work, and the only record is whatever your memory kept.&lt;/p&gt;

&lt;p&gt;Logging work for a previous day in Jira is easy enough: open the ticket, Log work, change the date. Logging three previous days across a dozen tickets is a different job, because first you have to remember what went where. Memory keeps very little. I've done this reconstruction enough times to know the pattern: you remember the big ticket you fought with on Wednesday, you forget the four small ones you closed before lunch, and the review you spent an hour on gets attached to the wrong day. The result looks like a timesheet, but half of it is fiction.&lt;/p&gt;

&lt;p&gt;But you don't have to remember. Jira already wrote it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Jira knows about your week
&lt;/h2&gt;

&lt;p&gt;Every time you touch an issue, Jira keeps a timestamped record of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status changes (To Do, In Progress, Done), in the issue's History tab&lt;/li&gt;
&lt;li&gt;every comment you wrote, with its creation time&lt;/li&gt;
&lt;li&gt;field edits: assignee, priority, sprint, estimate, labels&lt;/li&gt;
&lt;li&gt;issues you created&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a pretty complete trail of &lt;em&gt;which&lt;/em&gt; tickets you were on and &lt;em&gt;roughly when&lt;/em&gt;. What it's missing is duration. Jira records that you moved a ticket to In Progress at 10:14 and commented on it at 11:50. It has no idea whether you worked on it for those ninety minutes or jumped to something else at 10:20.&lt;/p&gt;

&lt;p&gt;So the realistic split is: activity gives you the issues and the order, you supply the hours. Pretending it's more precise than that is how reconstructed logs start reading like fiction again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The manual version: one previous day at a time
&lt;/h2&gt;

&lt;p&gt;You can do this by hand. Open Jira, search for issues you changed that week, and go through them one by one: the History tab shows when you moved it, the comments show when you wrote something. Lay the tickets out in order per day, drop the ones you only touched for bookkeeping (editing an estimate is not work), and give each one a rounded duration. Keep the day capped at the hours you actually sat there, so eight small touches don't add up to an eleven-hour log. Then log every entry with the date the work happened, not today's: the Log work dialog has a date field, it just defaults to now.&lt;/p&gt;

&lt;p&gt;That works. It also takes forty minutes for three days, which is exactly why most people skip it and type "8h, misc" into one ticket instead. If you want the careful version of the method, with the exact search and the tricky cases, I wrote it up &lt;a href="https://time.planim.app/blog/reconstruct-jira-worklogs-from-activity" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The version I built instead: several days at once
&lt;/h2&gt;

&lt;p&gt;I build a desktop time tracker for Jira called &lt;a href="https://time.planim.app/jira" rel="noopener noreferrer"&gt;Planim Time&lt;/a&gt;. It's a menu-bar timer most of the time, but the part that matters for this post is a sidebar called Recent Work, which does the reconstruction above and puts the result on a calendar.&lt;/p&gt;

&lt;p&gt;Here's what it does when you open a week that has gaps:&lt;/p&gt;

&lt;p&gt;It pulls your trail. Through your own Atlassian account it fetches every issue you changed that week, with its history and comments. It keeps your status moves, comments, field edits and created issues, and throws away the bookkeeping: worklog edits, estimate changes, deleted comments.&lt;/p&gt;

&lt;p&gt;It turns touches into blocks. Touches on the same issue that sit close together become one work session. The block starts at your first touch and runs to your last one, and it never crosses midnight or runs into the future. Each block carries a confidence level for &lt;em&gt;where it sits on the clock&lt;/em&gt;: a session with a status change at the start and a comment at the end is "high", a single comment at 3 pm is "low" and you should treat the start time as a guess.&lt;/p&gt;

&lt;p&gt;It refuses to invent time for bulk actions. If you moved six tickets to Done in ten seconds on Friday afternoon, that is not six work sessions. Those land in the sidebar as candidates with no duration. You can give one a duration if it was real work, or leave it. A ticket that was swept &lt;em&gt;and&lt;/em&gt; had real activity that day keeps a single card.&lt;/p&gt;

&lt;p&gt;It shows them on the calendar before anything is written. Suggestions appear as ghost blocks on the week grid, next to the worklogs you already have. You can drag one to another hour, resize it, reassign it to a different issue, or dismiss it. The sidebar and the calendar stay in sync, so what you see is what will be logged.&lt;/p&gt;

&lt;p&gt;It keeps the day from overflowing. A capacity bar at the top shows logged hours plus proposed hours against your daily target. If suggestions sum past your target, the app trims them rather than letting Wednesday become a fourteen-hour day. The gap to your target stays visible as a gap, instead of being quietly absorbed into the last block.&lt;/p&gt;

&lt;p&gt;Then one click. "Add all" creates a draft worklog from every timed block (candidates without a duration are skipped, on purpose). Push sends them to Jira as ordinary worklogs under your account, each dated to the day the work happened, so your Friday push doesn't look like you did the entire week on Friday.&lt;/p&gt;

&lt;p&gt;Three days of gaps is a few minutes of looking at the ghosts and nudging the ones that are wrong. Mostly you're confirming durations, which is the one thing the data can't tell you anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The better fix is upstream
&lt;/h2&gt;

&lt;p&gt;Everything above is damage control. The cheap fix is to not have the gap in the first place, and a timer in the menu bar that writes a worklog with the real start time when you stop it does most of that. Idle detection (macOS and Windows) catches the "left the timer running through lunch" case. Recent Work is for the days when you forgot to start it at all, which, for me, is still about one day in ten.&lt;/p&gt;

&lt;p&gt;Planim Time runs on macOS, Windows and Linux. It's on the &lt;a href="https://marketplace.atlassian.com/apps/1927150482" rel="noopener noreferrer"&gt;Atlassian Marketplace&lt;/a&gt; and at &lt;a href="https://time.planim.app/jira" rel="noopener noreferrer"&gt;time.planim.app&lt;/a&gt;. If you've got a reconstruction trick that works better than reading the issue history, I'd like to hear it in the comments; the sidebar started as exactly that kind of trick.&lt;/p&gt;

</description>
      <category>jira</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Jira can't move a worklog to another issue, so we put worklogs on a calendar</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Mon, 17 Aug 2026 11:00:00 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/jira-cant-move-a-worklog-to-another-issue-so-we-put-worklogs-on-a-calendar-479d</link>
      <guid>https://dev.to/jorrygo_dev/jira-cant-move-a-worklog-to-another-issue-so-we-put-worklogs-on-a-calendar-479d</guid>
      <description>&lt;p&gt;Last Friday I found 3h 20m logged on &lt;code&gt;PROJ-1417&lt;/code&gt;, a ticket I'd touched for about five minutes. The actual work went into &lt;code&gt;PROJ-1423&lt;/code&gt;, which showed zero. No problem, I thought, I'll move the worklog.&lt;/p&gt;

&lt;p&gt;Turns out you can't. Jira has no "move worklog to another issue". There's no button in the UI, and the REST API doesn't have the operation either. I build a time tracker for Jira for a living, and this one still gets a "wait, seriously?" from almost everyone I tell.&lt;/p&gt;

&lt;p&gt;What Jira expects you to do instead: open the entry, write down the duration, the start time and the comment somewhere, delete it, open the correct ticket, and type everything back in through the Log work dialog. If you get distracted halfway through, that's it. Worklogs have no undo and no recycle bin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why there's no move button
&lt;/h2&gt;

&lt;p&gt;A worklog is a tiny record: seconds spent, start timestamp, comment, author. Notice what's missing: an issue field. The issue is the &lt;em&gt;address&lt;/em&gt; of the worklog (it's literally in the API path, &lt;code&gt;/rest/api/3/issue/{key}/worklog/{id}&lt;/code&gt;), not one of its properties. There's nothing to edit. Any "move", in any tool, can only be a copy created on the destination plus a delete at the source. Jira simply doesn't offer to run the pair for you.&lt;/p&gt;

&lt;p&gt;Knowing this doesn't make the manual version less annoying, but at least it stops looking like an oversight. It's structural.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrong-ticket time comes in batches
&lt;/h2&gt;

&lt;p&gt;Something we learned from our own support inbox: misfiled worklogs almost never arrive one at a time. There's always a systematic cause. The timer kept running on the old ticket after a task switch, so the whole afternoon landed wrong. The hours went to the story when the sub-task was the actual work. Or two client projects have near-identical keys and autocomplete guessed wrong all week.&lt;/p&gt;

&lt;p&gt;Each of those gives you five or ten bad entries in one go, and the delete-and-retype ritual is per entry. Realistically, nobody spends their Friday on that. The entries stay where they fell, the project totals are off, and whatever reads those totals (reports, invoices) is off too. Jira makes it worse by having no screen where you'd even notice: there's no view that &lt;a href="https://time.planim.app/blog/see-all-your-jira-worklogs" rel="noopener noreferrer"&gt;shows all your worklogs in one place&lt;/a&gt;, so a misfiled entry stays invisible unless you go digging through the Work log tab of a ticket you don't suspect.&lt;/p&gt;

&lt;h2&gt;
  
  
  So we put the week on a calendar
&lt;/h2&gt;

&lt;p&gt;Our tracker is called Planim Time, a &lt;a href="https://time.planim.app/jira" rel="noopener noreferrer"&gt;desktop time tracker for Jira&lt;/a&gt; that lives in the menu bar. The part that matters for this post is the calendar: every worklog is a block on a week grid, sitting at its start time, with the issue key on it. Height is duration.&lt;/p&gt;

&lt;p&gt;Once your week looks like that, misfiled time stops being a search problem. The bad entry is just... sitting there. Wrong key, plain sight.&lt;/p&gt;

&lt;p&gt;And each way an entry can be wrong turns into a direct manipulation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wrong issue: click the block, search for the right ticket, confirm. Duration, start time and comment travel with it&lt;/li&gt;
&lt;li&gt;wrong day or time: drag it where it belongs&lt;/li&gt;
&lt;li&gt;ran too long because you forgot to stop the timer: grab the edge and shrink it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything writes back to Jira as ordinary worklogs, through your own account. Sync is two-way, so entries you created in Jira's own dialog (or in any other tool) show up on the calendar too and can be fixed the same way. Wrong day and wrong duration have a few extra wrinkles of their own; we wrote those up separately in a guide to &lt;a href="https://time.planim.app/blog/how-to-change-logged-hours-in-jira" rel="noopener noreferrer"&gt;changing logged hours in Jira&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To be clear about what happens underneath: Jira still has no move, so on "confirm" the app creates the copy on the new issue first and deletes the original after. Worst case, a crash between the two leaves you a duplicate for a moment. The manual ritual's worst case is the entry existing only in your memory.&lt;/p&gt;

&lt;p&gt;The limits are Jira's rules, not ours: you can only move worklogs you authored (the API has no way to write a worklog as someone else), and you need worklog permissions on both projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it by hand anyway
&lt;/h2&gt;

&lt;p&gt;Maybe a stray worklog happens to you twice a year and installing anything is overkill. Fair. Then at least flip the order of the official procedure: log the time on the &lt;em&gt;correct&lt;/em&gt; issue first, with the original date in the Date started field, check it saved, and only then delete the entry from the wrong ticket. Create first, delete second. Nothing ever exists only in your head. The full walkthrough, permission names and estimate prompts included, is in our post on &lt;a href="https://time.planim.app/blog/move-jira-worklog-to-another-issue" rel="noopener noreferrer"&gt;moving a Jira worklog to another issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The tracker itself is free to try: macOS, Windows, Linux, signs in with your Atlassian account. Tracking and two-way sync are on the free tier; the calendar is a Pro feature with a 14-day trial. If your Fridays regularly include archaeology on your own worklogs, you'll know within a week whether the calendar earns its keep.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>automation</category>
      <category>task</category>
    </item>
    <item>
      <title>Forgot to log your time in Jira again? Your activity already remembers.</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Wed, 24 Jun 2026 13:58:40 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/forgot-to-log-your-time-in-jira-again-your-activity-already-remembers-3llb</link>
      <guid>https://dev.to/jorrygo_dev/forgot-to-log-your-time-in-jira-again-your-activity-already-remembers-3llb</guid>
      <description>&lt;p&gt;There is a specific kind of dread that arrives on Friday afternoon. The timesheet is due, you open Jira to log your week, and Tuesday is blank. Wednesday is mostly blank. Your memory of the last five days is three meetings, a bug that ate a morning, and a general sense of having been busy. So you start guessing. Everything becomes "probably two hours," you round until the day adds up to something believable, and you quietly hope nobody ever audits it.&lt;/p&gt;

&lt;p&gt;If that feels familiar, you are not bad at your job. You are just doing a job that involves jumping between five tickets, three Slack threads, and the occasional production fire, none of which leave you in a calm state to remember a timer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timers assume a workday you do not have
&lt;/h2&gt;

&lt;p&gt;This is the part nobody admits about time tracking. A timer assumes you will press start the moment you begin a task and stop the moment you finish. On a quiet day, fine. On a real day, you forget within the hour, the timer is still running on a ticket you closed before lunch, and by Friday it has captured maybe a third of your actual week. The tool that was supposed to rescue you from guessing has left you guessing anyway, just with extra steps.&lt;/p&gt;

&lt;p&gt;So most people give up on the timer and go back to reconstructing the week from memory. Memory is the worst possible source. It is exactly the thing the whole exercise was meant to avoid.&lt;/p&gt;

&lt;h2&gt;
  
  
  You do not need to remember. Jira already wrote it down.
&lt;/h2&gt;

&lt;p&gt;Here is the reframe that fixed this for me. You do not actually have to remember your week, because Jira has been keeping notes the entire time. Every ticket that got assigned to you, every issue you commented on, every status you dragged from In Progress to In Review, all of it is recorded with a timestamp. Put together, that is a near-complete log of what you worked on. It is just scattered across issues instead of sitting in a tidy timesheet.&lt;/p&gt;

&lt;p&gt;So the honest way to fill a blank week is not to squeeze your memory until something comes out. It is to read your own activity back and turn it into worklogs. Your activity tells you which tickets you touched and roughly when. You supply the one thing it can never know, which is how long each one actually took.&lt;/p&gt;

&lt;p&gt;You can do this entirely by hand: open each issue you touched, check its history and your comments, drop it onto the right day. If you like that sort of thing, I wrote a longer, more technical walkthrough of the manual version &lt;a href="https://time.planim.app/blog/reconstruct-jira-worklogs-from-activity" rel="noopener noreferrer"&gt;over here&lt;/a&gt;. It works. It is also tedious enough that most people try it once and quietly go back to guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The version that does the digging for you
&lt;/h2&gt;

&lt;p&gt;Quick disclosure before this next part: I am on the team behind Planim Time, so take the recommendation with that in mind. We built it because we kept losing our own Tuesdays.&lt;/p&gt;

&lt;p&gt;Planim Time is a small menu-bar app for tracking Jira time. The feature that matters for the Friday scramble is Recent Work. It reads your recent Jira activity, the issues you were assigned, commented on, created, or moved, and turns them into suggested worklogs. They appear as blocks on a calendar, sitting roughly where you touched them during the day. You drag to set the length, a capacity bar keeps a dozen small touches from quietly adding up to an eleven-hour day, and once it looks right you push the whole week to Jira in one click.&lt;/p&gt;

&lt;p&gt;It does not pretend to know your durations, because nothing can. Jira records when you touched a ticket, never for how long. What the app removes is the archaeology. Instead of rebuilding Tuesday from a blank page, you start from a draft that is already mostly right and just correct the hours.&lt;/p&gt;

&lt;p&gt;A few things we were deliberate about, since you have probably been burned by a tracker before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It lives in your menu bar, not a browser tab you forget to open.&lt;/li&gt;
&lt;li&gt;It works offline and writes worklogs straight onto the Jira issue, so no separate cloud is holding your hours hostage.&lt;/li&gt;
&lt;li&gt;No screenshots, no keystroke monitoring, none of that.&lt;/li&gt;
&lt;li&gt;The free tier is a complete tracker, not a countdown to a paywall.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stop guessing your timesheet
&lt;/h2&gt;

&lt;p&gt;If the end-of-week scramble is a recurring feature of your life, it is worth ten minutes to try a different approach. You can grab Planim Time at &lt;a href="https://time.planim.app/jira?ref=devto_forgot_to_log" rel="noopener noreferrer"&gt;time.planim.app&lt;/a&gt;, point it at your Jira, and let it show you what you already did this week.&lt;/p&gt;

&lt;p&gt;And if you would rather keep the whole thing manual, the &lt;a href="https://time.planim.app/blog/reconstruct-jira-worklogs-from-activity?ref=devto_forgot_to_log" rel="noopener noreferrer"&gt;longer guide&lt;/a&gt; has the queries to do it yourself. Either way, the takeaway is the same: stop interrogating your memory on Friday. Your activity already knows the answer, you just have to read it back.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>productivity</category>
      <category>softwaredevelopment</category>
      <category>tooling</category>
    </item>
    <item>
      <title>The best Jira time trackers for engineering teams in 2026</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Tue, 12 May 2026 19:48:53 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/the-best-jira-time-trackers-for-engineering-teams-in-2026-n94</link>
      <guid>https://dev.to/jorrygo_dev/the-best-jira-time-trackers-for-engineering-teams-in-2026-n94</guid>
      <description>&lt;p&gt;Prices in this category move. Re-verify before quoting any number internally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criteria
&lt;/h2&gt;

&lt;p&gt;What I'm not weighing, and what I am.&lt;/p&gt;

&lt;p&gt;I don't weigh &lt;strong&gt;mobile apps&lt;/strong&gt; highly for this audience. Phone-first tracking matters for field service, agencies billing from cafés, consultants on the move; Clockify below earns its spot partly on that. For an engineer in a sprint at a two-monitor desk, it's almost never the deciding factor.&lt;/p&gt;

&lt;p&gt;I don't weigh &lt;strong&gt;AI weekly summaries&lt;/strong&gt; highly. They're the headline feature on every tracker's pricing page right now, and the engineers I talk to read them once and turn them off. The hour you logged on Tuesday is whatever the worklog says.&lt;/p&gt;

&lt;p&gt;I do weigh:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Survival of a Jira outage.&lt;/strong&gt; Atlassian's &lt;a href="https://status.atlassian.com/" rel="noopener noreferrer"&gt;status page&lt;/a&gt; shows a couple of degraded periods most months. If your tracker goes blank during them, that's a failure mode you keep paying for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where the API token lives.&lt;/strong&gt; OS keychain means it shares a vault with the saved passwords in your browser. SaaS account means you inherit that vendor's threat model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Round-trip sync, not just push.&lt;/strong&gt; When a teammate fixes a worklog directly in Jira, your tracker either picks that change up or silently overwrites it on the next push. Most overwrite. I've written about &lt;a href="https://time.planim.app/blog/two-way-jira-worklog-sync" rel="noopener noreferrer"&gt;why&lt;/a&gt; separately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether you need a Jira admin to install it.&lt;/strong&gt; Marketplace apps need one; desktop apps don't. For a single engineer trying to evaluate something, the difference between five minutes and three weeks of internal tickets is decisive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free tier that isn't a teaser.&lt;/strong&gt; An indefinite free tier that does the job, or a long trial. "Fourteen days then it nags you" doesn't count.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tempo Timesheets: finance and PMO that doesn't sleep
&lt;/h2&gt;

&lt;p&gt;If someone at your company has "Director" in their title and reads timesheets monthly, &lt;a href="https://marketplace.atlassian.com/apps/6572/tempo-timesheets-time-tracking-report" rel="noopener noreferrer"&gt;Tempo Timesheets&lt;/a&gt; is probably already the answer. It's been the #1 time-management product in the Atlassian ecosystem since 2009, and the feature stack shows it: approvals, capacity planning, budgets, invoice-ready timesheets, audit-friendly reports. None of which is easy to retrofit.&lt;/p&gt;

&lt;p&gt;Tempo Timesheets on Cloud starts at &lt;strong&gt;$10 per user per month&lt;/strong&gt; and scales per Jira user from there. List prices have crept up roughly once a year, so before quoting any number, re-check the &lt;a href="https://marketplace.atlassian.com/apps/6572/tempo-timesheets-time-tracking-report?hosting=cloud&amp;amp;tab=pricing" rel="noopener noreferrer"&gt;Marketplace listing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Trade-off: you're paying per Jira user for a workflow that mostly serves the finance side of the room. A four-engineer squad paying $40/month so two of them log hours and a non-engineer runs an approval flow stops liking enterprise features pretty quickly. That's the gap I see most when teams leave Tempo. Tempo isn't bad; the engineers were paying for a finance product they didn't need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick Tempo if&lt;/strong&gt; approvals, capacity planning, or client invoicing inside the tracker are load-bearing for your team. We have a &lt;a href="https://time.planim.app/jira/vs/tempo?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=best-jira-trackers" rel="noopener noreferrer"&gt;longer head-to-head&lt;/a&gt; if you want the row-by-row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clockwork: free for ten, Jira-internal for everyone else
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://marketplace.atlassian.com/apps/1219213/clockwork-pro-for-jira-time-tracking-and-timesheets" rel="noopener noreferrer"&gt;Clockwork&lt;/a&gt; (HeroCoders) is the lightest option in the Marketplace. Two SKUs: &lt;strong&gt;Pro&lt;/strong&gt;, free for ≤10 Cloud users and paid above, and &lt;strong&gt;Lite&lt;/strong&gt;, which HeroCoders split off in January 2025 for teams that want something smaller and cheaper than Pro.&lt;/p&gt;

&lt;p&gt;Under ten Cloud users, this is hard to beat: a real timesheet UI, embedded inside Jira, at zero cost. The catch is the same as Tempo's at smaller scale. The timer is a Jira widget that lives or dies with the page. No offline mode, no menu-bar surface.&lt;/p&gt;

&lt;p&gt;Watch the upgrade cliff. Above ten users the per-Jira-user model bills every Jira user, including ones who'll never log an hour. A 50-person org where 12 actively track ends up paying for 38 people who don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick Clockwork if&lt;/strong&gt; you want a free timesheet that lives inside the Jira tab itself (no separate app to install) and your team is ≤10 users on Cloud, or your policy is "everything stays in Jira's database". &lt;a href="https://time.planim.app/jira/vs/clockwork?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=best-jira-trackers" rel="noopener noreferrer"&gt;Head-to-head&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everhour: when the timer also has to be the invoice
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://everhour.com/pricing" rel="noopener noreferrer"&gt;Everhour&lt;/a&gt; is the right pick when "track an hour" and "bill a client" are two faces of the same job. Pricing as of April 2026 is &lt;strong&gt;$8.50 per user per month billed annually&lt;/strong&gt;, with a 15% premium for monthly billing and a five-seat minimum on the paid plan. The free tier covers up to five users.&lt;/p&gt;

&lt;p&gt;The Jira integration is a Marketplace add-on that replaces Jira's native time-tracking UI with Everhour's controls (with an optional browser extension for list and board views). Projects sync into Everhour's web dashboard, and that's where the centre of gravity is: budgets, billable rates, custom reports, QuickBooks/Xero/FreshBooks integrations, capacity planning. If a single hour has to end up on a client invoice through the same tool that started the timer, Everhour has built around that opinion for years.&lt;/p&gt;

&lt;p&gt;The engineering trade-off: you pay for an invoicing engine even if you never invoice, your token lives in Everhour's cloud (not your OS keychain), and logging forty minutes against &lt;code&gt;PROJ-1247&lt;/code&gt; takes more clicks than it should.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick Everhour if&lt;/strong&gt; you bill clients hourly and the tracker also has to be the billing system. &lt;a href="https://time.planim.app/jira/vs/everhour?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=best-jira-trackers" rel="noopener noreferrer"&gt;Head-to-head&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clockify: generalist with a phone in its pocket
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://clockify.me/pricing" rel="noopener noreferrer"&gt;Clockify&lt;/a&gt; is the breadth play. The free tier covers &lt;strong&gt;up to five users&lt;/strong&gt; and includes a working timer, timesheets, idle detection, calendar view, mobile + desktop apps, Pomodoro, kiosk mode, and 80+ integrations. Paid tiers run &lt;strong&gt;Basic $4.99 / Standard $6.99 / Pro $9.99 / Enterprise $14.99&lt;/strong&gt; per user per month, with ~20% off on annual. The Jira integration pushes worklogs through the browser extension.&lt;/p&gt;

&lt;p&gt;If your tracking life really does span Jira, GitHub, Trello, Asana, Basecamp, ClickUp plus a phone, that breadth is paid for and used. If 90% of your hours are on Jira, you're paying for surface you'll never touch. The Jira integration is also one-way (extension → Jira), not round-trip, so the moment a teammate fixes a worklog in Jira, the tracker goes quietly out of sync.&lt;/p&gt;

&lt;p&gt;There's also the second-cloud problem: time entries live primarily inside Clockify and get pushed to Jira on demand. For an engineering team that already pays Atlassian for Jira, a parallel store of the same data is something a security review will eventually flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick Clockify if&lt;/strong&gt; you track across many tools or you need a mobile app as a first-class surface. &lt;a href="https://time.planim.app/jira/vs/clockify?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=best-jira-trackers" rel="noopener noreferrer"&gt;Head-to-head&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planim Time:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://time.planim.app/jira?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=best-jira-trackers" rel="noopener noreferrer"&gt;Planim Time for Jira&lt;/a&gt; It's a native desktop app, a menu-bar or system-tray icon on macOS, Windows, and Linux. It talks to your Jira instance through your personal API token, stores the token in the OS keychain, and runs entirely offline. Worklogs sync both ways: edit one in Jira's UI and it shows up in the tracker within about a minute, no webhook server in the middle. Issue selection is JQL-driven, so whatever filter you already use in Jira, the tracker can use too.&lt;/p&gt;

&lt;p&gt;On a normal workday the surface most people live in is the calendar (Pro). It's a multi-week grid where worklogs are draggable blocks you can resize to extend or shorten. Closer to how Google Calendar feels than a row-by-row spreadsheet. Team stats roll up every member who logged time to the workspace's Jira issues, not only the paid seats, so the boards include everyone's hours without you having to buy licences for the people who won't use Pro. Both come with a 14-day trial on first launch, no card.&lt;/p&gt;

&lt;p&gt;Where Planim Time loses to the four above: no approval workflows, no capacity planning, no budgets, no full invoicing (CSV export is as close as we get), no mobile app, no kiosk mode. We track worked hours and push them to Jira; everything else is somebody else's job. If you need any of that, one of the four above will earn its seat. We wrote separately about &lt;a href="https://time.planim.app/blog/why-native-desktop-time-trackers" rel="noopener noreferrer"&gt;why we shipped a desktop binary instead of a Marketplace plugin&lt;/a&gt; if you want the architectural side of that trade-off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick Planim Time if&lt;/strong&gt; Jira is your source of truth, you want a timer that survives both Jira outages and laptop tab-evictions, and you'd rather your API token live in the OS keychain than in another vendor's cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Finance reads your timesheets monthly?&lt;/strong&gt; Tempo Timesheets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want the timer to live inside the Jira tab itself, ≤10 users on Cloud?&lt;/strong&gt; Clockwork (Pro Free or Lite).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You bill clients hourly through the same tool that runs the timer?&lt;/strong&gt; Everhour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your tracking life spans many tools and you need a real mobile app?&lt;/strong&gt; Clockify (free up to five users; $4.99–$14.99 per seat above that).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jira is the source of truth and you want a timer that survives Atlassian's bad afternoons?&lt;/strong&gt; Planim Time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five buckets, five different winners. There isn't a single "best Jira time tracker", and the mistake every other roundup makes is pretending there is one.&lt;/p&gt;

&lt;p&gt;If you've got Jira and an hour you need to remember on Tuesday, &lt;a href="https://time.planim.app/jira/download?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=best-jira-trackers" rel="noopener noreferrer"&gt;download Planim Time&lt;/a&gt; and point it at your real instance for a sprint. If it doesn't earn its keep in the first week, one of the four above will, and now you know which one.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>agile</category>
      <category>jira</category>
      <category>devtoo</category>
    </item>
    <item>
      <title>Building a Jira Time Tracker with Tauri: How I Stored API Tokens Securely</title>
      <dc:creator>Vladimir Letiagin</dc:creator>
      <pubDate>Wed, 29 Apr 2026 18:49:08 +0000</pubDate>
      <link>https://dev.to/jorrygo_dev/building-a-jira-time-tracker-with-tauri-how-i-stored-api-tokens-securely-46aj</link>
      <guid>https://dev.to/jorrygo_dev/building-a-jira-time-tracker-with-tauri-how-i-stored-api-tokens-securely-46aj</guid>
      <description>&lt;p&gt;I've been building a small menu-bar app for tracking time on Jira issues. Mostly it's boring CRUD: a timer, a list of issues, push worklogs back to Jira. Except for one thing I had to figure out on day one. The user logs in with a Jira API token, and that token has to live somewhere on their machine.&lt;/p&gt;

&lt;p&gt;Where, exactly?&lt;/p&gt;

&lt;p&gt;Every Tauri tutorial skips this part. Below is what I tried, what broke, and what I shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  My first attempt (don't do this)
&lt;/h2&gt;

&lt;p&gt;I already had a local SQLite database in the app for caching Jira issues, worklogs, and user prefs. So my first move was the obvious one — stick the API token in a &lt;code&gt;settings&lt;/code&gt; table next to everything else. One database, one place to look, simple.&lt;/p&gt;

&lt;p&gt;Don't do this.&lt;/p&gt;

&lt;p&gt;SQLite stores everything in a plain &lt;code&gt;.db&lt;/code&gt; file on disk. No encryption by default. Anyone with file access on the machine reads your token in two seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 app.db &lt;span class="s2"&gt;"SELECT value FROM settings WHERE key='api_token'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Backups, iCloud sync, Dropbox, malware scanners — all see it. Same problem with &lt;code&gt;localStorage&lt;/code&gt; in the Tauri webview, with a JSON file in the app data dir, or with convenience plugins like &lt;code&gt;tauri-plugin-store&lt;/code&gt;. Plain text on disk is plain text on disk. And if your frontend has any XSS-shaped bug, the token is right there for a script to grab.&lt;/p&gt;

&lt;p&gt;What you actually want is the OS credential store. Every desktop OS ships with one. They exist specifically to keep secrets off plain disk and away from other users on the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tauri suggests
&lt;/h2&gt;

&lt;p&gt;Tauri's official docs point you at the Stronghold plugin. It uses IOTA's Stronghold engine, encrypts everything with a password, and writes to a snapshot file.&lt;/p&gt;

&lt;p&gt;Stronghold solves a different problem — a full vault with key derivation, multiple records, the whole deal. For a single API token it's the wrong shape. It also needs the user to enter a password to unlock the vault, or you stash that password somewhere, and now you've moved the problem one level up. Where do you store the password that unlocks Stronghold?&lt;/p&gt;

&lt;p&gt;I wanted the OS to handle the trust. That means the system keychain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually used: the keyring crate
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://crates.io/crates/keyring" rel="noopener noreferrer"&gt;&lt;code&gt;keyring&lt;/code&gt;&lt;/a&gt; crate is a Rust wrapper that gives you one API on top of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: Keychain Services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: Credential Manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux&lt;/strong&gt;: Secret Service API (gnome-keyring, KWallet, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You write the code once, and on each platform it talks to the native store. Users get the same security guarantees they expect from Safari saving a password or git saving a credential.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Heads up: &lt;code&gt;keyring&lt;/code&gt; 3.x is a major bump from 2.x with breaking API changes. Half the tutorials you'll find on Google are still on the old version, so check the version in their &lt;code&gt;Cargo.toml&lt;/code&gt; before copy-pasting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;keyring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"apple-native"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"windows-native"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"sync-secret-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"crypto-rust"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those features matter. &lt;code&gt;apple-native&lt;/code&gt; and &lt;code&gt;windows-native&lt;/code&gt; use the platform APIs directly. &lt;code&gt;sync-secret-service&lt;/code&gt; is the Linux backend. &lt;code&gt;crypto-rust&lt;/code&gt; handles the D-Bus transport encryption in pure Rust so you don't drag in an OpenSSL dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole storage module
&lt;/h2&gt;

&lt;p&gt;This is the entire &lt;code&gt;secure_store.rs&lt;/code&gt; from my app. There's nothing more to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;keyring&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SERVICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"planim-time-tracker"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SERVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Keyring entry error: {e}"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
        &lt;span class="nf"&gt;.set_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to save to keyring: {e}"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SERVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Keyring entry error: {e}"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
        &lt;span class="nf"&gt;.get_password&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;keyring&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NoEntry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read from keyring: {e}"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;delete_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SERVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Keyring entry error: {e}"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
        &lt;span class="nf"&gt;.delete_credential&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;keyring&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NoEntry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(()),&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to delete from keyring: {e}"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three functions, no state, no init. The &lt;code&gt;SERVICE&lt;/code&gt; constant is what shows up as the entry name in Keychain Access on macOS or Credential Manager on Windows. Pick a stable name and don't change it later — if you do, every existing user loses their saved credentials and has to re-auth.&lt;/p&gt;

&lt;p&gt;One thing worth being explicit about: this is for &lt;strong&gt;secrets only&lt;/strong&gt;. Tokens, refresh tokens, passwords. User preferences, base URLs, UI state — that all goes in SQLite or wherever else makes sense. Keychain entries are slow-ish to read and have size limits, so don't dump everything in there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it into a Tauri command
&lt;/h2&gt;

&lt;p&gt;I never touch &lt;code&gt;secure_store&lt;/code&gt; from the frontend. The frontend asks for an action, the backend handles the secret. The Jira API token never leaves the Rust side once it's saved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[tauri::command]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;settings_save_jira_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AppState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// non-secret fields go in SQLite&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nn"&gt;secure_store&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;set_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jira_api_token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern: secrets in keyring, everything else (Jira base URL, email, preferences) in SQLite. The frontend never holds the token in JS state for longer than a single form submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-OS gotchas you'll only find out about in production
&lt;/h2&gt;

&lt;p&gt;This is the part nobody mentions until it bites you.&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;

&lt;p&gt;The first time another binary tries to read a Keychain item, the user gets an "allow access" prompt — three buttons: Always Allow, Allow, Deny. No password input. A lot of devs avoid Keychain because they think it nags users for their macOS password every time. It doesn't, as long as you put items in the &lt;em&gt;login&lt;/em&gt; keychain (which is what &lt;code&gt;keyring&lt;/code&gt; does by default). The login keychain is unlocked automatically when the user signs into the Mac, and stays unlocked.&lt;/p&gt;

&lt;p&gt;The catch: Keychain identifies your app by its code signature. If the signature changes, Keychain treats it as a new app and prompts again. In development, every &lt;code&gt;cargo build&lt;/code&gt; produces a new ad-hoc signature, so you see the "allow access" prompt after every rebuild. Annoying, but it goes away in production as long as you sign with a stable Developer ID. The signature stays consistent across app updates, and Always Allow sticks.&lt;/p&gt;

&lt;p&gt;If you ship unsigned, you have a bigger problem than Keychain prompts: macOS 15+ makes it deeply painful for users to launch your app. The right-click → Open trick is gone in Sequoia — users now have to dig into System Settings → Privacy &amp;amp; Security and explicitly allow the binary every time. Sign your app. The Apple Developer Program is $99/year and worth it for any real desktop app.&lt;/p&gt;

&lt;p&gt;Notarization, by the way, doesn't affect Keychain at all. That's only for Gatekeeper letting users open downloaded &lt;code&gt;.dmg&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;There's also a quirk where if you change your bundle identifier or developer team, Keychain treats the new build as a different app. Pre-existing entries from old builds stay in the user's Keychain forever unless you clean them up. On uninstall, run &lt;code&gt;delete_credential&lt;/code&gt; for every key your app stores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;Credential Manager is per-user. No prompt at all, ever. Items are protected by the user's Windows login. Easy mode.&lt;/p&gt;

&lt;p&gt;The catch: if a user logs into Windows with a different account, they don't see those credentials. Which is what you want, but it occasionally confuses users with multiple Windows accounts on the same machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux
&lt;/h3&gt;

&lt;p&gt;This is where it gets messy. The Secret Service API needs an active D-Bus session and a running provider, usually &lt;code&gt;gnome-keyring-daemon&lt;/code&gt; or &lt;code&gt;kwalletmanager&lt;/code&gt;. On a clean GNOME or KDE desktop, no problem. On a minimal i3 or sway setup, or in a CI container, there's no daemon running, and &lt;code&gt;keyring&lt;/code&gt; returns an error.&lt;/p&gt;

&lt;p&gt;I handle that case explicitly. If the keyring lookup fails on Linux, I show the user a message asking them to install gnome-keyring. Most desktop users won't hit it, but power users on bare WMs will, and silently failing is the worst possible UX.&lt;/p&gt;

&lt;p&gt;For headless or server use, the &lt;code&gt;keyring&lt;/code&gt; crate has a &lt;code&gt;mock&lt;/code&gt; feature for tests, and there are file-based fallbacks. I didn't ship those because the app is desktop-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd skip if I started over
&lt;/h2&gt;

&lt;p&gt;A few things I tried first and threw away:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encrypting a JSON file with a hardcoded key in the binary.&lt;/strong&gt; Anyone with a hex editor pulls that key out. Pointless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stronghold.&lt;/strong&gt; Not because it's bad — it's a serious piece of crypto engineering. But for a single API token, it added a password-unlock flow my users didn't want. They expected the OS to remember them, the way every other app does.&lt;/p&gt;

&lt;h2&gt;
  
  
  See it in production
&lt;/h2&gt;

&lt;p&gt;The app I built on top of all this is &lt;a href="https://time.planim.app/jira" rel="noopener noreferrer"&gt;time.planim.app&lt;/a&gt;. One-click timers on Jira issues, automatic worklog sync, calendar view, custom JQL filters — all without leaving the menu bar.&lt;/p&gt;

&lt;p&gt;If you went the Stronghold route, or built some hybrid setup, or solved this differently on Linux — drop your approach in the comments. Curious what trade-offs other people made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; — if you're shopping around for a Jira time tracker rather than building one, I followed up with &lt;a href="https://time.planim.app/blog/where-jira-time-trackers-store-your-api-token" rel="noopener noreferrer"&gt;a comparison of how five different&lt;br&gt;
  products handle this same problem&lt;/a&gt; — Tempo, Clockwork, Everhour,&lt;br&gt;
  Clockify, and Planim Time. Same threat-model lens, applied to the rest of the category, with the actual auth model each one uses.&lt;/p&gt;

</description>
      <category>tauri</category>
      <category>rust</category>
      <category>security</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
