# Clockify

- Tool: Bubble
- Difficulty: Intermediate
- Time required: 2–3 hours
- Last updated: July 2026

## TL;DR

Connect Bubble to Clockify using the API Connector plugin with a custom X-Api-Key header (not Bearer). Clockify has two separate API surfaces: the main CRUD API for time entry management and a Reports API for pre-aggregated summaries via POST. Build a team time-tracking dashboard with date-range reporting — Clockify's unlimited-user free tier makes this the most cost-effective internal tool option in this category.

## Build a Clockify team dashboard in Bubble without coding

Clockify's appeal for early-stage teams is its completely free unlimited-user tier — everyone tracks time at no cost. But the built-in Clockify dashboard is generic. Building a Bubble app on top of Clockify's API lets you create a custom view: show each team member's hours for the current sprint, compare billable vs. non-billable time by project, or display weekly summaries alongside your CRM data.

The key insight that makes this integration non-obvious for Bubble beginners: Clockify has two separate API base URLs and authentication headers. The main API (`api.clockify.me/api/v1`) handles CRUD operations on time entries, projects, users, and workspaces. The Reports API (`reports.api.clockify.me/v1`) returns pre-aggregated summaries that would otherwise require summing thousands of individual time entries in Bubble workflows — a slow and WU-expensive approach.

Another non-obvious detail: Clockify uses `X-Api-Key` as the authentication header, not the standard `Authorization: Bearer`. Using the wrong header name is the single most common reason the integration fails during setup. This tutorial shows you exactly how to configure both API groups with the correct header format and build a working dashboard with aggregated reports.

## Before you start

- A Clockify account (free tier works for the main API; API access is available on all plans)
- The Clockify workspace ID — visible in the URL bar when you open Clockify: app.clockify.me/tracker (the segment after /tracker is not the workspace ID; look at your workspace URL in Settings → Workspace Settings)
- The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → search 'API Connector' by Bubble)
- Basic familiarity with Bubble's workflow editor, repeating groups, and input elements

## Step-by-step guide

### 1. Get your Clockify API key and workspace ID

Log into Clockify and navigate to your profile by clicking your avatar in the top-right corner → Profile Settings. Scroll to the bottom of the Profile Settings page and look for the 'API' section. Click 'Generate' to create your API key and copy it immediately.

Your Clockify workspace ID is found separately. Go to Settings → Workspace Settings in the left sidebar. The workspace ID appears in the browser URL bar as `app.clockify.me/settings/{workspaceId}`. Copy and save this ID — you will need it in every API call path.

Note that the API key is personal to your account. If you are building a team dashboard, you can use your own key to access workspace-wide data (as long as your account has workspace admin permissions). For apps where individual users log their own time, each user would need to provide their own API key.

**Expected result:** You have a Clockify API key (40-character hex string) and your workspace ID ready. Both are saved somewhere accessible during the Bubble setup process.

### 2. Create the Clockify Main API Connector group

In your Bubble app, open the Plugins tab and confirm the API Connector plugin (by Bubble) is installed. Click 'Add another API' and name it 'Clockify Main'.

IMPORTANT: Set the authentication type to 'No authentication' — do not use 'Bearer' or 'Basic Auth'. Instead, manually add a custom header. Click 'Add shared header for all calls' and enter:
- Key: `X-Api-Key`
- Value: your Clockify API key
- Tick the 'Private' checkbox

Clockify specifically requires the header name to be `X-Api-Key` — using `Authorization: Bearer your_key` causes a 401 error on every request. This is the single most common setup mistake for this integration.

Set the base URL to `https://api.clockify.me/api/v1`.

Add a Content-Type header: `Content-Type: application/json` (mark this one as non-private since it is not sensitive).

```
{
  "api_name": "Clockify Main",
  "base_url": "https://api.clockify.me/api/v1",
  "headers": [
    {
      "key": "X-Api-Key",
      "value": "<private>",
      "private": true
    },
    {
      "key": "Content-Type",
      "value": "application/json",
      "private": false
    }
  ]
}
```

**Expected result:** The 'Clockify Main' API group appears in your API Connector list with the X-Api-Key header showing a Private lock icon.

### 3. Add and initialize the projects and users calls

Inside the Clockify Main API group, click 'Add another call'. Name it 'Get Projects'. Set method to GET and path to `/workspaces/{workspaceId}/projects`. Add a path parameter: workspaceId (text, static — enter your actual workspace ID as the default value, or make it dynamic if the workspace ID varies).

Click 'Initialize call'. Bubble sends the real GET request and Clockify returns a JSON array of project objects. Bubble detects fields including id, name, hourlyRate, clientId, and color. Set 'Use as' to 'Data' and tick 'List'.

Add a second call: 'Get Users'. Path: `/workspaces/{workspaceId}/users`. Initialize with your workspace ID. This populates a team member dropdown for filtering the reports view.

For time entries, add call 'Get Time Entries': path `/workspaces/{workspaceId}/user/{userId}/time-entries` with both workspaceId and userId as parameters. Note that time entry durations return as ISO 8601 format strings (e.g., `PT2H30M` meaning 2 hours 30 minutes). In Bubble, display these in a text element using the raw string for now — Step 6 handles formatting.

```
GET https://api.clockify.me/api/v1/workspaces/{workspaceId}/projects
X-Api-Key: <private>

GET https://api.clockify.me/api/v1/workspaces/{workspaceId}/users
X-Api-Key: <private>

GET https://api.clockify.me/api/v1/workspaces/{workspaceId}/user/{userId}/time-entries
X-Api-Key: <private>
```

**Expected result:** Three initialized calls appear in the Clockify Main group. The projects call returns your workspace projects, users call returns team members, and time-entries call returns individual log entries with ISO 8601 duration strings.

### 4. Create the Clockify Reports API Connector group

The Reports API is a completely separate base URL and requires its own API Connector group. Click 'Add another API' again and name it 'Clockify Reports'.

Add the same X-Api-Key private header as before (the same API key works for both APIs). Set the base URL to `https://reports.api.clockify.me/v1`.

Add a call named 'Summary Report'. Set method to POST — this is critical. The Reports API endpoint is POST, not GET. Bubble beginners commonly configure this as GET and receive empty results with no error message.

Set the path to `/workspaces/{workspaceId}/reports/summary`. Add workspaceId as a path parameter. Set the body type to 'JSON' and add the request body:

Initialize the call with real values: use a date range from last week to today, and a grouping that suits your dashboard. After initialization, Bubble detects the response fields including totals and groupOne items.

Set 'Use as' to 'Data' — not a list, since the summary report returns a single object with nested groupings.

```
POST https://reports.api.clockify.me/v1/workspaces/{workspaceId}/reports/summary
X-Api-Key: <private>
Content-Type: application/json

{
  "dateRangeStart": "<dateRangeStart>",
  "dateRangeEnd": "<dateRangeEnd>",
  "summaryFilter": {
    "groups": ["USER", "PROJECT"]
  },
  "exportType": "JSON"
}
```

**Expected result:** The 'Clockify Reports' group has an initialized Summary Report POST call. Bubble shows the response structure including totals.totalTime, groupOne array, and individual member/project breakdown data.

### 5. Build the team timesheet repeating group

On your Bubble page, add two Date/Time Picker inputs: 'Week Start' and 'Week End'. Set their default values to the start and end of the current week using Bubble date expressions.

Add a Repeating Group. Set its data source to 'Get data from an external API → Clockify Reports - Summary Report'. Pass the date picker values as dateRangeStart and dateRangeEnd parameters (formatted as ISO 8601 strings).

Inside the repeating group cell, add:
- Text element: current cell's data's groupOne's name (user name)
- Text element: current cell's data's duration formatted (note: report durations come back in seconds as an integer — divide by 3600 to display hours)
- A horizontal bar element (a resizable Group with background color) whose width is a percentage of the max hours in the dataset

To show the hours-worked bar proportionally, set the Group width condition: '(current cell's groupOne's duration / max(all groupOne durations)) * 100' as a percentage. Bubble's expression editor supports basic math operations.

Add a 'Refresh' button that triggers the API call to reload when the date range changes.

**Expected result:** The repeating group displays team members with their total hours for the selected date range. Changing the date pickers and clicking Refresh re-runs the Reports API call and updates the display.

### 6. Handle pagination and add project-filter dropdown

Clockify's main API paginates time entries at a maximum of 200 entries per page (page-size parameter). For teams with high time entry volume, you need pagination. On Bubble's free plan, display only the first page (add `page-size=200` and `page=1` as query params). On paid plans, use a Backend Workflow loop to fetch subsequent pages.

For the dashboard filter, add a Dropdown element above the repeating group with its choices set to 'Get data from an external API → Clockify Main - Get Projects'. Show the project name field as the display. When the user selects a project, re-run the Summary Report call with an additional filter in the request body:

Also add an 'All Projects' option (value: empty string) that removes the project filter.

For the project breakdown view, add a second repeating group below the summary that shows individual time entries for the selected user and project using the Clockify Main 'Get Time Entries' call. RapidDev's team has built time-tracking dashboards exactly like this for multiple Bubble clients — if you want a custom solution with multi-workspace support or Stripe billing integration, book a free scoping call at rapidevelopers.com/contact.

```
// Reports API body with project filter
{
  "dateRangeStart": "<dateRangeStart>",
  "dateRangeEnd": "<dateRangeEnd>",
  "summaryFilter": {
    "groups": ["USER", "PROJECT"],
    "filterGroupIds": ["<projectId>"]
  },
  "exportType": "JSON"
}
```

**Expected result:** The dashboard has a project filter dropdown. Selecting a project re-runs the summary report filtered to that project. The time entries repeating group shows individual log lines for the selected user and project with ISO 8601 durations converted to human-readable format.

## Best practices

- Use the X-Api-Key header name exactly as written — case and hyphen placement matter. Mark it Private in Bubble API Connector so it stays server-side.
- Create two separate Bubble API Connector groups: one for api.clockify.me (CRUD) and one for reports.api.clockify.me (aggregated reports). Mixing them in one group causes URL conflicts.
- Prefer the Clockify Reports API for dashboard summaries instead of fetching all individual time entries and summing them in Bubble workflows. The Reports API is faster and uses far fewer Workload Units.
- Format date inputs as ISO 8601 strings before passing them to the Clockify API. Use Bubble's 'formatted as' date expression with format 'YYYY-MM-DDTHH:mm:ssZ'.
- Handle null duration values for running time entries with a Bubble conditional — show 'Running…' text instead of blank. This prevents confusing gaps in your repeating group.
- Add Privacy Rules (Data tab → Privacy) to any Clockify data you store in Bubble's database, restricting access to authenticated users with appropriate roles.
- For pagination, limit initial loads to the first 200 entries using page-size=200 and use a 'Load more' button that increments the page parameter rather than auto-loading all pages — this controls WU consumption and keeps page load fast.

## Use cases

### Weekly team hours dashboard by project

Build a Bubble page that shows each team member's logged hours for the current week grouped by project. Use the Clockify Reports API POST call with a JSON body specifying the date range and groupBy settings. Display the results in a repeating group with team member names and hours per project — no manual summing required.

Prompt example:

```
Create a Bubble page with a date range picker. When the date range changes, call the Clockify Reports API: POST https://reports.api.clockify.me/v1/workspaces/{workspaceId}/reports/summary with a JSON body containing dateRangeStart, dateRangeEnd, and groupings for user and project. Display results in a repeating group showing user name, project name, and total duration in hours.
```

### Time entry creation widget embedded in a Bubble CRM

Embed a Clockify time-logging widget directly into a Bubble CRM or project portal. Team members can select a project from a dropdown (populated from the Clockify projects endpoint) and log time without opening the Clockify app. The POST /time-entries call creates the entry on their behalf using their own API key.

Prompt example:

```
Add a 'Log Time' panel to a Bubble project record page. Include a project dropdown populated by GET https://api.clockify.me/api/v1/workspaces/{workspaceId}/projects. Add start time, end time inputs, and a description text input. On submit, call POST /workspaces/{workspaceId}/time-entries with a JSON body. Show a success message on completion.
```

### Billable hours report for client invoicing

Create a Bubble report page that totals billable hours by client for a selected date period, ready to reference when generating invoices. Use the Clockify Reports API detailed report endpoint to pull billable time entries grouped by client. Display a summary table with project name, billable hours, and estimated invoice amount based on a rate input.

Prompt example:

```
Build a Bubble page with a month picker and a rate-per-hour number input. Call the Clockify Reports API summary endpoint with billable=true filter. Display a repeating group grouped by project showing total billable hours, and calculate the invoice subtotal by multiplying hours by the rate input.
```

## Troubleshooting

### All API calls return 401 Unauthorized

Cause: The API key is invalid, expired, or configured with the wrong header name. The most common cause is using 'Authorization: Bearer' instead of 'X-Api-Key' as the header name.

Solution: Open your Clockify Profile Settings → API section and verify your API key. In the Bubble API Connector, confirm the header key is exactly 'X-Api-Key' (not 'Authorization', not 'X-API-Key'). Regenerate the API key in Clockify if needed and update the header value in Bubble.

### The Summary Report POST call returns empty results or a 400 error

Cause: The Reports API call is configured as GET instead of POST, or the request body is missing or malformed. The Reports API requires POST with a JSON body containing dateRangeStart, dateRangeEnd, and summaryFilter.

Solution: In the Bubble API Connector call for Summary Report, confirm the method is set to POST (not GET). Confirm the body type is 'JSON' and the body contains valid JSON with dateRangeStart and dateRangeEnd fields in ISO 8601 format (e.g., '2026-07-01T00:00:00Z'). Use Bubble's 'Initialize call' to send a test request and confirm a valid response.

### Initialize call fails with 'There was an issue setting up your call'

Cause: Bubble's API Connector requires a real successful response to detect field structure. This error appears when the API returns an empty response, a non-200 status, or when the call parameters are incomplete during initialization.

Solution: Enter valid static default values for all parameters before initializing. For the workspace ID parameter, enter your actual workspace ID as the default value. For date range parameters in the Reports API, enter real ISO 8601 date strings (e.g., '2026-07-01T00:00:00Z' and '2026-07-07T23:59:59Z'). Bubble must receive a real JSON response with data to detect field names.

### Running time entries show null or blank duration in the repeating group

Cause: Active (currently running) Clockify time entries have no end time and therefore no calculated duration — the timeInterval.end and timeInterval.duration fields are null.

Solution: Add a conditional to the duration text element in your repeating group: 'When this element's data source's timeInterval duration is empty, display Running… instead of the duration value.' Use a different text color (orange) to visually distinguish active from completed entries.

### Backend workflow pagination is greyed out or unavailable

Cause: Backend Workflows (API Workflows) are a paid Bubble feature not available on the Free plan.

Solution: Upgrade to Bubble's Starter plan ($32/mo) to access Backend Workflows. As a workaround on the free plan, add page-size=200 to your time entries call and accept the 200-entry limit for the initial display. For the Reports API, pagination is less of an issue since the summary endpoint aggregates data server-side.

## Frequently asked questions

### Why do I need two separate API Connector groups for Clockify?

Clockify has two completely separate API servers with different base URLs: api.clockify.me for CRUD operations (creating, reading, updating time entries, projects, and users) and reports.api.clockify.me for aggregated report data. Bubble API Connector groups are scoped to one base URL, so you need two separate groups. Both groups use the same X-Api-Key authentication header.

### What is ISO 8601 duration format and how do I display it in Bubble?

Clockify returns individual time entry durations as ISO 8601 duration strings like PT2H30M (2 hours 30 minutes) or PT45M (45 minutes). Bubble cannot directly parse this format into numbers. To display hours and minutes, either use the Reports API which returns durations in seconds (divide by 3600 for hours), or use Bubble's text operators to extract the number between 'H' and the end of the string. For display purposes, the Reports API seconds-based format is far easier to work with.

### Does Clockify's free plan include API access?

Yes. Clockify's REST API is available on all plans including the free tier. The free plan supports unlimited users and unlimited time tracking. Some advanced features like custom fields and SSO require paid plans, but the core time entries, projects, users, and reports endpoints are accessible with a free account.

### Can multiple Bubble users log their own time using their own Clockify accounts?

Yes, but you need to collect and store each user's API key securely in Bubble. Create a Bubble User data type field (type: text, name: clockify_api_key) and let users enter their own key in their profile settings. When making Clockify API calls, dynamically pass the current user's API key as the X-Api-Key header value. Note: storing API keys in Bubble user records requires robust privacy rules to prevent key exposure.

### How do I get pre-aggregated team reports instead of summing thousands of individual time entries?

Use the Clockify Reports API (reports.api.clockify.me) instead of the main API. The Summary Report endpoint accepts a POST request with a date range and grouping settings, and returns pre-computed totals — total hours per user, per project, or per client. This is much faster than fetching all individual time entries and summing them in Bubble workflows, and it uses far fewer Workload Units.

---

Source: https://www.rapidevelopers.com/bubble-integrations/clockify
© RapidDev — https://www.rapidevelopers.com/bubble-integrations/clockify
