OtiumWork manual β€” Employee edition

A single-page reference of every help topic relevant to employees. This page is always live β€” when help docs change, this page updates automatically.

πŸ’‘ To save as PDF: use your browser's print (Ctrl+P / Cmd+P) and choose "Save as PDF". Sidebar & nav are hidden in print mode.

Contents (26 topics)

  1. Getting started with OtiumWork β€” The 5-minute overview β€” what OtiumWork does, what's captured, how to get tracking.
  2. Ask Oti β€” the in-app copilot β€” The in-app copilot β€” ask "how do I…" in plain English and get the answer plus the help page it came from.
  3. Desktop client β€” Install the Windows tray client, configure it, use the tray menu. Covers v1.6.5 β€” soft-idle detection, magic-link install, in-app reconfigure, lock detection.
  4. My Day β€” The single page every employee lands on β€” project tiles, tap to focus, everything captured rolls up under the active tile.
  5. My Tasks β€” One inbox for everything assigned to you β€” personal to-dos, project tasks, IT tickets, marketing requests.
  6. My activity β€” Audit drawer β€” raw events, corrections, fine-grained project linking. My Day is now the daily surface.
  7. Projects and clients β€” Create projects, assign people, track budgets, link to clients.
  8. Vacation (PTO) β€” Request vacation, track your balance, get manager approval β€” and feed approved time-off into capacity planning.
  9. AI-drafted weekly timesheet β€” Click ✨ on any week in /timesheets β€” Claude writes a professional weekly summary from your captured events.
  10. Partners / resellers (admin) β€” Manage external resellers β€” partners, contacts, deal intents, Q&A, training modules.
  11. Partner portal β€” Reseller-facing help β€” sign in, submit deals, ask technical questions, take certifications.
  12. Categories, work types, and departments β€” Editable per-company taxonomy used to classify every event β€” categories, work types, departments.
  13. IT helpdesk β€” IT helpdesk module β€” anyone submits, IT triages with priority/due-dates, screenshot uploads, daily morning digest.
  14. Tags β€” Free-form labels that supplement the fixed category enum β€” for cross-cutting classifications.
  15. Competitor tracking (your company's) β€” Track competitor product releases, subscribe to what you care about, feed a dev roadmap.
  16. Feedback + OtiumWork roadmap β€” How feature requests flow into the product roadmap, how to vote on what ships next, and how the bug pipeline turns into Claude-Code-fixed issues reviewed on staging before release.
  17. Account recovery β€” what to do when you can't log in β€” How to get back into your account if you've lost your password, 2FA device, backup codes, or even your email.
  18. 2FA β€” I lost my device β€” You lost your phone (or your authenticator app got wiped). Here's how to get back in.
  19. I think my account is compromised β€” You suspect your account is compromised. Here's what to do, in order.
  20. Bulk Events
  21. Connect your Outlook calendar
  22. Customer Tracker
  23. Engineering AI
  24. Manager Event Edit
  25. Profitability Ai Estimator
  26. Teams

1. Getting started with OtiumWork

Last updated 2026-08-07 Β· open as standalone page

Getting started with OtiumWork

OtiumWork is an AI-native operations platform for modern teams β€” consulting firms, agencies, in-house ops teams, multi-entity holding companies, and SaaS finance teams. It replaces manual time tracking (Clockify, Harvest, etc.) by capturing your foreground window activity automatically and using AI to classify it into billable categories.

What gets captured

  • Active application name (e.g. chrome.exe, excel.exe)
  • Active window title (the text in the title bar)
  • Duration β€” in 5-minute flush buckets
  • Your idle state β€” so we don't log anything while you're away

Explicitly not captured: screenshots, keystrokes, clipboard, file contents, camera, microphone, URLs beyond what's in the title bar, emails, chat contents.

The two sides of OtiumWork

  1. Desktop client β€” a small Windows tray app that captures the above and sends it to your company's OtiumWork server every 5 minutes.
  2. Web app β€” where you review + correct your events, see reports, track projects, and manage your team.

First steps

  1. Install the desktop client. Takes ~2 minutes.
  2. Open My Day and tap a project tile to start a focus session β€” everything captured rolls up under that project until you switch tiles.
  3. Visit My events in the sidebar at the end of your first day β€” review what was captured, link events to projects, correct any misclassifications.
  4. Managers + admins: set up Projects and clients so billable work gets attributed.

When you're stuck

  • Ask Oti β€” the copilot at the top of the sidebar. Ask "how do I …" in plain English; it answers from these help docs and links the page it used.
  • Help & FAQ β€” every topic, browsable.
  • Your manual at /help/manual β€” everything that applies to your role and module access on one printable page. Worth reading end to end on day one.
  • Tooltips β€” hover any sidebar item for a one-line description of what's behind it.

Who sees what

  • Employees: your own captured events (no one else's), your own timesheet, projects you're on, your own Clockify history.
  • Managers: everything above + events/reports for your direct reports + the team-aggregate view.
  • Admins: everything company-wide. Plus settings, billing, AI budget, client management.

Privacy + transparency

Every event we captured on your machine shows up on My events, editable. You can pause tracking at any time from the system tray icon (right-click β†’ Pause 30 min / 1 hour / 4 hours). Tracking never resumes on its own β€” you decide.

More on privacy: see Privacy & data.

2. Ask Oti β€” the in-app copilot

Last updated 2026-08-07 Β· open as standalone page

Ask Oti β€” the in-app copilot

Ask Oti is the first item in the sidebar (🦦). It's a chat window that answers two kinds of questions:

  1. How do I use this? β€” "how do I request vacation", "where do I edit a work type", "how does approval work". Oti searches the same help docs you can read at /help and answers with the relevant passage plus a link to the full page, so you can always check the source.
  2. What's going on with my work? β€” "what's on my plate", "where did my time go last week", "how much PTO do I have left". These read your live data.

What it can see

Oti runs on skills β€” small tools it's allowed to call. Which skills exist for you depends on your role and module access, exactly like the rest of the app:

  • Everyone gets the help search, their own tasks, their own tracked hours, their own PTO balance.
  • Managers and admins additionally get rollups over the people they're responsible for.
  • Sales-team members get the Salesforce / pipeline skills.

Ask "what can you do?" and Oti lists every skill you currently have. If a skill isn't listed, it isn't available to you β€” asking again won't unlock it, and Oti can't read another employee's data on your behalf.

Tips

  • Ask in plain English; you don't need to know the feature's name.
  • If your question is ambiguous, Oti asks a short clarifying question instead of guessing.
  • Conversations are saved per person in the sidebar β€” reopen an old one or delete it any time.
  • Oti never invents product behaviour. If the help docs don't cover something, it says so β€” that's your cue to file feedback so the doc gets written.

The other ways to get help

  • Help & FAQ β€” every topic as a browsable card grid.
  • Your manual at /help/manual β€” every topic that applies to your role and module access, stitched into one printable page. Good for a new hire's first day.
  • Hover any sidebar item β€” each one has a one-line tooltip explaining what lives there.
  • Admins get a setup checklist on the admin dashboard that tracks first-run configuration.

3. Desktop client

Last updated 2026-04-29 Β· open as standalone page

Desktop client

The desktop client is the Windows app that sits in your system tray and captures foreground window activity. Windows only (Mac support is on the roadmap).

Installing

Three paths β€” pick whichever applies.

Magic-link install (easiest, v1.6.3+)

The OtiumWork install email contains a link starting with otiumwork://install?token=...&email=.... After you've run the .exe once, clicking that link in any browser pre-fills your credentials and launches the client β€” no typing the company code at all.

Direct download (most users)

  1. You received an email from OtiumWork with a download link. Click it to save otiumwork-client.exe.
  2. Double-click to run. If Windows says "Windows protected your PC", click More info β†’ Run anyway. (Code-signing cert landing soon will eliminate this warning.)
  3. A small dialog opens asking for your email and company code β€” both were in the email you received. Paste them in, click Start. - Important: paste, don't retype β€” the company code is a 22-character random string and typos fail silently. The dialog now verifies your credentials with the server before saving (v1.6.3+); if anything's wrong it stays open with an inline error.
  4. A tray icon appears in the bottom-right of the taskbar. If you don't see it, click the ^ arrow β€” Windows sometimes hides new tray icons in the overflow.

Microsoft Intune (IT-managed deployments)

If your company uses Intune, the client is pushed to you automatically β€” you don't download anything. Once installed, log off and back on and the tray icon appears.

Using the tray menu

Right-click the tray icon for options:

  • Tracking / ⚠ Authentication failed β€” top-line status. When auth is failing (mistyped token, etc.), this turns into a clickable item that opens Reconfigure credentials directly, and the icon flips to a red "!" badge.
  • Review recent events β€” opens a small window with your last 15 events; edit categories / work types / projects / notes inline.
  • Open OtiumWork (web) β€” jumps to your My events page in the browser.
  • Pause 30 min / 1 hour / 4 hours β€” stops tracking for that window. Tracking auto-resumes after the timer expires.
  • Resume tracking (appears when paused).
  • Flush pending events now β€” immediately sends buffered events instead of waiting for the 5-minute flush cycle.
  • Reconfigure credentials… (v1.6.2+) β€” reopens the setup dialog with your current values pre-filled. Use this if you typed the wrong email or company code. New values are verified against the server before saving.
  • β˜… Install update v<N> (appears when a new version is available) β€” downloads + swaps the running client without needing a reboot.
  • Quit OtiumWork β€” shuts down the tracker until you launch it again. Tracking does not auto-restart until next Windows login.

How tracking decides "active vs idle vs walked-away" (v1.6.5+)

The client samples the foreground app + window title every 10 seconds. It then categorizes each sample using a layered idle-detection model:

Signal Category
Workstation locked Dropped β€” user is genuinely away
Recent keyboard / mouse input (< 2 min) Active
Foreground app is Teams / Zoom / Webex / Meet / Skype / Slack / Discord / etc. (the meeting whitelist) Active even with no input β€” user is in a call
Microphone in use anywhere on the machine Active β€” user is on a call or dictating
Camera in use Active β€” user is in a video meeting
No input for 2-10 min, foreground app stable Passive β€” captured + tagged so reports can distinguish "actively typing" from "window focused but reading"
No input for 10+ min, no audio/video signals Dropped

Privacy: only the boolean state of mic/camera is read (same data Windows uses for its tray privacy indicator). No audio or video content is captured or transmitted.

Every 5 minutes the client batches its samples and POSTs to /api/events. Events are tagged with passive_minutes separate from total minutes so the soft-idle time is auditable.

Auto-update + cleanup (v1.6.4+)

The client checks for a newer version at every Windows login and every 24 hours while running. If found, it downloads in the background, verifies a SHA-256 hash, kills any stale otiumwork-client.exe processes, deletes orphan .exe copies in Downloads / Desktop / OneDrive folders, and relaunches itself. The same cleanup runs on every fresh launch, so a freshly-downloaded .exe always sweeps stale versions before starting.

Troubleshooting

Tray icon is RED with a "!" badge: - Your credentials are being rejected. Right-click β†’ top item ("⚠ Authentication failed β€” Reconfigure") β†’ enter the correct email + 22-character company code (paste, don't retype).

Tray icon doesn't appear at all: - Click the ^ arrow near the clock; it might be hidden there. Drag it out onto the taskbar if so. - Check Task Manager β†’ Details tab for otiumwork-client.exe. If it's not running, log off/on.

Events aren't flowing: - Open the log file: paste %APPDATA%\OtiumWork\client.log into File Explorer and press Enter. Scroll to the bottom. Any WARN / ERROR lines explain what's wrong. - A 401 in the log = wrong credentials β†’ Reconfigure. - Check your internet connection.

I paused and forgot to resume: - Right-click the icon β†’ Resume tracking. It won't catch up on missed time β€” tracking only starts from the moment you resume.

Uninstall

  1. Run otiumwork-client.exe --uninstall from a Windows command prompt, or
  2. Delete %LOCALAPPDATA%\OtiumWork\ manually.

Your captured events remain on the OtiumWork server β€” uninstalling just stops future capture.

4. My Day

Last updated 2026-06-30 Β· open as standalone page

My Day

/my-day is your daily home in OtiumWork: tap a project tile β†’ everything your desktop client captures until the next tap gets attributed to that project. For retrospective fixes use /events/mine.

How it works

  1. You open /my-day. You see tiles for every project your manager has assigned to you, ordered by urgency (tasks due in 48h β†’ over-budget β†’ logged-today β†’ recent β†’ everything else).
  2. You tap a tile. A focus session opens: project_id = that tile.
  3. The desktop client polls /api/focus/current every 30 seconds and stamps X-Session-Id on every captured event it uploads.
  4. On the server side, each work_event is tagged with session_id and its project_id is set to the tile's project (the tile-press is explicit user intent β€” it overrides any auto-guessed project_id from rules / LLM).
  5. When you tap a different tile, the current session closes (ended_at = now) and a new one opens.
  6. Tap "Stop & go idle" to close the session without opening a new one (lunch, end of day, etc.).

Capture modes β€” Auto / Hybrid / Manual

At the top of /my-day there's a small capture-mode chip. Click it to cycle AUTO β†’ HYBRID β†’ MANUAL β†’ AUTO. It sets how the server treats events your desktop client uploads β€” it does not change the desktop client itself, only what the server keeps:

Mode What the server does Use it when
Auto (default) Every desktop-client event becomes its own work_event row. Tapping a tile sets the project_id. This is the classic "tap a tile, everything rolls up under it" behavior. You want full automatic tracking with the least effort.
Hybrid While a block is open, the captures during that block are collapsed into one session-level row instead of one row per window-switch β€” so a 1-hour block is a single tidy line, not 40 fragments. While no block is open, it behaves exactly like Auto. You like auto-capture but want clean, block-sized entries instead of granular fragments.
Manual The server drops every desktop-ingest event. Nothing is recorded unless you create it β€” by tapping a tile or starting a manual block. Your hours come only from time you explicitly log. You'd rather punch in your own time blocks and not have passive capture at all.

Why this matters for your totals: in Hybrid and Auto, a project can show hours from two sources at once β€” the manual block you opened and the desktop activity captured underneath it. That's expected; the block and its rolled-up captures are the same time, not double-counted. In Manual, the only hours are the blocks you key in.

If a project shows hours you don't recognize, check your capture mode first β€” Hybrid/Auto are folding in real desktop captures, not just what you typed.

What doesn't change

  • Category, work type, department β€” still set by the existing three-layer classifier (admin rules β†’ past corrections β†’ Claude Haiku). My Day only owns project_id.
  • Desktop client capture β€” nothing changed. Window titles still get captured every few seconds, grouped into per-minute buckets, flushed every 5 minutes.
  • Your manual corrections β€” still override everything. Edit any event at /events/mine and your edit wins.

Precedence for project_id / task_id on each event

From highest to lowest priority:

  1. Manual correction in /events/mine
  2. My Day active tile + task (manually chosen via the tile grid / task dropdown)
  3. Outlook auto-focus session (opened because a calendar meeting's subject matched a project)
  4. Classification rule match on window title
  5. LLM (Haiku) guess
  6. NULL β€” shows up in the "needs classifying" queue

Note: if you manually tap a tile, the Outlook auto-focus will not override your choice. It only acts when you're idle (no open session) or when the currently-open session was itself auto-opened by Outlook.

The timeline below the tiles

Once you've started using tiles, "Today's sessions" appears below the grid β€” a chronological list of what you focused on today with duration per session. Each session is expandable to see the underlying captures.

Logging time against tasks (not just the project)

Hours can attach to a project alone, or to a task within that project. If you want per-task breakdowns β€” and you usually do, since that's what makes the project's task list and burn-down meaningful β€” you have to point your time at a task. Here's the full flow:

  1. Give the project tasks. A project with no tasks can only collect project-level hours. Add tasks from the project's detail page (/projects β†’ the project β†’ per-task table), from My Tasks, or inline while editing a block (the + next to the Task dropdown β€” manager/admin role).
  2. When you work, pick the task. Tap the project tile, then choose the task in the Task: dropdown on the active banner. From then on every captured event stamps both project_id and task_id, so the hours land on that task. The soonest-due task is auto-selected; switch anytime.
  3. Check it landed. The project detail page breaks hours down per task and per person. If a task reads zero, no time was stamped to it.

The common trap: if you open a manual block (or tap a tile) and don't select a task, the hours attach to the project only β€” the project shows time but every task reads zero. This is the #1 reason "the project has 14 hours but the tasks are empty."

Fixing it after the fact: you don't have to re-log anything. Click the block on the My Day timeline β†’ Edit block β†’ set the Task β†’ save. The underlying work_event rows are re-stamped onto the task. For older entries, use the activity drawer at /events/mine (edit inline, or shift-click a range and bulk-assign).

Overlap warnings β€” when a manual block fights a calendar meeting

If you start a manual focus session (project tile) at, say, 2-3pm AND your Outlook calendar has a meeting at 2:30-3pm, both blocks render side-by-side on the timeline strip β€” and both contribute minutes to your totals. That's 1.5h on the books when you really only worked 1h.

To avoid the double-count, an amber banner appears at the top of /my-day with two quick-resolve buttons per overlap:

  • Keep meeting Β· drop session β€” deletes the manual focus_session plus any work_event rows that rolled under it.
  • Keep session Β· drop meeting β€” deletes the calendar-imported work_event row(s) for that meeting.

A pair is flagged when they overlap by β‰₯50% of the shorter block. Up to 5 conflicts surface at a time. Desktop-tracker fragments inside a meeting window get absorbed silently β€” those never trigger this warning.

Stale focus-session warning

If your current focus session was started more than 24h ago, a separate amber banner shows up offering a one-click "Stop the stale session" button. Common case: you tapped a tile on Monday morning and forgot to tap Stop. Until you close it, every desktop capture gets stamped with that session's project, and the calendar strip stretches across the whole stale window.

What "My Activity" is for now

/events/mine is still there. It's the audit / fix-it drawer: use it when you forgot to tap a tile in the morning, when you want to fine-tune per-event categorization, or when a manager asks "what were these 3 hours on Tuesday?" It's no longer the daily destination β€” My Day is.

Urgency ordering (tile sort)

Top-to-bottom:

  • Due soon (red): project has an assigned task with end_date ≀ today + 48h
  • Over budget (orange): project has budget_hours and logged hours exceed it
  • Today (blue): you've already logged any time to this project today β€” stay in context
  • Recent (gray): you've logged time to it in the last 7 days
  • Assigned (faint): everything else you're assigned to

Phase 2 (live as of 2026-04-24)

  • Task-level granularity. When you tap a tile, the active banner shows a Task: dropdown with that project's open tasks. The soonest-due task is auto-selected; switch to a different one anytime. Every event captured during the session stamps both project_id and task_id, so timesheets and reports can break down hours per task.
  • "Switch tile?" nudge. If the last 15 minutes of captured window titles mention a different project more than the tile you're currently on, a yellow banner appears at the top of /my-day with a one-click Switch to X button. Safety-net for the "oh, I forgot to tap" moment.
  • Outlook-meeting auto-focus. If you've connected Outlook (Admin β†’ Settings β†’ Microsoft 365), a server cron checks every 2 minutes whether a meeting is happening right now. If the meeting subject contains the name of one of your assigned projects, a focus session opens automatically β€” same as if you'd tapped the tile. Manually-opened sessions are never overridden. When the meeting ends, the auto-opened session closes and you return to idle.

Editing a calendar block β€” inline task creation

Click any block on the calendar timeline to open the Edit block modal. You can change the label, project, task, and start/end times.

If the project you pick doesn't yet have the task you want (e.g. you just realised this meeting was a reseller meeting and that task doesn't exist), hit the + button next to the Task dropdown. A small inline row appears β€” type the task name, hit Add (or Enter), and the task is created on that project and pre-selected for you. Manager/admin role required.

Phase 3 (not yet)

  • Cross-device tile sync via websocket (today it's 30s polling, which is fine)
  • AI suggesting the right tile from capture signals without even needing the nudge
  • Manager live view: see who on your team is focused on what right now

5. My Tasks

Last updated 2026-06-06 Β· open as standalone page

My Tasks

/my-tasks is your personal work inbox. It sits next to My Day in the sidebar (with a badge showing how many tasks are open) and pulls everything that's yours into one triage list.

What shows up here

  • Personal β€” standalone to-dos that aren't tied to any project. These are private to you; no one else can see them.
  • Each project β€” tasks on real projects where you're the assignee or a collaborator.
  • IT tickets β€” helpdesk tickets assigned to you (they already mirror in as tasks). The row links back to the ticket.
  • Marketing requests β€” requests assigned to you, linking back to the request.

Each task shows its priority (high / normal / low) and due date, and overdue dates are highlighted in red. Tasks handed to you by a colleague are tagged "from ".

Adding a task

Use the bar at the top:

  • Name β€” what needs doing.
  • For β€” leave as Myself for a personal to-do, or pick a colleague to assign it to them (see below).
  • Due and Priority β€” optional.

Working your list

  • βœ“ Done β€” marks a personal or project task complete and drops it off the list.
  • Edit β€” (personal tasks) change the name, due date, or priority inline.
  • βœ• β€” (personal tasks) delete it.
  • Open β†— β€” for IT tickets and marketing requests, jump to the source to update them there. (Their status is owned by that module, so close them at the source to keep things in sync β€” not from here.)

Assigning a task to someone else

Pick a colleague in the For dropdown when adding a task. It becomes their personal task, shows up in their My Tasks marked "from you", bumps their sidebar badge, and emails them a heads-up. Use it for quick hand-offs without spinning up a whole project.

My Day integration

Tasks that are due today or overdue also appear in an "On your plate today" strip at the top of My Day, so your daily planning sees them without leaving the page.

6. My activity

Last updated 2026-04-27 Β· open as standalone page

My activity

As of 2026-04-24, My Day (/my-day) is where you start your day. My activity (/events/mine) is now the audit drawer β€” the place to go when you forgot to tap a tile, when you want to re-categorize a specific event, or when a manager asks about a particular hour block.

Everything below still works identically β€” it just isn't your daily home anymore.

The review page

/events/mine is your personal review page β€” every event captured on your machine (or entered via Timer) shows up here. Review it when My Day's auto-attribution needs correcting.

Filters

The page defaults to your last 7 days. Use the filter bar at the top to narrow further:

  • From / To β€” pick any date range. To-date is inclusive ("To = Friday" includes all of Friday).
  • Project β€” show only events linked to one project, or All for everything.
  • Only unclassified β€” toggle to see just the rows still tagged category=unclassified. Best filter when you want to triage quickly.
  • Reset β€” clears all filters and snaps back to the last-7-days default.

Results are paginated 100 per page; if you have more matches, page links appear under the table. The result count is shown at the right side of the filter bar.

What you can edit

Every row is inline-editable:

  • Category β€” the type of work (see Categories)
  • Work type β€” billable / internal / training / off-time
  • Project β€” which project this event is for
  • Department requested β€” which team requested this work (cross-team support)
  • Note β€” any free-text explanation
  • Tags β€” labels you or your admin set up

Bulk-link to a project

If you worked on the same project for several captured events:

  1. Check the β˜‘ box at the left of each relevant row.
  2. Scroll to the top; the "N selected" banner lights up.
  3. Pick a project from the dropdown (or "(unlink)" to clear).
  4. Click Apply. All checked events are updated in one shot.

Or hit + New project in the same banner if the project doesn't exist yet β€” it'll be created with you auto-assigned as a team member.

What "corrected" means

When you edit any classification field, OtiumWork records your edit as an employee correction. This has two effects:

  1. Future identical events get classified the same way automatically. If you change all chrome.exe / Gmail events to category=communication, the classification engine will use that pattern on future events without calling the AI.
  2. Your manager can see the correction count on the Team page β€” high correction rates hint that either the AI is misclassifying you specifically or your work is ambiguous.

Categories can come from multiple sources

Each event's classification_source field tells you how it was classified:

  • rule β€” an admin-defined pattern matched (fastest, free)
  • past_event β€” someone previously classified the same (app, title) and we reused it
  • employee_correction β€” you manually corrected it
  • manager_override β€” your manager changed it
  • llm β€” AI classified it (Claude)
  • timer β€” legacy: logged via the old Timer page (removed; old events still carry this source)
  • clockify_import β€” imported from a Clockify historical export

Getting more classified

If too many events show as "unclassified", try: - Writing admin rules (admins only): /admin/rules β€” deterministic patterns that fire instantly and cost nothing. - Correcting a few events β€” future similar ones inherit automatically. - Turn AI classification on (admins): /admin/settings β†’ AI billing β†’ Metered mode with a cap. Haiku is cheap.

7. Projects and clients

Last updated 2026-07-31 Β· open as standalone page

Projects and clients

OtiumWork uses two first-class entities for billable work attribution:

  • Client β€” a billing entity (e.g., "Acme Corp"). Has a stage (prospect / active / archived), billing email, notes. Lives at /admin/clients.
  • Project β€” work done for a client (e.g., "Acme Q3 feasibility study"). Has status, budget, start/end dates, team assignments. Lives at /projects.

Every work event can be linked to a project; every project belongs to a client; reports aggregate up the chain.

Who can do what

Employee Employee who is the project's PM Manager Admin
Create project βœ“ (auto-assigned as member) βœ“ βœ“ βœ“
Edit project Only if you created it βœ“ for the projects you manage βœ“ for your team's βœ“
Assign people to project β€” βœ“ for the projects you manage βœ“ for your direct reports βœ“
View project detail Only if you're assigned or created it βœ“ βœ“ for your team's βœ“
Plan on the Gantt (add tasks, drag dates, set assignee / status / %) β€” βœ“ for the projects you manage βœ“ βœ“
Create client β€” β€” βœ“ βœ“
Change client stage β€” β€” βœ“ βœ“

Project managers with the plain "employee" role

Running a project doesn't require the manager role. Someone counts as a project's PM β€” and gets the rights in the column above, on that project only β€” if either:

  • they're named in the project's Project manager field (project edit), or
  • their role on project in the team list is PM, Project Manager, Program Manager, or Lead (case-insensitive).

A PM-employee sees the projects they manage on /projects with the planning (Gantt) strip, and can assign work to anyone staffed on those projects. They still don't get the company-wide capacity grid, the KPI strip, profitability, or any dollar figures β€” those stay manager/admin (and Finance-flag) territory.

Client stages

  • Prospect β€” you're doing pre-sales work, not yet a paying customer. Hours here count as pre-sales investment (/reports/presales).
  • Active β€” paying customer. Hours on active-stage clients count as billable.
  • Archived β€” dormant or lost deal. Hours stay in history but are ignored by active reports.

When a prospect signs, flip their stage to active from /admin/clients β€” pre-sales hours retroactively count as converted investment.

Project status

  • planned β€” not started yet (shows on Gantt but not in active views)
  • active β€” in progress
  • on_hold β€” paused
  • completed β€” done and billed
  • canceled β€” shut down before completion
  • archived β€” historical / imported (hidden from /projects by default; click + Show N archived to reveal)

Jumping to a specific project

The filter bar on /projects and /projects/gantt has a Jump to β†’ dropdown listing every non-archived project alphabetically. Picking one navigates straight to that project's detail page (per-task table + per-person time-spent breakdown). On the unified Gantt at /projects, project name labels are also clickable.

Budgets + alerts

Projects can have: - Budget hours β€” e.g., "200 hours allocated" - Budget dollars β€” hard spend ceiling - Alert threshold β€” default 80%; when logged hours Γ— hourly_rate crosses this fraction of budget, admins get an email alert

Linking events to projects

Three ways:

  1. Desktop client correction β€” right-click tray icon β†’ Review recent events β†’ pick project from dropdown.
  2. Web review page β€” /events/mine, edit inline or use bulk-link.
  3. Rules β€” admins create pattern-based rules at /admin/rules that auto-link matching events.

8. Vacation (PTO)

Last updated 2026-04-25 Β· open as standalone page

Vacation (PTO)

/pto is the employee surface for requesting time off and seeing your balance. Approved vacation automatically reduces your weekly capacity on Team β†’ Capacity, so managers see realistic availability.

How the balance works

Each year:

remaining = annual_vacation_days βˆ’ approved_days_this_year βˆ’ pending_days_this_year
  • Entitled β€” your contract entitlement (default 20 days/year; admin/finance set per-employee).
  • Used β€” sum of approved vacation days in the current calendar year.
  • Pending β€” requests still awaiting a decision, whether that's a manager or an executive after escalation (held against your balance until decided).
  • Remaining β€” what you can still request.

Workdays only β€” Mon–Fri. Weekends in your range don't count against the balance.

Request flow

  1. Employee opens /pto, picks dates, optionally adds a reason, submits.
  2. Manager sees the pending request at Team β†’ Vacation approvals. Approves or rejects with a comment.
  3. Approved vacation immediately reduces capacity in the affected weeks. Rejected requests go back to "Rejected" with the manager's comment visible to the employee.
  4. Cancel β€” employees can cancel their own request for as long as it's still awaiting a decision, including after a manager escalated it to an executive. Once it's approved or rejected they can't.

Manager view

/team/pto-approvals (Team β†’ Vacation approvals) β€” pending requests from your direct reports float to the top with employee, dept, dates, days, reason, and Approve/Reject buttons. Admins see the full company.

Capacity impact

Each cell on Team β†’ Capacity now shows allocated / capacity where:

capacity = max(0, 40 βˆ’ company_holiday_hours βˆ’ approved_PTO_hours)

Approved PTO uses 8h/workday for the days that fall in that week. Tooltip on the cell breaks down the math.

Setting entitlement (admin or finance-flag holders)

/admin/employees/<id>/edit β€” the Annual vacation entitlement (days) field. Defaults to 20. Set:

  • 0 for contractors (typically no PTO)
  • 25 for senior staff
  • Custom per contract

Existing approved/pending days roll forward β€” only new requests use the updated cap.

Limits

  • Tier 1 only β€” company-wide holidays + per-employee approved vacation.
  • No carryover-from-prior-year accounting yet.
  • No accrual rate (e.g., "1.67 days per month") β€” just a yearly cap.
  • No outlook-OOO sync yet (Tier 2, future).

9. AI-drafted weekly timesheet

Last updated 2026-04-24 Β· open as standalone page

AI-drafted weekly timesheet

On /timesheets every week with captured activity has a ✨ AI draft button. Click it and Claude reads your week's events and produces a professional summary β€” executive-line, per-project bullets, and a Notes section for anything noteworthy (long meetings, unusual off-time, etc.).

What it's good for

  • Weekly status emails to your manager β€” copy the draft, paste into Outlook, edit if needed, send.
  • Monthly performance docs β€” string together four weeks to get a month-end narrative.
  • Client retainer reports β€” filter to billable work, paste as the "this week's delivery" block.
  • Memory aid β€” if you can't remember what you did on Tuesday, the draft reminds you.

How it works

  1. Your week's captured events (desktop client + Timer + any Clockify-imported rows) are grouped by project and category.
  2. A compact JSON payload (not your raw window titles β€” just aggregates + sample notes) is sent to Claude Haiku with a "draft a professional timesheet" prompt.
  3. Result stored on your timesheet row β€” regenerating overwrites. Can draft as many weeks as you want.

Uses AI credits from your company's metered allocation (or your BYOK key if admin picked that). One draft β‰ˆ $0.005 at Haiku pricing.

Privacy

  • Per-user scope β€” only you see your own drafts. Even your manager can't see them (they live on your timesheet row).
  • Events not in your captured work (e.g., someone else's) are never fed to the model.
  • Drafts are never auto-submitted β€” you always review before sending anywhere.

10. Partners / resellers (admin)

Last updated 2026-04-27 Β· open as standalone page

Partners / resellers (admin)

OtiumWork supports external resellers that sell on behalf of your company β€” distinct from your internal sales team and from your customers. They get their own portal with role-gated views, submit deals for approval, see commission accruals, take certifications, and ask technical questions.

To use this feature, an admin must enable the Partner portal module on the company at /admin/modules. New tenants default OFF.

Adding a partner

  1. Navigate to Partners β†’ Resellers in the sidebar (admin only).
  2. Click + New partner. Set name, default commission %, territory notes. W-9 status is for US 1099 tracking.
  3. After save, you're on the partner detail page. Add contacts (one row per person at the partner who needs portal access). Each contact gets a role:
Role Sees Can do
principal Everything for this partner Manages other contacts, signs reseller agreement, all of the below
salesperson Own deals, sales/marketing/competitive collateral, price lists Submits deal intents, uploads POs, sees own commission accruals
technical All this partner's deals (no $), engineering docs, own certs Uploads technical questions, takes certifications, attaches files
finance All deals + commissions + payouts + price lists (read-only) Downloads payout reports
viewer Dashboard, deal status (read-only) Nothing else
  1. Each new contact gets an invite email (link valid 7 days) sent automatically. They click to access the portal β€” no password needed; sessions last 30 days.

Deal intents

When a partner contact submits a deal via /partners/deals/new, it lands as a quote.kind='partner_intent' row in Partners β†’ Deal intents. Review queue lets you:

  • Approve β†’ flips kind to quote, the row enters your normal quote β†’ invoice flow with partner_id and the chosen commission rate locked in. Partner sees status change in their portal.
  • Reject β†’ stays as partner_intent with status='rejected'. Add a note (visible to partner).

All decisions log to audit_log. No bypass: every deal goes through admin review before it can become a sellable quote.

Q&A inbox

Technical role contacts can submit pre-sales technical questions at /partners/questions/new. Optionally tied to a specific deal. Admins (or any role at your tenant with admin access) respond at Partners β†’ Questions. Mark as answered to keep open for follow-up, or check Close when fully resolved.

Training & certifications

Modules + auto-graded quizzes β€” annual re-certification supported.

  1. Partners β†’ Training β†’ + New module. Set kind (sales/technical/both), required-for-role, pass threshold (default 80%), valid-for-days (default 365 = annual).
  2. Quiz JSON uses this structure: json {"questions": [ {"q": "What's our standard discount cap?", "choices": ["5%","10%","15%","20%"], "correct_idx": 1}, {"q": "...", "choices": [...], "correct_idx": 0} ]}
  3. Partners see required modules on their dashboard + at /partners/training. They take the quiz; on pass, a certification row is written with expires_at = completed_at + valid_for_days.
  4. Failed attempts don't break anything β€” partners can re-take immediately.

Document sharing

Partner contacts can see documents in any document_space marked partner-visible:

  1. Edit a document space (under /documents/spaces).
  2. Set Visible to partner (drop-down with your active partners) and Visible roles (CSV like principal,salesperson for marketing/pricing, principal,technical for engineering docs, or all).
  3. Partners see allowed spaces at /partners/documents and download via the same app/storage.py path internal users use. Same audit log.

Schema reference

  • partner β€” one row per reseller organization.
  • partner_contact β€” multiple per partner, with role.
  • partner_session β€” magic-link sessions, 30-day idle expiry.
  • partner_question β€” Q&A inbox, optionally deal-linked.
  • training_module β€” module + quiz JSON + pass threshold + validity.
  • certification β€” polymorphic: either partner_contact_id OR employee_id set (CHECK constraint). Same framework will be reused for internal-employee certifications in v2.
  • sale.partner_id + sale.partner_commission_rate β€” partner attribution + override of partner.default_commission_pct.
  • sale.po_document_id β€” PO uploaded by partner, attached to the sale.
  • quote.kind (quote | partner_intent) + quote.partner_id + quote.partner_intent_* β€” partner-submitted deal-intent state.
  • document_space.partner_visible_to_id + document_space.partner_role_visibility β€” role-gated visibility.
  • company.module_partner_portal_enabled β€” per-tenant feature flag.

Sequencing & limits (v1)

What's NOT implemented yet (revisit when needed): - PO upload UI on individual deals β€” column exists, no upload form yet. - Cert expiry banners with date math β€” dashboard shows certs but doesn't compute "expires in N days" client-side. - Multi-currency partner commissions. - Tiered commission structures. - Territory exclusivity / hard collision blocking on deal intents. - Partner-driven sub-resellers. - 1099 threshold automation (we track w9_status only β€” actual 1099 generation lives in QuickBooks). - API access for partners (no Salesforce-to-OtiumWork sync). - Internal-employee certification (schema is ready; UI not wired).

For migration off Salesforce: enable the partner portal module β†’ add ABC Mexico (or whoever) as a partner β†’ invite their principal contact β†’ they invite their team or you do β†’ done. Salesforce seats can come down once they've used the portal for a real deal cycle.

11. Partner portal

Last updated 2026-04-27 Β· open as standalone page

Partner portal

This page describes what your resellers / partners see at /partners/*. They reach the portal via an invite email containing a magic link β€” no password is set up for them.

Sign-in flow

  1. Partner contact receives an invite email the first time you add them as a contact (admin: /admin/partners/<id>/contacts/new). Link valid 7 days.
  2. They click β†’ land at /partners/dashboard. A 30-day session cookie is set.
  3. After the first invite expires, they go to /partners/login, enter their email, and a fresh 30-minute sign-in link is sent. Same flow as the customer portal.

If they can't find the invite, resend from /admin/partners/<id> β†’ contacts table β†’ "Resend invite."

What each role sees

The dashboard, navigation, and per-page content are gated by the contact's role. See the admin help for the full role table.

The top nav shows: Dashboard Β· Deals Β· Documents Β· Questions Β· Training. Items they can't act on are still visible read-only or hidden depending on role.

Submitting a deal (salesperson, principal)

/partners/deals/new β€” they fill in customer name, deal title, expected USD amount, optional close date, and free-text description. Submit creates a quote row with kind='partner_intent'. They see "Pending approval" in their portal until you decide.

Documents

/partners/documents lists every document_space marked visible to their partner_id with their role in the allowed-roles CSV. Downloads route through the same authenticated app/storage.py path that internal employees use β€” full audit, no separate storage backend.

Questions (technical, principal)

/partners/questions/new β€” submit subject + body, optionally link to a deal_id. Posts to partner_question table. Admins answer at /admin/partners/questions. Threading is single-level for v1 β€” one question, one answer (admin can update the answer).

Training

/partners/training lists modules where required_for_role matches their role (or is both). Each module shows their cert status:

  • Not started β€” no passed cert exists for this contact + module.
  • Certified β€” passed cert exists. Expiry date shown if valid_for_days was set.

/partners/training/<id> shows the module description and the quiz. They submit; auto-grading writes a certification row. Pass = score β‰₯ module's pass_threshold (admin sets per-module, default 80%).

Footer

Every partner-portal page renders the tenant's brand (logo + primary color from company.brand_*), with a small "Powered by OtiumWork" footer link. Per-reseller white-label (where ABC Mexico shows their OWN brand instead of the tenant's) is a separate v2 build.

Limits / gotchas

  • Sessions are tied to one browser. Sign-in from a new device β†’ another magic link.
  • The invite token in the URL is single-use. If they click twice, the second click hits "Link already used."
  • salesperson role currently sees ALL of their partner's deals, not just deals they personally submitted. Tighter per-contact scoping is a follow-up.
  • Deal collisions (two partners registering the same end-customer) currently surface only at admin review time β€” no automatic territory rules in v1.

12. Categories, work types, and departments

Last updated 2026-04-25 Β· open as standalone page

Categories, work types, and departments

Each event gets two orthogonal classifications plus an optional department attribution. The starting values are below; admins can customize them per-company at /admin/taxonomy (sidebar: Categories & depts).

Editing the lists

  • Add β€” type a new value, click Add. Available immediately in every dropdown.
  • Deactivate β€” hides from new entries; existing events keep showing it (no rewrite).
  • Reactivate β€” re-enables a previously deactivated value.

To rename a value, add the new one and deactivate the old. Bulk-correct existing events from the team admin page if you need historical consistency. Changes affect My Day, /events/mine, reports, manager review, classification rules, and the AI auto-classifier β€” all read your current company list.

Category β€” what kind of work was it?

Fixed enum:

Category Meaning Example
analysis Deep thinking, modeling, calculations CFD simulations, financial modeling
design Creating or editing deliverables CAD drawings, slide decks, proposals
documentation Writing docs, reports, wikis Tech docs, SOPs, knowledge base
meeting Scheduled or ad-hoc calls/meetings Teams, Zoom, phone calls
communication Email, chat, messaging Outlook, Slack, Teams chat
admin Internal operations Expenses, HR, IT tickets, billing
research Exploration without clear deliverable Reading papers, competitor research
content Marketing / content creation Blog posts, social media, graphics
prospecting Pre-sales, lead gen Demos for prospects, outreach
pipeline Active deal management Proposals, scoping calls
training Learning, courses, onboarding Certifications, company training
off_time Not actively working PTO, sick leave, holidays
unclassified Not yet categorized Pending review

Work type β€” how does this affect billing?

Work type Meaning
billable Time the client pays for
internal Internal work, not billable
revenue Revenue-generating but not client-billable (e.g., commission work)
off Off-time (vacation, holiday, sick)
training Paid training time
unclassified Not yet categorized

How they combine

A single event has one category and one work type. Examples:

  • "Building a client slide deck" β†’ category=design, work_type=billable
  • "Team standup" β†’ category=meeting, work_type=internal
  • "Attending a conference" β†’ category=training, work_type=training
  • "Doctor's appointment" β†’ category=off_time, work_type=off

Who decides these values

  1. Admin rules (instant, deterministic) β€” patterns like "app=Outlook.exe β†’ category=communication"
  2. Past event lookup β€” if someone corrected chrome.exe / Gmail before, new events inherit
  3. AI classification β€” Claude Haiku classifies when nothing else matches
  4. You correcting on My events β€” always wins; propagates forward

Adding your own dimensions

OtiumWork's categories are fixed (changing the enum requires a schema change). For project-specific or team-specific labels that don't fit the enum, use tags β€” see Tags.

13. IT helpdesk

Last updated 2026-05-04 Β· open as standalone page

IT helpdesk

A simple in-app ticketing system. Any employee can submit, IT staff triage from a shared queue. Tickets support screenshot/PDF attachments, deadlines, priority queue, and a morning digest email.

Turning it on

Per-tenant module flag: admin β†’ /admin/settings/modules β†’ toggle IT helpdesk ON. Default is OFF for new and existing tenants (it doesn't show up in anyone's sidebar until you enable it).

To make someone an IT-staff member, set their department field to IT at /admin/employees/<id>/edit. They'll start seeing the IT queue automatically.

What employees see

A single sidebar link in Your work:

  • πŸ› οΈ IT tickets β†’ opens /it/mine which has both:
  • The list of their own tickets with current status + due date
  • A prominent "+ Report an issue" button at the top

Click the button β†’ fill in title + category (Hardware / Software / Network / Account / Other) + description β†’ submit. Attach up to 5 screenshots or PDFs (≀ 5 MB each, image/* or application/pdf) on the form or on any follow-up comment. The IT team is emailed immediately.

When IT updates the ticket β€” comments, status changes β€” the reporter gets an email with a direct link.

What IT-staff and admins see

A dedicated IT sidebar section with three items:

  • πŸ› οΈ Report IT issue β€” same form as above
  • β—† My tickets β€” tickets they've personally submitted
  • ≑ IT queue β€” the shared triage queue

The queue shows every ticket sorted by priority (Urgent β†’ High β†’ Medium β†’ Low), then by due date (overdue surfaces first), then last-update time. Overdue dates render in red. Filter by status (Open / In progress / Resolved / Closed / All) and by category. A πŸ“ŽN badge in the title column shows attachment count.

Triaging a ticket

Open a ticket from the queue. As IT-staff you get an "IT actions" panel:

  • Claim this ticket β€” one-click; sets you as assignee, status flips to In progress
  • Status dropdown β€” Open β†’ In progress β†’ Resolved β†’ Closed
  • Priority dropdown β€” Low / Medium / High / Urgent
  • Due date picker β€” sets a deadline; overdue tickets surface in red
  • Assignee dropdown β€” reassign to another IT-staff member
  • Comment box β€” visible to the reporter; firing a comment also emails them. Supports attachments.

Manager priority/deadline overrides

Anyone with role manager (or admin) gets a Manager actions panel on a ticket they can view, with just two controls:

  • Priority β€” promote/demote so urgent items surface
  • Due date β€” set a deadline to commit to

Managers can't change status or reassign β€” those stay with IT-staff β€” but they can flag importance and ETA from any ticket they have access to (their own team's submissions, or all of them if they're admin).

Status semantics

  • Open β€” fresh ticket, not yet picked up
  • In progress β€” someone's working on it
  • Resolved β€” IT thinks it's fixed; reporter gets emailed and can reply if wrong
  • Closed β€” done, no further activity expected

Morning digest

Every morning at 07:00 UTC, every IT-staff member (and admin) gets one email per company listing all open + in_progress tickets, grouped by priority and sorted by due date. Overdue tickets are flagged inline. The digest is skipped for companies with zero open tickets.

If you want a different time, that's a single line in the prod crontab (/api/cron/it-morning-digest).

Email notifications

When Who gets emailed
Ticket submitted Every IT-staff member + admins (broadcast)
Status change The reporter
Comment added The "other party"
Daily 07:00 UTC All IT-staff for any tenant with open tickets

Attachment policy

  • Allowed types: image/png, image/jpeg, image/gif, image/webp, image/heic, application/pdf
  • Max size: 5 MB per file, 5 files per upload (per submit/comment)
  • Storage: content-addressed via app/storage.py (deduped by sha256)
  • Access: only the reporter, IT-staff, and admins can fetch via /it/attachment/<id>

Out of scope for v1: SLA timers, internal-only comments, asset register, knowledge base, escalation rules. Add as needed via /feedback.

14. Tags

Last updated 2026-04-24 Β· open as standalone page

Tags

Tags are free-form labels you apply on top of the fixed category + work type enums. Use them for things that span projects, departments, or quarters β€” any label that doesn't fit a single existing dimension.

When to use a tag vs a category

Use a category when the work is intrinsic (analysis, meeting, admin…) β€” these are cross-company standards.

Use a tag when: - The label is temporary: Q3-push, year-end-close - The label cross-cuts existing dimensions: R&D, compliance-audit - The label is project-specific but you don't want a separate project: prospect-X-research

Managing tags (managers + admins)

/admin/tags β€” add, edit, archive, set colors. Each tag has: - Name (short) - Color (color picker) - Active flag

Applying tags to events

  • Desktop client tray β†’ Review β†’ picker (once shipped in a future client release)
  • Web: /events/mine β€” tags appear on each row (future enhancement)
  • Manager overrides on the team view can set tags too

Filtering by tag

On /reports/time there's a Tag filter dropdown. Pick a tag to scope the report to only tagged events.

Honest take

Tags are table-stakes feature-parity with Clockify, but in practice most OtiumWork customers use categories + projects + clients and rarely need tags. If you never reach for them, that's fine β€” delete them later.

15. Competitor tracking (your company's)

Last updated 2026-04-24 Β· open as standalone page

Competitor tracking (your company's)

This is where your company tracks your own competitors β€” the ones in your market, that threaten your product. For SoftInWay that's Aspen Technology, Concepts NREC, etc. for the AxSTREAM engineering software.

Not to be confused with OtiumWork's roadmap at /roadmap β€” that's for voting on features you want OtiumWork to build. This page is for your product's competitive landscape.

Three surfaces:

/admin/competitors β€” source list

A curated list of competitor URLs (product blogs, changelogs, release notes, etc.). Every Monday at 10 UTC, Claude fetches each URL and surfaces feature announcements from the last 14 days.

Editing a competitor: expand the Edit disclosure on any row. Change name, URL, category, notes. Employees, managers, and admins all have edit access.

Deep scan a competitor: expensive one-shot β€” Claude reads their product pages and enumerates their full feature set, tagged SHIP / CONSIDER / AVOID / NOOP vs your offering. Takes ~30-60 seconds per competitor. Managers + admins only.

/admin/competitors/features β€” feature board

Every finding across competitors, tagged with: - SHIP β€” build this, high priority - CONSIDER β€” debate it - AVOID β€” intentionally different from them - NOOP β€” you already do it / not relevant

Inline edit of tag, status (new / backlog / building / shipped / skipped), priority 1-5, and notes. Use the status workflow to push SHIP items through your dev pipeline: new β†’ backlog β†’ building β†’ shipped.

Dev roadmap view: /admin/competitors/features?tag=SHIP&status=backlog gives you the fastest next-steps list.

Voting: Every feature row has a β–² vote button. Any employee can vote (one per user per feature). Vote counts sort rows within each tag β€” popular items float up, so the roadmap reflects what the team actually wants to see shipped. Includes features auto-promoted from user feature requests.

/admin/competitors/mine β€” personal feed

Per-user subscriptions. Click the β˜† on any competitor or feature to star it β€” now it shows up in your feed.

Two subscription scopes: 1. Personal β€” just you. Click β˜† on a row. 2. Team (managers + admins only) β€” expand the "team…" disclosure, pick a department, click Toggle team. Everyone in that department now gets the subscription.

Two subscription targets: 1. Full competitor β€” any signal from the competitor hits your feed. 2. Specific feature β€” only that one feature is tracked.

Email digest

Weekly digest runs automatically; recipients are all active admins. Shows SHIP / CONSIDER findings from the last 14 days across all tracked competitors.

16. Feedback + OtiumWork roadmap

Last updated 2026-07-02 Β· open as standalone page

Feedback + OtiumWork roadmap

Every employee can suggest a feature, report a bug, or ask a question. There are three places to start:

  • Topbar "Feedback β–Ύ" dropdown (every page) β†’ ✦ Suggest a feature / 🐞 Report a bug
  • Sidebar footer β€” same two links
  • /feedback directly β€” kind defaults to "feature" but is set by ?kind= query param, so the dropdown links route to the right form

What happens next depends on the kind.

Three separate boards β€” don't confuse them

  1. /roadmap β€” OtiumWork's product roadmap. Visible to all users at all tenants. Vote on what OtiumWork should build next. This is where your /feedback feature requests land.
  2. /admin/competitors/features β€” your company's internal tracker of your company's competitors (e.g., SoftInWay tracking Aspen's and NREC's feature sets for their AxSTREAM engineering product). Has nothing to do with OtiumWork as a tool. Scoped to your tenant.
  3. /admin/feedback β€” raw feedback list, admin-only. For triaging bugs and questions.

Kind = feature

When you pick "feature request" and submit:

  1. Entry saved to feedback_request table.
  2. Auto-acknowledgment email confirms receipt, linking you to /roadmap.
  3. Notification email to OtiumWork's product team.
  4. Auto-promoted to OtiumWork's roadmap β€” a matching row with competitor=[User Request], tag CONSIDER, priority 3.
  5. You (and every authenticated user at any tenant) can vote on it from /roadmap.

Voting to prioritize

On /roadmap (and also on your tenant's /admin/competitors/features for your own company's competitor features):

  • β–² N button next to every feature β€” click to cast your vote, click again to remove it. One vote per user per feature.
  • Vote counts show live and sort order lifts high-vote items to the top within each tag β€” so a CONSIDER with 12 votes surfaces above a CONSIDER with 0 votes.

The OtiumWork product team uses /roadmap vote counts to sequence what gets built next.

Kind = bug / question

Bugs and questions go through the same pipeline (notification + auto-reply) but aren't auto-promoted to the roadmap β€” they're for the OtiumWork team to act on, not for voting.

Bug-triage workflow (Claude Code agent)

Bugs flow into a dedicated workflow that uses Valentine's Claude Code session as the fix-it loop β€” not an autonomous cron, not an API agent.

  1. User submits at /feedback?kind=bug β†’ row in feedback_request (kind='bug', status='open', triage_status=NULL).
  2. Email pings the product owner.
  3. Valentine opens Claude Code and asks "any new bugs?" or runs /triage β€” the agent (this is documented in CLAUDE.md at the repo root) calls scripts/triage_bugs.py list over SSH to surface open bugs.
  4. Working through them in conversation: agent reads each bug, identifies the file, proposes a fix, Valentine says "go" or "skip".
  5. On "go": agent applies, commits, deploys, and runs triage_bugs.py fix <id> <commit_sha> '<short note>' β€” that marks the row resolved AND emails the reporter automatically (idempotent via reporter_emailed_at).
  6. For non-bugs: reject <id> 'reason' (not reproducible) or wontfix <id> 'reason' (by design). Both email the reporter.

Much of step 3–5 also runs autonomously through staging via the fix-it runner (see below); production always stays a human gate.

Staged-for-review + approvals (staging β†’ prod)

Fixes are reviewed on the staging site before they reach production. The bug view (/admin/feedback) shows where each item is:

Status Meaning
β—§ On staging Fixed on the internal staging site otiumdevelopment.softinway.com (VPN-only; branch + commit shown). Awaiting developer review.
βœ“ Dev-approved A developer verified it on staging and signed off. Waiting for the weekly prod go.
β–Ά Prod-approved The owner approved release; the promoter will ship it.
βœ“ Live Shipped to production; reporter emailed.

Who does what:

  • Developers (accounts with the developer flag) see the staging branch/commit + a "Review on staging β†—" link, and a βœ“ Dev-approve button to confirm the fix is correct.
  • The owner gets a weekly Telegram digest of dev-approved items and clicks β–Ά Approve for prod. Running deploy/promote-approved.sh then promotes just those fixes (isolated cherry-pick) and closes them.

Emails: the whole dev team (owner + developers) is auto-emailed on every new report and every status change. The person who filed the item is emailed twice: once when it's fixed on staging (pending release) and again when it goes live.

Schema on feedback_request: triage_status (triaged β†’ staged β†’ dev_approved β†’ prod_approved β†’ fixed), triage_notes, fixed_branch, fixed_commit_sha, staged_at, staged_emailed_at, dev_approved_by/at, prod_approved_by/at, resolved_at, reporter_emailed_at.

Admin view

/admin/feedback lists every submission with status. Developers additionally see the staging branch/commit, a review link, and the approve buttons described above.

Closing the loop manually

Admins can also manually close a feedback item at /admin/feedback. Submitter gets an email saying it was marked resolved, with an optional note (e.g., "Shipped in v1.6.5" or "See /help/reports β€” this already works"). Submitter can reply to the email or re-submit if they disagree.

17. Account recovery β€” what to do when you can't log in

Last updated 2026-05-28 Β· open as standalone page

Account recovery β€” what to do when you can't log in

Use the matrix below. Each row is independent β€” combine them as needed.

What you lost What to do
Password (still have email) Click Forgot password? on /login. Set a fresh one via the email link. Link expires in 1 hour.
TOTP device (still have backup codes) At the 2FA prompt, paste one of your backup codes instead of the 6-digit code. Each code works once.
TOTP device + backup codes Email your company admin. They can disable 2FA on your account from /admin/employees β†’ your row β†’ Reset 2FA. You'll re-enroll on next login.
Email account (you can no longer receive mail) Email your admin from any other channel (chat, phone). They can change your account's email at /admin/employees β†’ your row β†’ Edit.
Platform-owner account, lost everything SSH to the VPS and run python scripts/owner_emergency_unlock.py --email <owner@…> --reset-2fa --reset-password --confirm. Dry-runs without --confirm.

How backup codes work

When you enable 2FA at /me/security, OtiumWork generates 8 single-use recovery codes. They're shown once β€” print them or store them in your password manager. Each code lets you log in without your TOTP device. After 6 of the 8 are used, regenerate at /me/security.

Why platform-owner recovery needs SSH

The owner account has 3-factor sign-in (password + TOTP + emailed magic link) on top of 2FA enforcement. That's deliberate β€” but it also means there's no in-app "reset everything" button, because it would be the most valuable credential on the platform. SSH access to the VPS is treated as the root-of-trust; if you have that, you can run the break-glass script.

Related

18. 2FA β€” I lost my device

Last updated 2026-05-28 Β· open as standalone page

2FA β€” I lost my device

Three paths back in, in order of preference:

1. Use a backup code (fastest)

At the 2FA prompt, paste any one of the 8 backup codes OtiumWork showed you when you enabled 2FA. Each code works once. Format: ABCDE-FGHIJ.

If you kept the codes in a password manager or printed them: great, you're 30 seconds from logged in.

After you're back in: - Go to /me/security and regenerate a fresh set of backup codes - Set up TOTP on your new device while you're there

2. Ask your admin to reset 2FA

If you've used up all backup codes (or never saved them), email your company admin. They can clear your 2FA at /admin/employees β†’ your row β†’ Reset 2FA.

You'll then log in with just your password, and OtiumWork will walk you through fresh enrollment.

3. Platform-owner: SSH break-glass

For the platform-owner account (whoever's on OPS_ACCESS_EMAILS), neither path 1 nor 2 applies β€” the admin who'd reset 2FA is you. SSH to the VPS and:

cd /opt/timeopt
.venv/bin/python scripts/owner_emergency_unlock.py \
    --email <your-owner-email> --reset-2fa --confirm

Dry-runs without --confirm. After running, log in with your existing password. The owner-2FA enforcement middleware will immediately redirect you to /me/security to re-enroll.

Preventing the next time

  • Save backup codes in 2 places: password manager + printed copy in a locked drawer.
  • Enable TOTP on your password manager (1Password, Bitwarden, etc.) β€” that way "device loss" only happens if you lose your manager too.
  • Tell at least one trusted person (co-founder, spouse) where the backup codes live.

Related

19. I think my account is compromised

Last updated 2026-05-28 Β· open as standalone page

I think my account is compromised

Move fast. The faster you act, the less anyone can do under your identity.

Right now (under 60 seconds)

  1. Open /me/sessions (if you can still log in). You'll see every active session, with device + IP hint + last-seen time.
  2. "Revoke all" the sessions you don't recognize. If you're not 100% sure which is yours, revoke everything β€” you'll be logged out and can log back in fresh.
  3. Change your password at /me/security (or via /forgot-password if you've been kicked out). Use something you've never used elsewhere β€” 16+ characters, mixed.

Next 5 minutes

  1. Enable 2FA at /me/security if you hadn't already. Pick TOTP (Google Authenticator, Authy, 1Password, Bitwarden). Save the 8 backup codes somewhere offline.
  2. Email your admin (or, if you ARE the admin, the platform owner) β€” tell them what happened. They can check audit logs for activity under your account.
  3. Tell anyone who got messages from you in the last 24-48 hours that the messages may not have been you.

If you got the "new sign-in detected" email

That email always includes a one-click revoke link for that exact session. Click it β€” even before doing anything else. You can investigate after.

If you can't log in at all

You're locked out: someone changed your password. Go straight to /forgot-password and recover via email link. If the attacker also changed your email, contact your admin (they can change it back).

If you ARE the platform owner and the attacker reached even your owner-protected account: SSH to the VPS and run scripts/owner_emergency_unlock.py --email <you> --reset-password --reset-2fa --confirm. See account recovery.

What admins should do when a user reports compromise

  • Pull the user's recent activity from /admin/audit (filter on actor_email).
  • Look for unexpected: data exports, employee record edits, project edits, integration token re-bindings, role changes.
  • If anything looks wrong, revoke the user's API tokens (if any) and inform any affected third parties.
  • File the incident in /admin/legal β†’ Incidents so there's a paper trail.

What we've already done for you

  • Every new sign-in from a device we haven't seen for your account triggers a "new sign-in detected" email with a revoke link.
  • Failed logins are rate-limited: 5 wrong passwords within 15 minutes locks the account for 15 minutes.
  • Audit log captures every privileged action with timestamp + actor + IP hash.
  • For the platform-owner account, sign-in requires password + TOTP + an emailed magic link.
  • Inactivity timeout: sessions auto-revoke after 8 hours of no activity.

Related

20. Bulk Events

Β· open as standalone page


summary: Multi-select bulk operations on a report's events β€” re-tag, set category, consolidate, or delete in one click.

Built for the case where a rep has dozens or hundreds of small events that all need the same fix (tag a project, set a category, delete noise, consolidate into a single clean entry). Manager-of-this-employee or admin only.

Where

/team/employees/<emp_id> β†’ Events table β†’ checkbox column on the left of each row. A sticky bar appears at the bottom of the page once any row is selected.

Bulk actions in the bar

  • β†’ Assign project dropdown (validates the employee is assigned to the project before saving)
  • β†’ Set category (from the company taxonomy)
  • β†’ Set work type (billable, internal, etc.)
  • Consolidate into one β€” sums the selected events' minutes, deletes the N rows, inserts one new event with the total. Takes the first row's app/window/category as defaults; takes the earliest occurred_at as the timestamp. Sets minutes_raw to the total too so the normalize cron leaves it alone.
  • Delete β€” permanent, with confirm dialog

What it does NOT include

  • Bulk minutes edit β€” intentionally absent. Bulk minutes is meaningless because each event represents a different captured interval; setting them all to "5 min" or "60 min" double-counts. If you want one clean entry of N minutes representing what was really 75 small events, use Consolidate into one instead.

Auth + safety

  • Manager-of-this-employee OR admin only (owner can't bulk-edit their own β€” they have to do per-event edits via My Events)
  • If any selected event is in an approved/locked week, the WHOLE batch aborts with an error β€” reopen the timesheet first
  • Every change records classification_source = 'manager_correction' or 'admin_correction'
  • audit_log captures action + event ids + count

21. Connect your Outlook calendar

Β· open as standalone page

Connect your Outlook calendar

OtiumWork can automatically pull your Microsoft 365 / Outlook meetings into your timeline so the time you spend in meetings shows up as events without you having to log them manually.

How to connect

  1. Open My Day (or any page where your events list is visible).
  2. Click Connect Outlook (button appears in the integrations strip if you don't already have a connection).
  3. You'll be redirected to Microsoft's sign-in page. Use the same Microsoft account that hosts your work calendar.
  4. Approve the requested permissions (read your calendar, read your profile so we know which mailbox you connected).
  5. Microsoft redirects you back to OtiumWork. You'll see a green "Outlook connected for your.email@company.com" banner.

What gets imported

After connecting, click Sync in the integrations strip to pull the last 7 days of meetings. After the first manual sync, OtiumWork runs a background sync nightly so future meetings appear automatically.

Each meeting becomes an event with: - Start + end time from the Outlook entry - Title from the meeting subject - Category / work-type auto-suggested (you can recategorise later) - Marked as source = outlook so you can tell auto-imported events apart

What does NOT get imported

  • Cancelled meetings
  • All-day events without a specific time range
  • Meetings older than the sync window (default 7 days back)
  • Meeting body / attendee list / attachments β€” we only read the time + title

Disconnect

Same integrations strip β†’ Disconnect Outlook. This deletes the stored refresh token and stops future syncs. Previously-imported events stay in your timeline (they're just normal events at that point).

Troubleshooting

"Your admin hasn't configured Microsoft credentials yet"

Your company hasn't set up the Microsoft 365 app registration. An admin needs to go to Admin β†’ Settings β†’ Microsoft credentials and enter the tenant ID + client ID + client secret. After that you can re-try Connect.

"approval request sent" / scopes need admin consent

Some Microsoft tenants require an admin to pre-approve the OtiumWork app's permissions. Ask a tenant admin to: 1. Open Azure Portal β†’ App registrations β†’ OtiumWork 2. Go to API permissions 3. Click Grant admin consent for [your tenant]

After admin consent is granted, you can click Connect again and it'll go through.

"Microsoft did not return a refresh_token"

Means the offline_access scope wasn't granted. Disconnect, then connect again and make sure you don't uncheck any scope on the consent screen.

"Your sign-in session expired before Microsoft redirected back"

You opened the Microsoft sign-in in a different tab or browser. Click Connect again and complete the prompts in the same tab.

Events imported but in wrong time zone

OtiumWork uses your profile's time zone. Check My Day β†’ settings β†’ Time zone. If it's set to UTC, switch to your actual zone and re-sync.

Privacy

OtiumWork only requests two Microsoft Graph scopes: - Calendars.Read β€” read your calendar entries (start/end/title only) - User.Read β€” read your basic profile so we display which account is connected - offline_access β€” allows nightly background sync without you having to re-sign in every time

We never request mailbox / email contents, contact list, or write access to your calendar.

For admins setting this up the first time

See Admin Settings β†’ Microsoft credentials for the one-time tenant config. You'll need: - Microsoft Entra ID tenant - App registration with redirect URI: https://otiumwork.com/admin/integrations/outlook/callback - Client ID + Client secret - Above three Graph permissions added + admin-consented

After that, every employee can self-serve Connect without your involvement.

22. Customer Tracker

Β· open as standalone page


summary: Roster of everyone who ever used your product, with daily Apollo job-change scans and a weekly digest of detected moves.

The Customer Tracker (called AxSTREAM Users for SoftInWay; renameable per-tenant under Admin β†’ Settings) keeps a list of every contact who has touched your product, then quietly watches for job changes via the Apollo people-match API.

Where

Sidebar β†’ Sales β†’ Customer Tracker (admins + managers only)

Adding a contact

Click + Add someone. Required fields:

  • Email β€” used as the unique key + the Apollo lookup target
  • Original company β€” what to compare future Apollo data against; if this drifts, the row gets a "⚠ changed" badge

Optional:

  • First / last name, salesperson assignment, "Currently using your product" checkbox, free-text notes

The salesperson dropdown auto-populates from active employees in the Sales department (falls back to all active employees if the company hasn't created a Sales department yet).

Tabs

  • All β€” everyone on the list
  • Current users β€” is_current_user = 1 rows (your active customer book)
  • Former users β€” churned / never paid / inactive
  • Changed (need attention) β€” rows where Apollo or SF data shows the contact has moved to a new company since you added them. Click Dismiss to acknowledge.

Buttons per row

  • πŸ” Check β€” manual Apollo refresh for that one contact (~1 credit)
  • Dismiss β€” clears the changed badge for now; future drift re-flags it
  • Γ— delete β€” admin only; permanent removal

Background scans (no UI, runs daily)

A daily cron at 13:30 UTC scans the oldest-synced contacts via Apollo, capped at 60 credits/day in batches of 32 (override the cap by editing customer_tracker_scan_state.daily_cap). At 60/day a roster of 3,500 takes about 60 days to fully refresh.

A weekly digest fires every Monday at 14:00 UTC β€” admins receive a list of contacts whose company drifted since the previous Monday, grouped by salesperson.

Requirements

  • The tenant must have an Apollo API key set under Admin β†’ Sales AI configuration β†’ Apollo API key (encrypted at rest with the OTIUM_SECRETS_KEY). Without it, the daily scan and the manual πŸ” Check button both no-op with a warning.

23. Engineering AI

Β· open as standalone page


title: Engineering AI (tech support) summary: How customer-facing engineers use OtiumWork's tech-support flavor of Peter β€” drafts tuned for resolution, customer-meeting notes that mirror to Salesforce.


Engineering AI

OtiumWork's Engineering section is the tech-support flavor of the Peter AI assistant. Sales reps use Peter to nurture prospects through the Challenger sale; engineers use the same plumbing but with a different prompt scaffold β€” one that's tuned to resolve technical questions, reference internal docs, and stay clear of sales moves.

Eligibility: anyone with employee.persona_mode = 'engineering' (or admins). Set by an admin from the employee record. Developer accounts (the is_developer "dev user type") also get the TPOC View β€” it's a technical team dashboard β€” but not the sales-tuned surfaces (Drafts, Customer meetings, Engineering AI config).

Four surfaces

TPOC View (/engineering/tpoc-view)

The shared team dashboard for tech-point-of-contact assignments. Each engineer's accounts are grouped together with at-a-glance health columns:

  • Last touch (resource-center activity of the first listed contact) plus a separate Real last touch column you fill in by hand for the actual customer touch β€” meeting, call, on-site. Both are colour-coded green (≀30d) / amber (31-89d) / red (β‰₯90d).
  • Next meeting is auto-joined from your Outlook calendar β€” any future external-attendee meeting whose attendee matches the row's POC email or domain.
  • Tech support expires with days-to-expiration pill.
  • Filter pills at the top: Tech support (Active / Expiring ≀90d / Expired) and SF match (No SF match / Loose match) for quick triage.

Per-row buttons:

  • πŸ”§ β€” Set or clear the SF Account override. Use this when the auto-matcher gets the wrong Account (or none).
  • ✎ β€” Edit any field. Salesperson / Tech support expiration / Comments push through to Salesforce. Real last touch / priority / POC name stay local.
  • ✨ β€” AI-generated 5-bullet account briefing. Pulls row data + extra contacts + last 5 SF Tasks, asks Claude Haiku for a tight summary. Cached on the row; click Regenerate to refresh.
  • πŸ‘₯ β€” Manage all contacts at the account beyond the primary POC. SF Contact ID auto-resolves from email where possible. The number shows how many extra contacts are tracked.
  • βœ‰ β€” Compose and send an email from your connected Outlook mailbox (saved to your Sent Items).
  • πŸ“ β€” View the last 10 SF Tasks on the account and add a new "TPOC note" Task inline. Single source of truth: notes live in Salesforce, not in OtiumWork.
  • πŸ—‘ β€” Remove the TPOC row (doesn't touch Salesforce).

Customer meetings (/engineering/meetings)

Customer-attended meetings synced from your Outlook calendar β€” past 30 days and next 14 days. The list is split into Past (most recent first) and Upcoming (soonest first). Any meeting with at least one external attendee (non-@<company>.com address) shows up.

  • Add notes per meeting in the inline text box. Saved locally so you keep a personal resolution log.
  • Save & sync to SF mirrors the notes to Salesforce as a Task on the matched record:
  • First we try exact email match β†’ SF Lead/Contact (Task on Contact + Account).
  • If no Contact exists, we fall back to domain match β†’ SF Account (Task attaches to the Account directly). Common case: SoftInWay has the Account in SF but the specific engineer the customer sent isn't yet a Contact.
  • Re-saving updates the same Task (idempotent via work_event.sf_activity_id).
  • Hide filters personal reminders that aren't real customer meetings (e.g. "pick up the kids" with a single external attendee). Click "Show hidden" to recover.

Salesforce sync uses the company-level SF connection β€” engineers don't need to connect their own SF. All engineering meeting Tasks attribute to the shared tenant SF token.

Drafts (/engineering/drafts)

Peter watches your inbox and drafts tech-support replies in your voice (see Voice tuning below). Drafts land in your Outlook Drafts folder β€” you review, edit, send.

Connect once at the top of the page (separate OAuth from the calendar Outlook β€” needs Mail.ReadWrite + Mail.Send scopes so Peter can write drafts on your behalf).

  • Autonomy controls under the disclosure:
  • 0 β€” Drafts only. Peter never sends. Everything goes to your Drafts.
  • 1 β€” Drafts + ask. Same as 0 today; reserved for an approval queue later.
  • 2 β€” Auto-send routine replies. Peter sends low-stakes replies automatically; novel or higher-stakes stays in Drafts.
  • Auto-draft toggle: turn off if you want the connection live (for sent-items scraping + command answering) but no inbound-reply drafts.

The drafts you see in Recent activity are status snapshots β€” drafted, auto-sent, skipped, not a lead. "View in Outlook" opens the actual draft for editing.

Engineering AI config (/engineering/config)

Two fields, both tune what Peter writes for you:

  • Engineering knowledge base (company-wide) β€” product docs, FAQs, version gotchas, escalation paths. Visible to every engineering rep on the tenant. Lives in sales_ai_company_config.engineering_kb.
  • Personal style notes β€” your voice, tone, escalation preferences. Same column as the sales-side per-rep style (employee.personal_style_notes).

Voice tuning

A new engineer can generate a draft of their personal_style_notes from their last 6 months of customer-facing sent mail. Pattern (one-shot, admin-run):

  1. Connect Sales-AI Outlook.
  2. Admin runs scripts/seed_<rep>_demo_drafts.py (or the voice-analysis script). Pulls ~30-50 external sent emails, asks Claude to extract tone / structural patterns / recurring phrases / tech-support reflexes, returns a ready-to-paste markdown block.
  3. Paste into /engineering/config β†’ Personal style notes β†’ Save.
  4. Optional: seed a handful of [Demo]-prefixed drafts in the engineer's Outlook Drafts so they can preview what Peter-in-their-voice produces before a real inbound arrives.

Differences from sales Peter

Sales (default) Engineering
System prompt Challenger framework, qualifying questions, next-step propose Resolve technical question, reference KB, escalate when needed
Calendar link Push when prospect signals interest Never push (no "grab time on my calendar")
Sales-shaped inbounds Engage / qualify Punt to Sales team gracefully
KB source sales_ai_company_config.knowledge_base sales_ai_company_config.engineering_kb
Pricing / commercial Engage (Sales rep follow-up) Never quote; route to peter@ or Sales

Operating notes

  • The same desktop client / calendar sync / Outlook OAuth infrastructure powers both modes β€” no separate setup beyond connecting one extra Outlook scope for the drafts pipeline.
  • Engineering meeting Tasks created in Salesforce attribute to the company SF user, not the individual engineer. Engineers are named in the Task description for traceability.
  • The Peter poll runs every ~5 min; drafts for new inbound external emails appear within minutes.

SoftInWay Services API + VPN

Configure tenant access at Network β†’ Network & VPN (sidebar). This page is developer-gated: anyone with the is_developer flag can manage it without holding the full admin role (admins can reach it too). It deliberately lives off the finance-gated Admin β†’ Settings page so an engineer who configures IP/tunnels doesn't need broad admin rights. To grant it, an admin sets a person's developer flag in Admin β†’ Employees β†’ Edit (the dev flag also removes them from people-analytics and finance β€” it's meant for technical/tooling access).

  1. API URL + key β€” what OtiumWork uses when making calls (e.g. for tech-support date sync).
  2. VPN tunnels β€” add one tunnel per network you need to reach (prod, dev, staging, …). Each row needs:
    • Slug β€” short id used in the systemd unit name (e.g. prod β†’ wg-quick@wg-svc-prod)
    • Label β€” human-friendly name shown in the admin table
    • Type β€” WireGuard or OpenVPN
    • Config β€” full .conf / .ovpn body
    • Enabled β€” uncheck to keep the row but stop bringing it up

Saving a tunnel doesn't immediately apply it on the VPS β€” it's stored in the DB. The next bash deploy/auto-deploy.sh reconciles: it walks the table, writes configs to /etc/wireguard/ and /etc/openvpn/client/, and brings the matching systemd units up. Tunnels deleted in the UI are torn down on the next deploy. No manual SSH required.

If you need the tunnel up right now (without waiting for the next deploy), just run bash deploy/auto-deploy.sh from your local box β€” it's the same script that ships code, and it includes the VPN reconcile step at the end.

24. Manager Event Edit

Β· open as standalone page


summary: Managers and admins can fix one of their reports' time entries β€” including minutes β€” from the employee detail page.

Closes the long-standing gap that an employee was the only person who could correct their own captured events. Now an admin (company-wide) or a manager (their direct reports only) can surgically fix one event at a time, including the Minutes field that was previously read-only post-capture.

Where

/team/employees/<emp_id> β†’ Events table β†’ click Edit on any row

Auth model

Role Their own events Reports' events Other employees' events
Owner (employee) βœ“ category/type/note/project β€” β€”
Manager βœ“ (no minutes) βœ“ full edit 403
Admin βœ“ (no minutes) βœ“ full edit βœ“ full edit

Minutes editing rules

  • Owners cannot change their own minutes via this surface (use the My Events page, which deliberately doesn't expose a Minutes input)
  • Managers + admins can change minutes; the change overwrites both minutes AND minutes_raw so the hourly normalize cron doesn't undo the edit on its next pass
  • Locked weeks (timesheet already approved by a manager) abort the edit with HTTP 423 β€” reopen the timesheet first

Audit trail

Every edit records:

  • work_event.classification_source = manager_correction or admin_correction (vs employee_correction for self-edits)
  • audit_log row capturing the actor's email + role + which fields changed

Employees can see who touched their data by checking their work_event.classification_source on the My Events page (column shown when value isn't auto or employee_correction).

Bulk operations

For multi-row work, use the checkbox column on the same employee events table β€” see bulk-events.md.

25. Profitability Ai Estimator

Β· open as standalone page


summary: Per-project P&L (cost / budget / invoiced / paid / margin) plus an on-demand Claude API estimate of what the same work would cost if delivered by AI agents.

Lives at /projects/profitability (admin or has-finance-access only). Managers see a hours-only variant on the same URL β€” costs are hidden for them automatically.

Top KPIs (full view, admin/finance)

  • Hours β€” total tracked hours in the date range
  • Total cost β€” SUM(hours Γ— employee.hourly_cost)
  • Total budget β€” SUM(project.budget_dollars)
  • Invoiced β€” SUM(invoice.total) for non-void invoices linked to projects
  • Paid β€” SUM(invoice_payment.amount) for those invoices
  • Margin (paid βˆ’ cost) β€” your true P&L for what's actually been collected

Per-project columns

Column What
Hours logged time
Cost hours Γ— hourly_cost per member
Budget project.budget_dollars
Invoiced sum of project's invoices
Paid actual payments received
Margin (paid) paid βˆ’ cost, color-coded green/red
vs Budget cost / budget %, green if ≀100%
AI cost est. see below

Project filter + drill-down

Pick a project from the dropdown to enter single-project view. You get three additional drill-down panels:

  • Top apps β€” which Windows apps consumed time
  • Top categories β€” meeting / docs / coding / etc.
  • Top window titles β€” what was actually being done (15 most time-heavy unique titles)

Useful when you want to understand WHY a project's cost is what it is, not just see the number.

AI cost estimate

Per-project "Estimate β†’" button calls Claude Haiku 4.5 with the project's name, description, top apps, top categories, sample window titles, and total hours. Returns:

  • Low / Median / High USD estimates of what equivalent output would cost via Claude API agents
  • 1-3 sentences of reasoning about WHY that range
  • A savings ratio (X.Xx cheaper via API) calculated from cost / median_estimate

Refresh + caching

The estimate is cached per project in project_ai_estimate and re-uses the cached value until the project's total minutes drift more than 20% from the cached snapshot. A small refresh link below the estimate forces a fresh computation.

Honest caveats

  • This is a heuristic, not a budgeting tool. Best for narrative ("this $30K project would be ~$200 via API agents"), not a price quote.
  • Quality depends on how well-tagged the events are. Untagged piles produce garbage estimates. Use the bulk-tag operations on /team/employees/<id> to tag historical work first if needed.
  • Cost is across ALL hours (billable + internal). The legacy CSV export still uses billable-hours-only revenue.

CSV export

The Download CSV button in the filter bar honors the date range + project filter. Exports per-member rows so spreadsheets can do their own grouping/pivoting.

26. Teams

Β· open as standalone page


summary: Hierarchical sub-teams + multi-membership β€” manage who belongs to which team and how teams nest.

OtiumWork's team model supports hierarchy (a parent team containing sub-teams) and multi-membership (one person belongs to many teams, each with a role). The legacy single-string employee.department field is still updated as a back-compat shim β€” every employee has exactly one primary team that mirrors into it.

Where

Admin β†’ Teams (/admin/teams) β€” admin-only management.

Listing page

Shows every team in the company with its parent (or β€” for top-level), member count, manager count, and an "active / inactive" pill. A small form at the top creates a new team.

Per-team detail page

  • Rename / change parent / delete (delete only allowed when the team has zero members and zero sub-teams)
  • Members table with per-row controls:
  • Role dropdown β€” member or manager (instant save)
  • make primary β€” set this team as the employee's primary; clears any other primary they had + updates employee.department to this team's name
  • Remove β€” pulls them off this team
  • Add an employee form β€” pick from active company employees not already on this team, choose role, optionally make this their primary

Hierarchy example (SoftInWay, as of today)

AxSTREAM Development                  ← parent
  β”œβ”€β”€ AxSTREAM AI Development Team    ← sub-team
  └── AxSTREAM ION Development Team   ← sub-team

Valentyn Barannik is the manager of all three β€” assigned to AI as his primary, also a manager-rank member of ION and the parent.

Filtering by team in reports

When you pick a parent team in the report's department filter, the filter automatically expands to include every sub-team beneath it. Picking "AxSTREAM Development" matches anyone whose primary team is "AxSTREAM AI Development Team" or "AxSTREAM ION Development Team" too. Picking a sub-team directly matches only that sub-team's members.

What still uses the old employee.department field

Some readers (utilization rollups, optimization grouping, the employee-edit form) still read employee.department rather than employee_team. They keep working because the team admin UI mirrors the primary team's name into that field on every membership change. A future Phase 3 will migrate those readers to query employee_team directly.