Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Everhour with V0

To integrate Everhour with V0 by Vercel, create a Next.js API route at app/api/everhour/route.ts that calls the Everhour REST API using a token stored in EVERHOUR_API_TOKEN. V0 generates time tracking dashboards and budget tracking views that fetch from your API route. Everhour's strength is deep integration with Asana, Jira, and Basecamp, making it ideal for displaying project time alongside task data.

What you'll learn

  • How to generate an Everhour API token and configure Vercel environment variables
  • How to create a Next.js API route that fetches projects, time entries, and budget data from Everhour
  • How to prompt V0 to generate project budget tracking and team time dashboards
  • How to display hours tracked versus budget limits with visual progress indicators
  • How to filter Everhour data by team member and project for targeted reports
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read20 minutesProductivityApril 2026RapidDev Engineering Team
TL;DR

To integrate Everhour with V0 by Vercel, create a Next.js API route at app/api/everhour/route.ts that calls the Everhour REST API using a token stored in EVERHOUR_API_TOKEN. V0 generates time tracking dashboards and budget tracking views that fetch from your API route. Everhour's strength is deep integration with Asana, Jira, and Basecamp, making it ideal for displaying project time alongside task data.

Building Project Budget and Time Dashboards with Everhour and V0

Everhour is uniquely positioned among time trackers because it embeds directly into project management tools — Asana tasks, Jira issues, and Basecamp to-dos show time tracking controls without leaving those interfaces. For teams already using these tools, Everhour captures time data where work happens rather than requiring a separate tracking step. The Everhour API then exposes this data for building custom reports and dashboards.

The integration pattern creates a Next.js API route that authenticates with Everhour using an API token. V0 generates the visualization components — project budget bars, team utilization tables, weekly time summaries, or burn rate charts — that fetch from the secure API route. The Everhour token never reaches the browser, which matters because Everhour tokens grant access to your entire team's time data.

Everhour's most distinctive feature for custom dashboards is its budget tracking: you can set hour budgets or monetary budgets on projects, and the API returns both the budget amount and time tracked so far. This makes Everhour-powered dashboards especially useful for agencies tracking billable hours against project budgets and clients wanting to see how much of their retainer has been consumed.

Integration method

Next.js API Route

V0 generates React dashboard components for displaying Everhour time tracking and budget data. A Next.js API route on Vercel proxies requests to the Everhour REST API, keeping your API token server-side. Components call /api/everhour/projects rather than Everhour directly, which prevents credential exposure and bypasses CORS restrictions.

Prerequisites

  • A V0 account with a Next.js project at v0.dev
  • An Everhour account at everhour.com with projects and time entries
  • An Everhour API token from your Everhour account settings
  • Project IDs for the specific projects you want to display in your dashboard
  • A Vercel account with your V0 project connected via GitHub

Step-by-step guide

1

Get Your Everhour API Token

Everhour uses token-based authentication. Your API token is found in your Everhour account settings and grants access to all your workspace data. To get your API token, log into Everhour at app.everhour.com. Click on your profile name or avatar in the top-right corner and select 'Account Settings' or 'Profile'. Navigate to the 'API' section in your profile settings. You will see your API token displayed there. Click to reveal or copy the token. If you do not see an API section, it may be that only workspace owners or admins have API access on your plan. Everhour's API token is a long alphanumeric string that identifies you as a user. Unlike some APIs, Everhour uses a single user-level token rather than organization-level tokens, which means API calls will have the same access permissions as your user account. If you are a workspace owner, you have full access to all projects and team members' time data. All Everhour API calls use the base URL https://api.everhour.com. Authentication uses a custom header: X-Api-Key with your token value. The API supports both JSON request and response bodies. For finding project IDs, navigate to a project in Everhour, and the project ID appears in the URL: https://app.everhour.com/#/projects/{projectId}. Alternatively, your API route can fetch all projects and return their IDs alongside names, letting your frontend choose which projects to display.

Pro tip: Everhour uses X-Api-Key as the authentication header (not Authorization: Bearer). Using the wrong header name results in a 403 Forbidden response.

Expected result: You have your Everhour API token and understand the X-Api-Key authentication pattern. You have noted at least one project ID for testing your API route.

2

Add Everhour Token to Vercel

Store your Everhour API token securely in Vercel's environment variables before writing any code. This ensures the token is available to your serverless function without being exposed in your codebase. Open your Vercel Dashboard, navigate to your project, click 'Settings', and select 'Environment Variables'. Add the following variable: EVERHOUR_API_TOKEN: Your Everhour API token. This is a secret — do not add a NEXT_PUBLIC_ prefix. Set it for Production, Preview, and Development environments. The token grants access to all your workspace's time and budget data, so protecting it server-side is important. Save the variable and trigger a redeployment. For local development, add EVERHOUR_API_TOKEN to your .env.local file. The .env.local file is excluded from Git by default in V0-generated projects.

.env.local
1# .env.local local development only, excluded from Git
2EVERHOUR_API_TOKEN=your_everhour_api_token_here

Pro tip: If your Everhour API token is accidentally committed to Git, regenerate it immediately from your Everhour account settings and update the Vercel environment variable.

Expected result: Vercel Dashboard shows EVERHOUR_API_TOKEN saved as an environment variable. Local .env.local is configured for development testing.

3

Create the Everhour Projects API Route

Create the Next.js API route that fetches project data from Everhour. Start with the projects endpoint to get a list of all active projects with their budget information. Create app/api/everhour/projects/route.ts. The Everhour projects endpoint is GET https://api.everhour.com/projects and returns an array of project objects. Each project includes id, name, type, status, budget (with type, progress, and limit fields), team members, and time tracking totals. The budget object is particularly valuable for agency dashboards. budget.type can be 'money' (dollar budget) or 'time' (hour budget). budget.progress.trackedTime contains seconds tracked so far. budget.progress.cost contains dollars spent. budget.limit is the budget ceiling. Calculate the percentage used as (trackedTime / (limit * 3600)) * 100 for time budgets. Filter for active projects (status: 'active') to avoid displaying archived projects. Sort by budget percentage used (most critical first) for an at-a-glance priority view. Everhour also offers a team report endpoint at GET https://api.everhour.com/team/time that returns time entries grouped by team member and project for a date range. This is useful for weekly team summary dashboards. Pass start and end query parameters in YYYY-MM-DD format.

V0 Prompt

Create a Next.js API route at app/api/everhour/projects/route.ts. Use EVERHOUR_API_TOKEN from environment variables. Fetch from https://api.everhour.com/projects with header X-Api-Key: {token}. Filter to active projects only. Return for each project: id, name, totalSeconds tracked, budgetType, budgetLimit, budgetTrackedSeconds, budgetPercent (calculated 0-100). Sort by budgetPercent descending. Handle errors with 500 status.

Paste this in V0 chat

app/api/everhour/projects/route.ts
1import { NextResponse } from 'next/server';
2
3interface EverhourBudget {
4 type: 'money' | 'time' | null;
5 progress: {
6 trackedTime: number;
7 cost: number;
8 } | null;
9 limit: number | null;
10}
11
12interface EverhourProject {
13 id: string;
14 name: string;
15 status: string;
16 budget: EverhourBudget | null;
17 time: {
18 total: number;
19 } | null;
20}
21
22export async function GET() {
23 const apiToken = process.env.EVERHOUR_API_TOKEN;
24
25 if (!apiToken) {
26 return NextResponse.json(
27 { error: 'Everhour configuration missing' },
28 { status: 500 }
29 );
30 }
31
32 try {
33 const response = await fetch('https://api.everhour.com/projects', {
34 headers: {
35 'X-Api-Key': apiToken,
36 'Content-Type': 'application/json',
37 },
38 next: { revalidate: 120 },
39 });
40
41 if (!response.ok) {
42 const errorText = await response.text();
43 console.error('Everhour API error:', errorText);
44 return NextResponse.json(
45 { error: `Everhour returned ${response.status}` },
46 { status: response.status }
47 );
48 }
49
50 const projects: EverhourProject[] = await response.json();
51
52 const activeProjects = projects
53 .filter((p) => p.status === 'active')
54 .map((p) => {
55 const trackedSeconds = p.budget?.progress?.trackedTime ?? p.time?.total ?? 0;
56 const budgetLimit = p.budget?.limit ?? null;
57 const budgetType = p.budget?.type ?? null;
58
59 let budgetPercent = 0;
60 if (budgetType === 'time' && budgetLimit && budgetLimit > 0) {
61 // Budget limit is in seconds for time budgets
62 budgetPercent = Math.min(
63 100,
64 Math.round((trackedSeconds / budgetLimit) * 100)
65 );
66 } else if (budgetType === 'money' && budgetLimit && budgetLimit > 0) {
67 const cost = p.budget?.progress?.cost ?? 0;
68 budgetPercent = Math.min(100, Math.round((cost / budgetLimit) * 100));
69 }
70
71 return {
72 id: p.id,
73 name: p.name,
74 totalHours: Math.round((trackedSeconds / 3600) * 100) / 100,
75 totalSeconds: trackedSeconds,
76 budgetType,
77 budgetLimitHours:
78 budgetType === 'time' && budgetLimit
79 ? Math.round((budgetLimit / 3600) * 100) / 100
80 : null,
81 budgetLimitMoney: budgetType === 'money' ? budgetLimit : null,
82 budgetCost: p.budget?.progress?.cost ?? 0,
83 budgetPercent,
84 };
85 })
86 .sort((a, b) => b.budgetPercent - a.budgetPercent);
87
88 return NextResponse.json({ projects: activeProjects, total: activeProjects.length });
89 } catch (error) {
90 console.error('Everhour projects fetch failed:', error);
91 return NextResponse.json(
92 { error: 'Failed to fetch projects' },
93 { status: 500 }
94 );
95 }
96}

Pro tip: Everhour time budget limits are stored in seconds in the API, not hours. Divide by 3600 to convert to hours for display. Money budget limits are in whole currency units (dollars, not cents).

Expected result: Calling /api/everhour/projects returns a JSON array of active projects sorted by budget percentage used, with hours tracked, budget limits, and calculated budget percentage for each project.

4

Generate Budget Dashboard with V0

With the projects API route returning budget data, prompt V0 to generate the budget tracking dashboard. Budget dashboards need clear visual indicators of health (green/yellow/red based on percentage) and actionable numbers (hours remaining, days until overrun at current burn rate). Before writing the V0 prompt, test /api/everhour/projects to see what data is actually returned for your projects. Note whether your projects have time budgets, money budgets, or no budgets — this affects which columns to display. For a budget health dashboard, describe the card layout with the project name, progress bar, and key numbers. Ask V0 to color the progress bar and status badge based on thresholds: green below 75%, yellow for 75-90%, red above 90%. This creates an immediate at-a-glance view of which projects need attention. For projects with remaining hours, ask V0 to calculate and display 'X hours remaining' or 'Over budget by X hours' based on the budgetPercent value. If your API returns enough data, you can also calculate the burn rate (hours per day) and project when the budget will be exhausted. Ask V0 to add a project filter (All / Over 90% / Over 75%) and a last-updated timestamp so users know how fresh the data is. Include a loading skeleton that matches your card layout and an error state with a retry button.

V0 Prompt

Build a project budget dashboard that fetches from /api/everhour/projects. Response has projects array with name, totalHours, budgetType ('time' or 'money' or null), budgetLimitHours (nullable), budgetLimitMoney (nullable), budgetCost, budgetPercent (0-100). Display projects as cards sorted by budgetPercent. Each card: project name as heading, progress bar colored green below 75 / yellow 75-90 / red above 90, percentage text ('72% used'), and detail line ('34.5 of 48 hours' for time budgets or '$2,340 of $5,000' for money budgets). Projects without a budget show 'No budget set' in grey. Add filter tabs: All / At Risk (>75%) / Critical (>90%). Show 6 skeleton cards while loading.

Paste this in V0 chat

Pro tip: For projects without a budget (budgetType is null), show total hours tracked with no progress bar rather than hiding them — these projects are still worth monitoring even without formal limits.

Expected result: V0 generates a project budget dashboard with color-coded progress bars, budget percentage indicators, and filter tabs for at-risk and critical projects. Loading skeletons display while the data fetches.

5

Add a Time Entries Route for Detailed Reports

Project managers often need to drill into specific time entries to understand where hours are going. Add a time entries API route that returns individual logged entries filtered by project and date range. Create app/api/everhour/time-entries/route.ts. The Everhour time entries endpoint is GET https://api.everhour.com/team/time with query parameters from (start date in YYYY-MM-DD format) and to (end date in YYYY-MM-DD format). You can also filter by project using the projects[] parameter. The response is an array of time records, each with user (name, email), task (id, name, project name), time (seconds), date (YYYY-MM-DD string), and an optional comment. Transform seconds to hours for display: const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60). For client-facing reports, you may want to group entries by date and calculate daily and weekly subtotals. For internal reports, grouping by team member shows individual utilization. Ask V0 to generate a tabbed view where the same data can be viewed by date, by person, or by task. For agencies and consultants who want full billing automation with Everhour time data feeding into invoice generation, RapidDev can help build an end-to-end workflow connecting Everhour time entries to invoice generation and payment collection.

V0 Prompt

Add a time entries detail view below the project budget cards. Create a 'View Details' button on each project card that opens a drawer panel. The drawer fetches from /api/everhour/time-entries?projectId={id}&start={weekStart}&end={weekEnd}. Response has entries array with date, user.name, task.name, seconds, and comment. Group entries by date. Show each entry as a row with date, person name, task name, duration (formatted as Xh Ym), and comment in muted text. Show a subtotal per day. Add loading state for the drawer.

Paste this in V0 chat

app/api/everhour/time-entries/route.ts
1import { NextRequest, NextResponse } from 'next/server';
2
3export async function GET(request: NextRequest) {
4 const apiToken = process.env.EVERHOUR_API_TOKEN;
5
6 if (!apiToken) {
7 return NextResponse.json(
8 { error: 'Everhour configuration missing' },
9 { status: 500 }
10 );
11 }
12
13 const { searchParams } = new URL(request.url);
14
15 const now = new Date();
16 const weekStart = new Date(now);
17 weekStart.setDate(now.getDate() - now.getDay());
18 const weekEnd = new Date(weekStart);
19 weekEnd.setDate(weekStart.getDate() + 6);
20
21 const formatDate = (d: Date) => d.toISOString().split('T')[0];
22
23 const from = searchParams.get('start') || formatDate(weekStart);
24 const to = searchParams.get('end') || formatDate(weekEnd);
25 const projectId = searchParams.get('projectId');
26
27 const url = new URL('https://api.everhour.com/team/time');
28 url.searchParams.set('from', from);
29 url.searchParams.set('to', to);
30 if (projectId) {
31 url.searchParams.append('projects[]', projectId);
32 }
33
34 try {
35 const response = await fetch(url.toString(), {
36 headers: {
37 'X-Api-Key': apiToken,
38 'Content-Type': 'application/json',
39 },
40 next: { revalidate: 60 },
41 });
42
43 if (!response.ok) {
44 return NextResponse.json(
45 { error: `Everhour returned ${response.status}` },
46 { status: response.status }
47 );
48 }
49
50 const data = await response.json();
51
52 const entries = (Array.isArray(data) ? data : []).map(
53 (entry: {
54 user: { name: string; email: string };
55 task: { name: string } | null;
56 time: number;
57 date: string;
58 comment: string | null;
59 }) => ({
60 date: entry.date,
61 userName: entry.user?.name || 'Unknown',
62 taskName: entry.task?.name || 'No task',
63 seconds: entry.time,
64 hours: Math.floor(entry.time / 3600),
65 minutes: Math.floor((entry.time % 3600) / 60),
66 comment: entry.comment || '',
67 })
68 );
69
70 return NextResponse.json({ entries, dateRange: { from, to } });
71 } catch (error) {
72 console.error('Everhour time entries fetch failed:', error);
73 return NextResponse.json(
74 { error: 'Failed to fetch time entries' },
75 { status: 500 }
76 );
77 }
78}

Pro tip: Everhour's team time endpoint (/team/time) requires owner or admin access. Individual users can only fetch their own time entries via /users/me/time. Check your account role if the team endpoint returns 403.

Expected result: Calling /api/everhour/time-entries returns an array of time entry records with user names, task names, formatted durations, and dates for the specified project and date range.

Common use cases

Project Budget Burn Rate Dashboard

An agency wants to see how quickly each active project is consuming its allocated budget. V0 generates a dashboard with project cards showing the budget limit, hours used, hours remaining, and a color-coded progress bar that turns red as the budget runs low. The dashboard helps project managers catch over-budget projects before they become a problem.

V0 Prompt

Build a project budget dashboard fetching from /api/everhour/projects. Each project has name, budget.type ('money' or 'time'), budget.progress.trackedTime (seconds), budget.progress.cost (dollars), budget.limit (hours or dollars), and budget.percentUsed (0-100). Display as cards with the project name, a progress bar colored green below 75%, yellow 75-90%, red above 90%, and text showing 'X of Y hours' or '$X of $Y'. Sort by percentUsed descending (most critical first). Show a red warning badge on projects over 90%.

Copy this prompt to try it in V0

Team Weekly Time Summary

A project manager wants a Monday morning snapshot of last week's team time broken down by person and project. V0 generates a summary table showing each team member's total hours for the previous week, with a breakdown by project. The table helps identify who is over-utilized and which projects consumed unexpected time.

V0 Prompt

Create a team time summary table fetching from /api/everhour/team-report. The response has users array where each user has name and projects array (projectName, seconds). Display as a table with users as rows. Show Total Hours column and one column per active project. Hours formatted as 'Xh Ym'. Color cells over 8h/project in orange. Add a totals row. Include a date range header showing the reporting period. Show an empty state if no data.

Copy this prompt to try it in V0

Client-Facing Hours Report

A consultant wants to show clients a weekly hours report for their project without giving them full Everhour access. V0 generates a clean branded report page that shows time entries for a specific project during the current month, with task descriptions and hours per entry.

V0 Prompt

Build a client hours report page that fetches from /api/everhour/time-entries?project={projectId}. The response has entries array with date (ISO string), user.name, task.name, seconds (integer), and comment. Group entries by date formatted as 'Monday, March 24, 2026'. Show each entry with user name, task name, hours formatted as 'Xh Ym', and optional comment. Show project total at the bottom in bold. Add a 'Download CSV' button that exports the visible data.

Copy this prompt to try it in V0

Troubleshooting

API returns 403 Forbidden for team time entries endpoint

Cause: Everhour's /team/time endpoint requires owner or admin role in the workspace. Regular team member tokens can only access their own time entries via /users/me/time, not the full team dataset.

Solution: Use an API token belonging to a workspace owner or admin to access team-wide time data. For individual user data, use the /users/me/time endpoint with that user's own token. Verify the account role in Everhour workspace settings.

typescript
1// For individual user time (works with any role):
2// GET https://api.everhour.com/users/me/time?from=2026-01-01&to=2026-01-31
3
4// For team-wide time (requires owner/admin):
5// GET https://api.everhour.com/team/time?from=2026-01-01&to=2026-01-31

Budget percentage shows 0 for all projects

Cause: Everhour time budgets are stored in seconds (not hours) in the budget.limit field. Dividing tracked time by the limit without accounting for the unit difference produces near-zero percentages.

Solution: Check whether budget.type is 'time' or 'money'. For time budgets, budget.limit is in seconds — compare directly with progress.trackedTime (also in seconds). Do not multiply budget.limit by 3600 before comparing.

typescript
1// Time budget: both limit and trackedTime are in seconds
2const budgetPercent = budgetType === 'time' && budgetLimit > 0
3 ? Math.round((trackedSeconds / budgetLimit) * 100)
4 : 0;
5
6// Convert to hours for DISPLAY only (not for calculation)
7const displayHours = trackedSeconds / 3600;
8const budgetHours = budgetLimit / 3600;

Date filter returns no results even though entries exist

Cause: Everhour expects date parameters in YYYY-MM-DD format (e.g., '2026-03-24'). Sending ISO 8601 datetime strings with time components or timestamps causes the date filter to be ignored or return an error.

Solution: Always format date parameters as YYYY-MM-DD strings. Use date.toISOString().split('T')[0] in JavaScript to get just the date portion from a Date object. Verify the date range encompasses dates when time was actually tracked.

typescript
1// Correct date format for Everhour API
2const formatDate = (d: Date): string => d.toISOString().split('T')[0];
3
4url.searchParams.set('from', formatDate(startDate)); // e.g., '2026-03-24'
5url.searchParams.set('to', formatDate(endDate)); // e.g., '2026-03-30'

Best practices

  • Store EVERHOUR_API_TOKEN without a NEXT_PUBLIC_ prefix — it grants access to all team members' time data and project budgets.
  • Cache project summary responses for 2 minutes and time entry responses for 1 minute — budget percentages update slowly enough that short caching is sufficient while reducing API call volume.
  • Use Everhour's built-in budget percentage calculation by comparing tracked time to budget limit in your API route rather than recalculating in the frontend component.
  • Always check for null budget fields before performing calculations — not all Everhour projects have budgets set, and dividing by null or zero causes errors.
  • Use YYYY-MM-DD date format for all Everhour API parameters — the API does not accept ISO 8601 datetime strings with time components.
  • For client-facing reports, filter to a specific project ID rather than returning all projects — this prevents accidental data disclosure between clients.
  • Handle the case where a team member's total hours exceed 40 hours per week in your dashboard — Everhour tracks actual logged time with no cap, so dashboards should not assume hours are bounded.

Alternatives

Frequently asked questions

What project management tools does Everhour integrate with natively?

Everhour integrates natively with Asana, Jira, Basecamp, Trello, GitHub, GitLab, ClickUp, Linear, and Notion. The integration embeds time tracking controls directly in those tools' interfaces. When you fetch time entries via the Everhour API, entries include task references linked to the originating project management tool.

Does Everhour have a free plan?

Everhour requires a paid plan — there is no permanently free tier. Pricing starts at $10/month per user. All paid plans include full API access. A 14-day free trial is available without a credit card.

Can I create time entries via the Everhour API?

Yes. Use POST https://api.everhour.com/time with a JSON body containing time (seconds), date (YYYY-MM-DD), and optionally task.id. This lets you build custom time entry interfaces in V0 where team members log time from your branded app rather than the Everhour interface. The entry appears in Everhour reports and budget calculations immediately.

How do I get a list of projects and their IDs?

Call GET https://api.everhour.com/projects with your API token. The response array contains project objects with id, name, and status fields. You can use these IDs to filter time entries and budget data. Project IDs are alphanumeric strings.

Can I display Everhour data for only a specific client?

Everhour supports clients as a concept — projects can be assigned to clients. Use GET /clients to fetch your client list, then filter projects by clientId when fetching project data. This lets you build client-specific budget dashboards that only show that client's projects.

How often should I refresh Everhour data in a live dashboard?

For internal dashboards where team members are actively logging time, refreshing every 2-5 minutes is reasonable. Use Next.js fetch caching with next: { revalidate: 120 } for the project summary route. For client-facing reports that update less frequently, 5-15 minute caching (revalidate: 300-900) is sufficient and reduces your API call count.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.