To integrate Time Doctor with V0 by Vercel, generate a remote team dashboard with V0, create a Next.js API route that calls the Time Doctor API using OAuth2 tokens, store credentials in Vercel environment variables, and deploy. Your app can display time logs, productivity scores, and team activity without exposing API secrets to the browser.
Build Remote Team Monitoring Dashboards with Time Doctor and V0
Time Doctor is widely adopted by remote-first companies who need more accountability than simple calendar-based tracking — its time logs, productivity scores, and optional screenshot monitoring give managers granular visibility into how remote teams spend their working hours. For companies that already use Time Doctor, integrating its API into a V0-generated dashboard means you can build custom reporting views that combine Time Doctor data with other business metrics, creating unified management dashboards without switching between tools.
Time Doctor's API provides access to user lists, time entries, project data, productivity reports, and activity summaries. The authentication uses OAuth2 — you exchange your Time Doctor company credentials for an access token, then include that token as a bearer header in all subsequent API calls. The API is version-based with v1 using Basic Auth (deprecated) and the newer v2 API using OAuth2 tokens via the api2.timedoctor.com endpoint. For single-company integrations, the simplest approach is to generate a token once through the OAuth flow and store it as a Vercel environment variable, using it to build company-wide reports.
V0 excels at generating the kind of data-heavy management interfaces that remote team dashboards require: sortable employee time tables, productivity score bar charts, project time allocation pie charts, and daily/weekly summary cards. Describe the specific metrics your management team cares about — hours worked today, productivity percentage, top projects by time, or team members who haven't logged time — and V0 generates the visual components. The Next.js API route handles the secure data fetch from Time Doctor, transforms the data as needed, and returns it in a shape optimized for your dashboard components.
Integration method
Time Doctor integrates with V0-generated Next.js apps through server-side API routes that call the Time Doctor REST API using OAuth2 bearer tokens. Your access credentials are stored as server-only Vercel environment variables and never reach the browser. V0 generates the remote team management dashboards and productivity reports; Next.js API routes securely fetch time logs, productivity data, and team member activity from the Time Doctor API. This enables managers to build custom reporting views on top of their existing Time Doctor account data.
Prerequisites
- A Time Doctor account with company admin access at timedoctor.com — API access is available on Standard and higher plans
- Time Doctor API v2 credentials: your company ID and an OAuth2 access token — obtain these from Time Doctor Settings → Company → Integrations → API
- At least one team member tracked in Time Doctor with existing time entries to display in your dashboard
- A V0 account at v0.dev and a Vercel account for deployment
- Node.js installed locally for running the development server and testing API routes before deploying
Step-by-step guide
Generate the Team Dashboard UI with V0
Generate the Team Dashboard UI with V0
Open V0 at v0.dev and describe the remote team monitoring dashboard you want to build. Time Doctor integrations typically focus on one of three views: a live team status board (who is active right now), a daily or weekly time summary report, or a productivity analytics dashboard with trend charts. Be specific with V0 about the data fields you will receive from Time Doctor: user name, total work time in seconds (convert to hours in your API route before returning), productivity score as a percentage, project names, and the time period being summarized. Tell V0 the exact API route paths your components will call (/api/timedoctor/users for team member lists, /api/timedoctor/reports for time data) and the expected response shapes. For dashboards with charts, mention that you want to use Recharts (which V0 uses by default for data visualization) and describe the chart type and data encoding explicitly. V0 generates the component with Tailwind CSS and proper loading skeletons for data tables automatically. Include descriptions of how status indicators should look — colored dots for active/inactive, colored badges for productivity score ranges. After generating the dashboard, push to GitHub via V0's Git panel to trigger a Vercel preview deployment.
Build a remote team management dashboard. Top section: three metric cards showing Total Hours This Week (with hours and change vs last week), Active Members Now (count with green indicator), and Team Avg Productivity (percentage with up/down trend). Below: a data table showing team members with columns: Name, Today's Hours, This Week's Hours, Productivity Score (colored badge), Last Active (time ago), and Status (dot indicator). Add a refresh button and show last updated timestamp. Data from GET /api/timedoctor/team-summary. Use a clean dark-mode-friendly design with a slate background and colored status indicators.
Paste this in V0 chat
Pro tip: Time Doctor returns time values in seconds — convert to hours in your API route before returning to the frontend to keep the V0-generated component code simple. Format as 'Xh Ym' (e.g., '3h 45m') for a cleaner dashboard display.
Expected result: A remote team dashboard renders in V0's preview with metric summary cards, team member activity table, and productivity score badges. The component fetches from /api/timedoctor/team-summary on mount with loading skeleton states.
Obtain Time Doctor API Credentials and Create API Routes
Obtain Time Doctor API Credentials and Create API Routes
Time Doctor uses OAuth2 for its v2 API at api2.timedoctor.com. To get an access token: log in to your Time Doctor account, go to Settings → Company → Integrations → API Access, and generate an access token. Alternatively, use the OAuth2 password grant flow by POSTing to https://webapi.timedoctor.com/oauth/v2/token with your Time Doctor email, password, client_id (1_3bc3-public), and grant_type=password — this returns an access_token and refresh_token. Store the access_token as TIMEDOCTOR_ACCESS_TOKEN in your .env.local file for local testing. The v2 API base URL is https://webapi.timedoctor.com/v1.1 (note: the 'v1.1' path is used for what Time Doctor calls their v2 API in current documentation). Key endpoints include: /companies (get your company ID), /companies/{companyId}/users (team member list), /companies/{companyId}/worklogs?start_date={date}&end_date={date} (time logs), and /companies/{companyId}/productivity (productivity scores). The company ID is included in the /companies response and must be stored as TIMEDOCTOR_COMPANY_ID. Create a unified route that handles different report types based on a query parameter to keep the route structure clean.
1// app/api/timedoctor/team-summary/route.ts2import { NextResponse } from 'next/server';34const TD_API_BASE = 'https://webapi.timedoctor.com/v1.1';56function getTDHeaders(): Record<string, string> {7 return {8 Authorization: `Bearer ${process.env.TIMEDOCTOR_ACCESS_TOKEN}`,9 'Content-Type': 'application/json',10 };11}1213function secondsToHoursMinutes(seconds: number): string {14 const hours = Math.floor(seconds / 3600);15 const minutes = Math.floor((seconds % 3600) / 60);16 return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;17}1819export async function GET() {20 const token = process.env.TIMEDOCTOR_ACCESS_TOKEN;21 const companyId = process.env.TIMEDOCTOR_COMPANY_ID;2223 if (!token || !companyId) {24 return NextResponse.json(25 { error: 'Time Doctor is not configured — set TIMEDOCTOR_ACCESS_TOKEN and TIMEDOCTOR_COMPANY_ID' },26 { status: 500 }27 );28 }2930 try {31 const today = new Date().toISOString().split('T')[0];32 const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)33 .toISOString()34 .split('T')[0];3536 // Fetch users and work logs in parallel37 const [usersResponse, worklogsResponse] = await Promise.all([38 fetch(`${TD_API_BASE}/companies/${companyId}/users`, {39 headers: getTDHeaders(),40 }),41 fetch(42 `${TD_API_BASE}/companies/${companyId}/worklogs?start_date=${weekAgo}&end_date=${today}&detail=project`,43 { headers: getTDHeaders() }44 ),45 ]);4647 if (!usersResponse.ok || !worklogsResponse.ok) {48 throw new Error('Failed to fetch Time Doctor data');49 }5051 const usersData = await usersResponse.json();52 const worklogsData = await worklogsResponse.json();5354 const users = usersData.users || [];55 const worklogs = worklogsData.worklogs?.data || [];5657 // Aggregate time by user58 const userTimeMap: Record<string, { today: number; week: number }> = {};59 const todayStr = today;6061 for (const log of worklogs) {62 const userId = String(log.userId);63 if (!userTimeMap[userId]) userTimeMap[userId] = { today: 0, week: 0 };64 userTimeMap[userId].week += log.time || 0;65 if (log.date === todayStr) {66 userTimeMap[userId].today += log.time || 0;67 }68 }6970 const teamMembers = users.map((user: any) => {71 const times = userTimeMap[String(user.id)] || { today: 0, week: 0 };72 return {73 id: user.id,74 name: user.fullName || user.name,75 email: user.email,76 todayHours: secondsToHoursMinutes(times.today),77 weekHours: secondsToHoursMinutes(times.week),78 isActive: user.online || false,79 lastActive: user.lastActivity || null,80 };81 });8283 const totalWeekSeconds = Object.values(userTimeMap).reduce(84 (sum, t) => sum + t.week,85 086 );8788 return NextResponse.json({89 teamMembers,90 summary: {91 totalWeekHours: secondsToHoursMinutes(totalWeekSeconds),92 activeNow: teamMembers.filter((m: any) => m.isActive).length,93 totalMembers: teamMembers.length,94 },95 });96 } catch (error) {97 const message = error instanceof Error ? error.message : 'Unknown error';98 console.error('Time Doctor API error:', message);99 return NextResponse.json({ error: message }, { status: 500 });100 }101}Pro tip: Use Promise.all to fetch users and work logs in parallel rather than sequentially — this cuts the API route response time roughly in half when both requests need to complete before returning data.
Expected result: GET /api/timedoctor/team-summary returns a team member list with today's hours, week hours, and active status, plus summary metrics for the dashboard header cards.
Configure Environment Variables and Deploy to Vercel
Configure Environment Variables and Deploy to Vercel
Set up Time Doctor credentials in Vercel and complete the production deployment. Open the Vercel Dashboard, navigate to your project, and go to Settings → Environment Variables. Add TIMEDOCTOR_ACCESS_TOKEN with your Time Doctor OAuth2 access token obtained in the previous step. Add TIMEDOCTOR_COMPANY_ID with your company's numeric ID from the Time Doctor API — retrieve it by calling GET https://webapi.timedoctor.com/v1.1/companies with your access token; the company ID is in the response. Neither variable should have the NEXT_PUBLIC_ prefix — both are server-side only. Set variables for Production, Preview, and Development scopes. For local development, add the same variables to .env.local and run npm run dev to test the dashboard with live Time Doctor data before deploying. One important consideration: Time Doctor access tokens expire and need to be refreshed. If you get consistent 401 errors after initial setup, your access token may have expired. Re-run the OAuth flow (POST to the token endpoint with your credentials) to generate a fresh token and update the Vercel environment variable. After saving variables in Vercel, trigger a redeployment from the Deployments tab and test the live dashboard. For teams requiring ongoing token refresh automation, RapidDev can help implement a refresh token rotation system that updates Vercel environment variables automatically when tokens expire.
Add a date range selector to the team summary dashboard with quick-select buttons for Today, This Week, Last Week, and This Month. When a range is selected, re-fetch from GET /api/timedoctor/team-summary?start=...&end=... and update all metrics and table data. Show a loading overlay on the table while the new data loads. Keep the last successful data visible during the refetch rather than showing a full skeleton. Add a Last updated timestamp in the top right corner.
Paste this in V0 chat
Pro tip: Time Doctor access tokens have a limited lifespan. If you see 401 errors after the initial successful deployment, you need to refresh your access token and update TIMEDOCTOR_ACCESS_TOKEN in Vercel. Consider setting a calendar reminder to refresh the token before it expires if you are not implementing automatic token refresh.
Expected result: The Vercel-deployed dashboard displays live Time Doctor team activity data. The team member table shows real hours and status from your Time Doctor account. The integration works end-to-end in production.
Common use cases
Daily Team Activity Summary
A morning dashboard that shows the current day's time tracking status for all team members: who has started tracking, total hours logged so far today, and productivity scores. V0 generates the team status table with color-coded indicators; a Next.js API route fetches today's time data from Time Doctor.
Build a daily team status dashboard. Show a summary row at the top: Active Now (count), Total Hours Today, Average Productivity Score. Below, a table of team members with columns: Name, Status (Active/Inactive with green/grey dot), Hours Today, and Productivity Score as a colored percentage badge (green 80%+, yellow 60-79%, red below 60%). Data from GET /api/timedoctor/daily-summary. Add an auto-refresh every 5 minutes. Use a clean remote work dashboard design.
Copy this prompt to try it in V0
Weekly Project Time Report
A weekly report showing how team hours are distributed across projects. V0 generates a stacked bar chart by project with a supporting data table; the API route aggregates time entries from the Time Doctor API grouped by project for the selected week.
Create a weekly project time report page. Show a week selector (default current week). Display a horizontal stacked bar chart showing hours per project for each team member. Below the chart, show a table with columns: Project, Total Hours, Team Member, and % of Work Week. Add a CSV export button. Data from GET /api/timedoctor/weekly-report?start=...&end=.... Use a professional report design with a blue color scheme and clear data labels on the chart.
Copy this prompt to try it in V0
Productivity Score Trends Dashboard
A management analytics page showing productivity score trends over the past 30 days for the whole team or individual members. The trend line chart shows daily average productivity scores; a filter lets managers drill into individual team members to see their personal trend.
Design a productivity trends page. At the top, a team member selector dropdown (All Team + individual names). Below, a line chart showing daily productivity scores for the last 30 days. If All Team is selected, show the team average. If an individual is selected, show their personal daily scores. Show a trend indicator (up/down arrow + % change from previous 30 days) next to the chart. Below the chart, show days with productivity below 60% highlighted in red. Data from GET /api/timedoctor/productivity?userId=...&days=30.
Copy this prompt to try it in V0
Troubleshooting
API returns 401 Unauthorized for all Time Doctor requests
Cause: The TIMEDOCTOR_ACCESS_TOKEN is expired, invalid, or not set correctly in Vercel. Time Doctor OAuth2 access tokens expire and must be refreshed.
Solution: Re-authenticate with Time Doctor's OAuth endpoint (POST to https://webapi.timedoctor.com/oauth/v2/token with your credentials) to obtain a fresh access token. Update TIMEDOCTOR_ACCESS_TOKEN in Vercel → Settings → Environment Variables, then trigger a redeployment. Consider storing the refresh_token as well to automate token renewal.
1// Refresh the access token2const response = await fetch('https://webapi.timedoctor.com/oauth/v2/token', {3 method: 'POST',4 headers: { 'Content-Type': 'application/json' },5 body: JSON.stringify({6 grant_type: 'password',7 client_id: '1_3bc3-public',8 username: process.env.TIMEDOCTOR_EMAIL,9 password: process.env.TIMEDOCTOR_PASSWORD,10 }),11});12const { access_token } = await response.json();Work logs endpoint returns empty data even though team members have tracked time
Cause: The date range in the start_date and end_date parameters does not overlap with when work was tracked, or the company ID is incorrect.
Solution: Verify the TIMEDOCTOR_COMPANY_ID by calling GET /v1.1/companies and checking the response. Confirm the date range format is YYYY-MM-DD. Test with a wide date range like the last 30 days to confirm data exists before narrowing to today or this week.
Time Doctor API returns 403 Forbidden with 'insufficient permissions'
Cause: The user account used to generate the access token does not have manager or admin access to view other team members' time data.
Solution: Generate the access token using a Time Doctor account with company admin or manager role. Regular employee accounts can only access their own time data — viewing team-wide reports requires admin privileges. Check the user's role in Time Doctor Settings → Team.
Dashboard shows 0 hours for all team members even though worklogs returns data
Cause: The userId field in the worklog entries may be a string or number type that does not match when used as an object key for aggregation.
Solution: Normalize user IDs to strings before using them as object keys — convert both the worklog userId and the users list ID to String() before comparing or indexing. Type mismatches between numeric and string IDs are a common source of data aggregation bugs.
1// Ensure consistent string keys for user ID mapping2const userId = String(log.userId);3const userTimeMap: Record<string, { today: number; week: number }> = {};Best practices
- Store TIMEDOCTOR_COMPANY_ID as an environment variable rather than hardcoding it — this makes it easy to point the dashboard at a different Time Doctor account for staging or testing
- Convert Time Doctor's seconds-based time values to hours and minutes in the API route, not in the frontend — this keeps V0-generated component code cleaner and avoids duplication
- Use Promise.all to fetch users and time data in parallel where possible — sequential API calls double the dashboard load time unnecessarily
- Add cache headers to your team summary route for 2-5 minutes — team activity data does not need to be real-time for most management use cases, and caching reduces Time Doctor API quota consumption
- Handle the case where team members have zero hours logged — show '0h' rather than empty cells or undefined to keep the dashboard readable
- Implement token refresh logic using the stored refresh token so dashboards do not break silently when access tokens expire
- For sensitive productivity data, add authentication to your dashboard route — time tracking data is sensitive employee information that should not be publicly accessible
Alternatives
Use Harvest instead of Time Doctor if your team needs time tracking tied directly to client billing and invoicing — Harvest integrates project time with invoice generation while Time Doctor focuses on employee productivity monitoring.
Choose Toggl Track over Time Doctor if your team prefers voluntary time tracking without screenshot monitoring — Toggl has a simpler API and is more employee-friendly for teams that find screen monitoring intrusive.
Use Everhour over Time Doctor if your team is primarily in Asana, Jira, or Trello and wants time tracking integrated directly into project management tools rather than as a standalone monitoring platform.
Frequently asked questions
Does Time Doctor have a free tier that allows API access?
Time Doctor's API is available on Standard and higher plans, not on the free trial. The Standard plan starts at $7 per user per month when billed annually. API documentation and credentials are accessible through the Time Doctor web interface under Settings → Company → Integrations once you have an active paid subscription.
How do I get my Time Doctor company ID?
After obtaining an access token, call GET https://webapi.timedoctor.com/v1.1/companies with the Authorization: Bearer {token} header. The response includes a companies array where each item has an id field — this is your company ID. Store it as TIMEDOCTOR_COMPANY_ID in your Vercel environment variables.
Can I access screenshot data through the Time Doctor API?
Yes — Time Doctor's API includes endpoints for screenshots if your plan includes screenshot monitoring. The screenshots endpoint returns URLs to screenshots organized by user and time period. Be aware of your local employment laws regarding screenshot monitoring — in some jurisdictions, employee consent and disclosure requirements apply.
How often does Time Doctor update its API data?
Time Doctor updates work log data in near real-time as employees track time. The active status (online/offline) and current work log updates happen within a few minutes of changes. For dashboards showing live status, refreshing data every 5 minutes is typically sufficient without placing excessive load on the Time Doctor API.
Can I filter Time Doctor data by specific projects or tasks?
Yes — the work logs endpoint supports filtering by project ID using the project_ids parameter. First fetch the project list via GET /v1.1/companies/{companyId}/projects to get project IDs, then pass project_ids[]={id} parameters to the work logs endpoint to filter by specific projects. This is useful for dashboards focused on billable project hours.
Is there a Time Doctor webhook I can use instead of polling?
Time Doctor does not currently provide webhooks for real-time event notifications. For live dashboards, you will need to poll the API at regular intervals. Client-side polling with a setInterval or SWR's refreshInterval option is the standard approach — polling every 5 minutes strikes a good balance between freshness and API quota consumption.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation