# How to Integrate Everhour with V0

- Tool: V0
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## 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.

## Before you start

- 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.

**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 — local development only, excluded from Git
EVERHOUR_API_TOKEN=your_everhour_api_token_here
```

**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.

```
import { NextResponse } from 'next/server';

interface EverhourBudget {
  type: 'money' | 'time' | null;
  progress: {
    trackedTime: number;
    cost: number;
  } | null;
  limit: number | null;
}

interface EverhourProject {
  id: string;
  name: string;
  status: string;
  budget: EverhourBudget | null;
  time: {
    total: number;
  } | null;
}

export async function GET() {
  const apiToken = process.env.EVERHOUR_API_TOKEN;

  if (!apiToken) {
    return NextResponse.json(
      { error: 'Everhour configuration missing' },
      { status: 500 }
    );
  }

  try {
    const response = await fetch('https://api.everhour.com/projects', {
      headers: {
        'X-Api-Key': apiToken,
        'Content-Type': 'application/json',
      },
      next: { revalidate: 120 },
    });

    if (!response.ok) {
      const errorText = await response.text();
      console.error('Everhour API error:', errorText);
      return NextResponse.json(
        { error: `Everhour returned ${response.status}` },
        { status: response.status }
      );
    }

    const projects: EverhourProject[] = await response.json();

    const activeProjects = projects
      .filter((p) => p.status === 'active')
      .map((p) => {
        const trackedSeconds = p.budget?.progress?.trackedTime ?? p.time?.total ?? 0;
        const budgetLimit = p.budget?.limit ?? null;
        const budgetType = p.budget?.type ?? null;

        let budgetPercent = 0;
        if (budgetType === 'time' && budgetLimit && budgetLimit > 0) {
          // Budget limit is in seconds for time budgets
          budgetPercent = Math.min(
            100,
            Math.round((trackedSeconds / budgetLimit) * 100)
          );
        } else if (budgetType === 'money' && budgetLimit && budgetLimit > 0) {
          const cost = p.budget?.progress?.cost ?? 0;
          budgetPercent = Math.min(100, Math.round((cost / budgetLimit) * 100));
        }

        return {
          id: p.id,
          name: p.name,
          totalHours: Math.round((trackedSeconds / 3600) * 100) / 100,
          totalSeconds: trackedSeconds,
          budgetType,
          budgetLimitHours:
            budgetType === 'time' && budgetLimit
              ? Math.round((budgetLimit / 3600) * 100) / 100
              : null,
          budgetLimitMoney: budgetType === 'money' ? budgetLimit : null,
          budgetCost: p.budget?.progress?.cost ?? 0,
          budgetPercent,
        };
      })
      .sort((a, b) => b.budgetPercent - a.budgetPercent);

    return NextResponse.json({ projects: activeProjects, total: activeProjects.length });
  } catch (error) {
    console.error('Everhour projects fetch failed:', error);
    return NextResponse.json(
      { error: 'Failed to fetch projects' },
      { status: 500 }
    );
  }
}
```

**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.

**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.

```
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  const apiToken = process.env.EVERHOUR_API_TOKEN;

  if (!apiToken) {
    return NextResponse.json(
      { error: 'Everhour configuration missing' },
      { status: 500 }
    );
  }

  const { searchParams } = new URL(request.url);

  const now = new Date();
  const weekStart = new Date(now);
  weekStart.setDate(now.getDate() - now.getDay());
  const weekEnd = new Date(weekStart);
  weekEnd.setDate(weekStart.getDate() + 6);

  const formatDate = (d: Date) => d.toISOString().split('T')[0];

  const from = searchParams.get('start') || formatDate(weekStart);
  const to = searchParams.get('end') || formatDate(weekEnd);
  const projectId = searchParams.get('projectId');

  const url = new URL('https://api.everhour.com/team/time');
  url.searchParams.set('from', from);
  url.searchParams.set('to', to);
  if (projectId) {
    url.searchParams.append('projects[]', projectId);
  }

  try {
    const response = await fetch(url.toString(), {
      headers: {
        'X-Api-Key': apiToken,
        'Content-Type': 'application/json',
      },
      next: { revalidate: 60 },
    });

    if (!response.ok) {
      return NextResponse.json(
        { error: `Everhour returned ${response.status}` },
        { status: response.status }
      );
    }

    const data = await response.json();

    const entries = (Array.isArray(data) ? data : []).map(
      (entry: {
        user: { name: string; email: string };
        task: { name: string } | null;
        time: number;
        date: string;
        comment: string | null;
      }) => ({
        date: entry.date,
        userName: entry.user?.name || 'Unknown',
        taskName: entry.task?.name || 'No task',
        seconds: entry.time,
        hours: Math.floor(entry.time / 3600),
        minutes: Math.floor((entry.time % 3600) / 60),
        comment: entry.comment || '',
      })
    );

    return NextResponse.json({ entries, dateRange: { from, to } });
  } catch (error) {
    console.error('Everhour time entries fetch failed:', error);
    return NextResponse.json(
      { error: 'Failed to fetch time entries' },
      { status: 500 }
    );
  }
}
```

**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.

## 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.

## 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.

Prompt example:

```
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%.
```

### 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.

Prompt example:

```
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.
```

### 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.

Prompt example:

```
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.
```

## 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.

```
// For individual user time (works with any role):
// GET https://api.everhour.com/users/me/time?from=2026-01-01&to=2026-01-31

// For team-wide time (requires owner/admin):
// 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.

```
// Time budget: both limit and trackedTime are in seconds
const budgetPercent = budgetType === 'time' && budgetLimit > 0
  ? Math.round((trackedSeconds / budgetLimit) * 100)
  : 0;

// Convert to hours for DISPLAY only (not for calculation)
const displayHours = trackedSeconds / 3600;
const 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.

```
// Correct date format for Everhour API
const formatDate = (d: Date): string => d.toISOString().split('T')[0];

url.searchParams.set('from', formatDate(startDate)); // e.g., '2026-03-24'
url.searchParams.set('to', formatDate(endDate));     // e.g., '2026-03-30'
```

## 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.

---

Source: https://www.rapidevelopers.com/v0-integrations/everhour
© RapidDev — https://www.rapidevelopers.com/v0-integrations/everhour
