How to migrate from Clockify

This guide shows you how to migrate a Clockify workspace into Solidtime using Solidtime’s built-in importer. It was validated on the “Klein & Partner KG” workspace (1808 time entries, 12 clients, 24 projects, 28 tasks, 11 tags, 3 members).

Important

Use the Clockify Time Entries import type, not Clockify Projects. “Clockify Projects” imports only the project structure — no time entries and no members. “Clockify Time Entries” imports the detailed report and creates clients, projects, tasks, tags, placeholder members (keyed by email), and all time entries.

1. Export the detailed report from Clockify

Set the Clockify language to English, so Billable reads Yes or No.

Set the date and time format to US, so the importer can parse it. In Profile Settings, set the date format to MM/DD/YYYY (month first) and the time format to 12-hour (AM/PM). The importer parses m/d/Y h:i A, and a day-first export fails with “please select the correct date format before exporting from Clockify”.

Open Reports ‣ Detailed, set the date range to cover everything, and apply no user or project filters.

Select Export ‣ Save as CSV.

2. Add the missing Billable column

On Clockify plans without billable tracking, the detailed CSV lacks the required Billable column. The importer requires it with values of exactly Yes or No.

The required header has 13 columns:

Project, Client, Description, Task, User, Group, Email, Tags,
Billable, Start Date, Start Time, End Date, End Time

If Billable is missing, add it; the importer ignores extra columns. This script defaults every entry to billable:

import csv, glob
src = max(glob.glob('/home/jensens/Downloads/Clockify_Time_Report_Detailed_*.csv'),
          key=lambda p: sum(1 for _ in open(p, encoding='utf-8-sig')))
dst = src.rsplit('.', 1)[0] + '_for_solidtime.csv'
rows = list(csv.reader(open(src, newline='', encoding='utf-8-sig')))
header = rows[0] + (['Billable'] if 'Billable' not in rows[0] else [])
bi = header.index('Billable')
out = [header]
for r in rows[1:]:
    if not any(c.strip() for c in r):
        continue
    r = list(r) + [''] * (len(header) - len(r))
    r[bi] = 'Yes'
    out.append(r)
csv.writer(open(dst, 'w', newline=''), quoting=csv.QUOTE_ALL).writerows(out)

Billability is editable per project or entry in Solidtime afterward.

3. Import into Solidtime

Open Import, choose the import type Clockify Time Entries, and upload the CSV. If a timezone field appears, choose Europe/Vienna, so the local wall-clock times convert to UTC correctly. Select Import Data.

Warning

Upload the file exactly once. Members, clients, projects, tasks, and tags are get-or-create, but time entries are not deduplicated — re-uploading the same file duplicates every entry. The whole import runs in a single database transaction, so a failure rolls back completely and leaves no partial state. If the import times out on a very large file (the Octane limit is about 60 seconds), split the CSV by date range and import the chunks in sequence.

4. Verify the import

kubectl exec -n solidtime solidtime-postgres-1 -c postgres -- psql -U postgres -d solidtime -c "SELECT m.role, u.name, u.email, (SELECT count(*) FROM time_entries t WHERE t.member_id=m.id) AS entries FROM members m JOIN users u ON u.id=m.user_id ORDER BY entries DESC;"

Each imported person appears as a placeholder member with their entry count. An already-registered user, matched by email, keeps their real role.

5. Invite the migrated members

The imported people are placeholder members. Invite them so they can claim their account and keep their attributed entries. Use invite-placeholder, not a fresh invitation — see How to manage members.