Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Everhour

Integrating Everhour with Lovable requires Edge Functions to proxy the Everhour REST API. Store your Everhour API key in Cloud Secrets, create an Edge Function that reads and creates time entries, project budgets, and team timesheets, and build time tracking interfaces in Lovable's React frontend. Everhour is purpose-built for teams already using Asana, Jira, or Trello — this integration lets you embed time tracking directly into your own Lovable app while syncing with those connected PM tools.

What you'll learn

  • How to generate an Everhour API key and store it in Cloud Secrets
  • How to build a Deno Edge Function that reads time entries, projects, and team data from Everhour
  • How to start and stop the Everhour timer and log manual time from a Lovable interface
  • How to build a budget burn-down dashboard showing project time vs budget
  • How to generate team timesheet summaries for billing and payroll reporting
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate14 min read40 minutesProductivityMarch 2026RapidDev Engineering Team
TL;DR

Integrating Everhour with Lovable requires Edge Functions to proxy the Everhour REST API. Store your Everhour API key in Cloud Secrets, create an Edge Function that reads and creates time entries, project budgets, and team timesheets, and build time tracking interfaces in Lovable's React frontend. Everhour is purpose-built for teams already using Asana, Jira, or Trello — this integration lets you embed time tracking directly into your own Lovable app while syncing with those connected PM tools.

Why integrate Everhour with Lovable?

Everhour occupies a unique position in the time tracking market: it's designed from the ground up to work alongside popular project management tools — embedding natively into Asana, Jira, Trello, and Basecamp interfaces — while maintaining its own project budget and reporting layer. Teams use Everhour because they want time tracking that fits into their existing PM workflow rather than requiring a separate app context-switch. The API gives you access to all of Everhour's data: time entries with project and task associations, running timers, project budget vs actual comparisons, team member time reports, and client billing summaries.

Building a custom Lovable integration with Everhour enables scenarios that neither Everhour's native interface nor its PM integrations fully address: a unified operations dashboard that shows project budget health alongside other business metrics, a client-facing time report portal where clients can see hours logged against their project without accessing your Everhour account, a simplified mobile-first time logging interface for field teams who find Everhour's native mobile app too complex, and automated weekly timesheet review workflows where team leads get prompted to approve time entries before they're billed.

Everhour's REST API v1 uses API key authentication via the X-Api-Key request header — a straightforward scheme that makes Edge Function setup quick. Time entries are the core resource: each entry has a time (seconds), date, user ID, and an associated project/task. Projects have budgets with types (time-based in seconds, money-based in currency), and the budget vs actual comparison is available via the project's budget endpoint. This tutorial covers timer control, manual time logging, budget tracking, and team timesheet generation.

Integration method

Edge Function Integration

Everhour has no native Lovable connector. All Everhour API calls use the REST API v1 at api.everhour.com, proxied through Supabase Edge Functions. API keys are stored in Cloud Secrets and sent as X-Api-Key headers. Edge Functions read and create time entries, manage timers, fetch project budgets, and pull team timesheets — returning structured time data to your Lovable frontend.

Prerequisites

  • A Lovable project with Cloud enabled
  • An Everhour account with at least one project and API access (available on Team plan and above)
  • An Everhour API key — generate at app.everhour.com → Team Settings → API → Generate new API key
  • The Everhour project IDs you want to track against — found in project URLs or via the API
  • Optional: Everhour connected to Asana, Jira, or Trello if you want to reference tasks from those tools in time entries

Step-by-step guide

1

Generate your Everhour API key and store it in Cloud Secrets

Everhour API keys are workspace-level credentials — they give access to all data in your Everhour team account, including time entries from all team members. To generate one, log into Everhour and click the team settings icon (gear icon) in the top navigation. Select 'Team Settings' and navigate to the 'API' or 'Integrations' section. Click 'Generate API Key', copy it immediately, and give it a descriptive label like 'Lovable Integration'. Note that API access in Everhour requires a Team plan or above — the free plan does not include API access. If you're on a free account and attempting to use the API, you'll receive 403 Forbidden errors on all requests. Confirm your plan before proceeding. With the key copied, open your Lovable project, click the '+' button, and select the Cloud tab. Navigate to Secrets and click 'Add secret'. Enter EVERHOUR_API_KEY as the name and paste your key as the value. Save it. Unlike other APIs that use Bearer auth, Everhour sends its API key as an X-Api-Key header — your Edge Function will set this custom header on every request.

Pro tip: Everhour API keys don't expire automatically, but you can revoke and regenerate them from Team Settings → API at any time. If you suspect a key compromise, regenerate it and update the Cloud Secret immediately.

Expected result: EVERHOUR_API_KEY appears in Cloud Secrets with a masked value. The Everhour Team Settings → API page shows the key as active.

2

Create the Everhour proxy Edge Function

Everhour's REST API v1 uses the X-Api-Key custom header for authentication. Time entries in Everhour are identified by a composite key — they reference both a project ID and optionally a task ID using Everhour's internal task format (e.g., 'tm:12345' for Trello tasks, 'jira:PROJECT-123' for Jira issues, or plain numeric IDs for Everhour-native tasks). Understanding this task ID format is important if you're integrating with Everhour's PM tool connections. The Edge Function below covers the most valuable operations: listing projects with budget data, fetching team time reports by date range, starting and stopping the timer for the authenticated user, creating manual time entries, and fetching per-project time summaries. The timer endpoints are user-scoped — the start/stop actions apply to the user whose API key is being used.

Lovable Prompt

Create a Supabase Edge Function at supabase/functions/everhour-proxy/index.ts. Read EVERHOUR_API_KEY from Deno.env.get(). Set X-Api-Key header on all requests. Handle CORS. Accept POST with action and params. Base URL https://api.everhour.com. Implement: 'list_projects' (GET /projects with params.limit=100 by default), 'get_project' (GET /projects/{params.projectId} with budget info), 'list_time' with params.from and params.to date strings (GET /team/time with from and to query params), 'get_project_time' (GET /projects/{params.projectId}/time with optional date range), 'start_timer' (POST /timers/current with project_id and optional task_id from params), 'stop_timer' (DELETE /timers/current), 'get_timer' (GET /timers/current), 'add_time' (POST /time with date, project_id, task_id optional, time in seconds, comment from params), 'list_members' (GET /team/members). Return full API responses.

Paste this in Lovable chat

supabase/functions/everhour-proxy/index.ts
1// supabase/functions/everhour-proxy/index.ts
2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
3
4const BASE = "https://api.everhour.com";
5const corsHeaders = {
6 "Access-Control-Allow-Origin": "*",
7 "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
8};
9
10serve(async (req) => {
11 if (req.method === "OPTIONS") {
12 return new Response("ok", { headers: corsHeaders });
13 }
14 const apiKey = Deno.env.get("EVERHOUR_API_KEY");
15 if (!apiKey) {
16 return new Response(JSON.stringify({ error: "EVERHOUR_API_KEY not set" }), {
17 status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" },
18 });
19 }
20 const authHeaders = { "X-Api-Key": apiKey, "Content-Type": "application/json" };
21 const { action, params = {} } = await req.json();
22 let resp;
23
24 switch (action) {
25 case "list_projects": {
26 const q = new URLSearchParams({ limit: String(params.limit || 100) });
27 resp = await fetch(`${BASE}/projects?${q}`, { headers: authHeaders });
28 break;
29 }
30 case "get_project":
31 resp = await fetch(`${BASE}/projects/${params.projectId}`, { headers: authHeaders });
32 break;
33 case "list_time": {
34 const q = new URLSearchParams();
35 if (params.from) q.set("from", params.from);
36 if (params.to) q.set("to", params.to);
37 if (params.userId) q.set("user", params.userId);
38 resp = await fetch(`${BASE}/team/time?${q}`, { headers: authHeaders });
39 break;
40 }
41 case "get_project_time": {
42 const q = new URLSearchParams();
43 if (params.from) q.set("from", params.from);
44 if (params.to) q.set("to", params.to);
45 resp = await fetch(`${BASE}/projects/${params.projectId}/time?${q}`, { headers: authHeaders });
46 break;
47 }
48 case "start_timer":
49 resp = await fetch(`${BASE}/timers/current`, {
50 method: "POST", headers: authHeaders,
51 body: JSON.stringify({ task: { id: params.projectId }, ...(params.taskId && { task: { id: params.taskId } }) }),
52 });
53 break;
54 case "stop_timer":
55 resp = await fetch(`${BASE}/timers/current`, { method: "DELETE", headers: authHeaders });
56 break;
57 case "get_timer":
58 resp = await fetch(`${BASE}/timers/current`, { headers: authHeaders });
59 break;
60 case "add_time":
61 resp = await fetch(`${BASE}/time`, {
62 method: "POST", headers: authHeaders,
63 body: JSON.stringify({
64 date: params.date, time: params.timeSeconds,
65 task: { id: params.projectId },
66 ...(params.comment && { comment: params.comment }),
67 }),
68 });
69 break;
70 case "list_members":
71 resp = await fetch(`${BASE}/team/members`, { headers: authHeaders });
72 break;
73 default:
74 return new Response(JSON.stringify({ error: `Unknown action: ${action}` }), {
75 status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" },
76 });
77 }
78
79 const data = await resp.json();
80 return new Response(JSON.stringify(data), {
81 status: resp.status, headers: { ...corsHeaders, "Content-Type": "application/json" },
82 });
83});

Pro tip: Everhour stores all time values in seconds. Convert to hours for display: Math.round(seconds / 3600 * 10) / 10 gives one decimal place (e.g., 7200 seconds → 2.0 hours). For budget comparisons, Everhour budgets use the same seconds unit.

Expected result: The Edge Function deploys. Calling action 'list_projects' returns your Everhour projects with ID, name, and budget data. Calling 'list_members' returns your team members.

3

Build a project budget burn-down dashboard

Everhour's budget data is exposed on each project object — the budget field contains the type ('time' for hours-based, 'money' for currency-based) and the value (in seconds for time budgets). By comparing the budget value to total time logged, you can calculate burn percentage and project health. For trend analysis, fetching time entries by week over the past month shows logging velocity. The dashboard component generates a project health overview that's useful for weekly agency reviews: color-coded burn indicators, time remaining calculations, and a pace indicator showing whether the current logging rate will exceed budget before the project end date.

Lovable Prompt

Create an EverhourBudgetDashboard component. Call action 'list_projects' to get all projects. For each project that has a budget, also call action 'get_project_time' with from=first day of current month, to=today to get hours logged this month. Calculate: budget hours (budget.value / 3600), hours logged, hours remaining, burn percentage. Render project cards in a 3-column grid. Each card shows project name, a circular progress indicator for burn percentage, hours logged vs budget, and a status badge: 'On Track' (under 75% burn), 'Watch' (75-95%), 'Over Budget' (over 100%). Cards are sorted by burn percentage. Add totals row: total budget hours across all projects, total logged hours, aggregate burn rate.

Paste this in Lovable chat

Pro tip: Everhour's project budget endpoint may return null budget values for projects without a configured budget. Always check if budget exists before calculating — render 'No budget set' for projects without one rather than showing 0% or erroring.

Expected result: The budget dashboard renders project cards with accurate burn percentages calculated from Everhour data. Color coding correctly reflects project health status. The totals row shows aggregate statistics.

4

Implement timer controls and manual time logging

The most engaging part of a time tracking integration is real-time timer control — users should be able to start a timer, see it counting up, and stop it to log the session. The Everhour timer API provides start (POST /timers/current), stop (DELETE /timers/current), and status (GET /timers/current) endpoints. The GET endpoint returns the current running timer state including the start time, which your frontend can use to calculate elapsed time client-side without additional API calls. For manual time entry (logging time after the fact), the Edge Function's add_time action creates a backdated entry on any past date. Combine this with a date picker and time input to create a manual log panel that team members can use to enter hours for work done without the timer running — a common scenario for meetings, calls, and focus sessions that happened before the user remembered to start the timer.

Lovable Prompt

Add a timer widget to the EverhourBudgetDashboard. The widget shows: current timer status (running/stopped) from action 'get_timer' called on mount. When running, show elapsed time counting up (calculate from startedAt timestamp). Show project name for the active timer. Add a 'Stop Timer' button calling action 'stop_timer'. Add a project selector dropdown and a 'Start Timer' button calling action 'start_timer' with the selected project ID. Also add a 'Log Time Manually' panel with: project selector, date picker, hours input (0.5 increments), description field, and Submit button calling action 'add_time' with timeSeconds = hours * 3600. Show a success toast after logging. Update the project card burn percentage immediately after logging.

Paste this in Lovable chat

Pro tip: Poll the timer status endpoint every 30 seconds to keep the displayed elapsed time synchronized with the Everhour server, especially if users may have multiple browser tabs or devices running. Avoid polling more frequently than every 15 seconds to stay within rate limits.

Expected result: The timer widget shows the current timer state and updates in real time. Start/stop controls work correctly. Manual time logging creates entries visible in Everhour immediately after submission.

Common use cases

Project budget burn-down dashboard

An agency tracks all projects in Everhour with time-based budgets and wants a single-page dashboard showing budget health across all active projects. The Edge Function fetches projects with their budget settings and time totals, then calculates burn percentage and estimated time-to-budget-exceeded based on current logging velocity. The frontend renders a card grid with color-coded health indicators and a burn rate trend.

Lovable Prompt

Build a budget dashboard at /project-budgets. Fetch all active projects from my Everhour Edge Function. For each project, show: project name, budget hours, hours logged, hours remaining, burn percentage as a progress bar (green under 70%, yellow 70-90%, red over 90%), and estimated days until budget exhausted based on average daily hours this month. Sort projects by burn percentage descending. Add a filter to show all projects, only over-budget, or only under 50% burned. Show total hours across all projects in a summary card at the top.

Copy this prompt to try it in Lovable

Team timesheet review and approval workflow

A project manager needs to review and approve weekly timesheets before client invoicing. The Edge Function fetches all time entries for the previous week grouped by team member, and the frontend presents an approval interface where the manager can review entries, add notes, and mark the week as approved — storing the approval in Supabase and triggering an invoice creation step.

Lovable Prompt

Build a timesheet review page at /timesheet-review. Add a week selector defaulting to last week. Fetch time entries for the selected week from my Everhour Edge Function grouped by user. Display each team member's entries in an expandable table: date, project name, task name, hours, and notes. Show weekly totals per person. Add an 'Approve' button per person that marks their timesheet as approved in a Supabase approvals table and sends them a notification. Show a summary: total team hours, billable vs non-billable split, and top projects by hours.

Copy this prompt to try it in Lovable

One-click time logging from project task list

A development team uses a custom Lovable task tracker backed by Supabase and wants to add Everhour time logging directly to the task list — clicking a task starts the Everhour timer for that task's associated Everhour project, and clicking again stops it and logs the entry. The Edge Function starts and stops the Everhour timer API and returns the elapsed time to update the UI.

Lovable Prompt

Add time tracking buttons to the task list component. Each task row shows a play button. Clicking play calls my Everhour Edge Function to start a timer for the associated Everhour project ID (stored in the task's everhour_project_id column). Show a running timer clock on the active task row. Clicking stop calls the Edge Function to stop the timer and logs the time entry. Show a 'Logged today' badge on tasks with time entries today. Store the Everhour time entry ID in Supabase on the task record for reference.

Copy this prompt to try it in Lovable

Troubleshooting

All API requests return 403 Forbidden

Cause: Everhour API access requires a paid Team plan or above. Free accounts do not have API access and will receive 403 on all requests regardless of authentication.

Solution: Verify your Everhour plan at app.everhour.com → Team Settings → Plan & Billing. If you're on the free plan, you'll need to upgrade to Team to use the API. If you're on a paid plan and still getting 403, check that the API key was generated from the Team Settings API page (not a personal settings page) and that the key is still active.

Time values in API responses are unexpectedly large numbers like 7200

Cause: Everhour stores all time values in seconds, not hours or minutes. A value of 7200 represents 2 hours (7200 / 3600 = 2.0).

Solution: Always convert seconds to hours for display using seconds / 3600. For displaying as hours and minutes, use Math.floor(seconds / 3600) for hours and Math.floor((seconds % 3600) / 60) for minutes. For budget comparisons, ensure both values are in seconds before comparing.

typescript
1// Convert seconds to readable format
2const toHoursDecimal = (seconds: number) => Math.round(seconds / 360) / 10;
3const toHoursMinutes = (seconds: number) => {
4 const h = Math.floor(seconds / 3600);
5 const m = Math.floor((seconds % 3600) / 60);
6 return `${h}h ${m}m`;
7};

Timer start returns 409 Conflict error

Cause: A timer is already running for the user. Everhour only allows one active timer per user at a time.

Solution: Before starting a new timer, call get_timer to check if one is already running. If yes, either stop the current timer first or prompt the user to stop it before starting a new one. Handle the 409 response in your UI by showing a message: 'Timer already running for [project name] — stop it before starting a new one.'

typescript
1// Check before starting
2const currentTimer = await callEdgeFunction('get_timer', {});
3if (currentTimer.timer?.status === 'active') {
4 // Show UI prompt to stop current timer first
5 setConflictProject(currentTimer.task?.name);
6 return;
7}

Time entries created via add_time don't appear in team reports

Cause: Time entries added via the API are created for the user associated with the API key. If the API key belongs to an admin and you're trying to log time for team members, the entries will appear under the admin's account, not the team member's.

Solution: To log time for specific team members, you need that member's API key or must use user-specific API tokens. Alternatively, use the user ID parameter in time entry creation to specify which team member the entry belongs to — check Everhour's API docs for the userId field on the POST /time endpoint.

Best practices

  • Always convert Everhour's second-based time values to hours before displaying to users — storing in seconds is correct for calculation but confusing to display raw.
  • Cache project lists with a 10-minute TTL since projects change infrequently, but always refresh time entry data in real time for accurate burn rate calculations.
  • Implement a maximum logging duration check before calling add_time — entries over 24 hours are almost always input errors and should prompt user confirmation.
  • Use Everhour's date range filters (from/to) when fetching time reports for billing periods rather than loading all-time data and filtering client-side.
  • For teams where different members track different projects, fetch only the projects relevant to the logged-in user rather than loading all team projects on every page load.
  • Handle the case where a project has no budget gracefully in your UI — many projects are tracked in Everhour without formal budgets, and budget calculations should not error on null values.
  • Test timer start/stop flows against Everhour's native interface in parallel to confirm entries are appearing correctly — API-logged time should be visible immediately in Everhour's standard reports.

Alternatives

Frequently asked questions

Does Everhour have a native connector in Lovable?

No, Everhour is not one of Lovable's 17 shared connectors. Integration requires an Edge Function that proxies the Everhour REST API v1. The API key is stored in Cloud Secrets and sent as an X-Api-Key header — it is never exposed to client-side code.

Does Everhour's API provide access to time entries from connected Asana or Jira tasks?

Yes — when Everhour is connected to Asana or Jira, time entries are linked to those external task IDs. The API returns task IDs in the format 'as:ASANA_TASK_ID' or 'jira:ISSUE_KEY'. You can use these IDs to cross-reference Everhour time data with task data from those PM tools in the same dashboard.

Can I use the Everhour API to generate invoices?

Everhour's API provides time entry and project data that you can use to calculate invoice amounts, but Everhour does not have invoice generation as a built-in API feature. For invoice generation, use the time data from Everhour combined with a billing integration like Stripe or QuickBooks in a separate Edge Function to create and send invoices based on tracked hours.

What is the Everhour API rate limit?

Everhour does not publicly publish detailed rate limit numbers, but standard practice is to stay under 100 requests per minute per API key. For dashboard applications that load multiple project time summaries in parallel, add a small delay between requests or implement request batching to avoid hitting limits. Monitor Cloud → Logs for 429 responses as an indicator of rate limit pressure.

How do I handle multiple team members each with their own Everhour accounts?

For a team dashboard where each member's data is aggregated, use an admin-level API key that has access to all team members' time entries — the /team/time endpoint returns entries across all users. For apps where individual users log their own time, you'll need each user to provide their API token, which you store per-user in Supabase's user settings table and pass dynamically to the Edge Function based on the authenticated user.

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.