CLI tool for automating time tracking on Holded
Project description
Holded TT
holded-tt is an unofficial Python CLI for automating time tracking on Holded.
It is a focused time-tracking tool, not a general-purpose Holded client.
[!IMPORTANT] This project is not affiliated with, endorsed by, or supported by Holded. Holded names and marks belong to their respective owner.
Install
Requirements
- Python 3.11+
- A Holded account with time tracking enabled
- An interactive terminal for
holded-tt login(email/password prompt, plus 2FA prompt when required)
# Install from PyPI (recommended for end users)
pipx install holded-tt
# or
uv tool install holded-tt
# or, inside an existing virtualenv
pip install holded-tt
After installation, you can check the global CLI version with:
holded-tt --version
Quick start
# 1. Authenticate
holded-tt login
# 2. Check your workplace ID
holded-tt workplaces
# Optional: inspect your profile and organization directory
holded-tt employee
holded-tt organization
# 3. Save your defaults
holded-tt config set defaults.workplace_id <id>
holded-tt config set defaults.start 09:00
holded-tt config set defaults.end 18:00
holded-tt config set defaults.timezone Europe/Madrid
holded-tt config set defaults.pause 14:00-15:00
# 4. Register a date range (preview first)
holded-tt track --from 2026-01-07 --to 2026-04-07 --dry-run
holded-tt track --from 2026-01-07 --to 2026-04-07
Runtime storage
The CLI stores its runtime files in a per-user config directory resolved via platformdirs as holded-tt.
- Linux:
~/.config/holded-tt - macOS:
~/Library/Application Support/holded-tt - Windows:
C:\Users\<user>\AppData\Roaming\holded-tt
Files created there:
config.toml: saved CLI defaultssession.json: saved Holded cookies andsaved_attimestampholidays.json: cached workplace holidays used bytrack
config.toml is created automatically on first run if it does not exist. session.json permissions are restricted on a best-effort basis.
Commands
holded-tt login
Authenticates with Holded interactively and saves the session locally.
The command always prompts for your email and password. If Holded requires two-factor authentication, it then prompts for the 2FA code and completes the login flow.
holded-tt login
holded-tt session
Shows the saved session status, validation mode, and when it was last refreshed. By default it validates the saved cookies live against Holded using /internal/real-time/discover.
holded-tt session
holded-tt session --live
holded-tt session --offline
--live checks the current session against Holded. --offline only inspects the locally saved cookies and timestamp.
holded-tt workplaces
Lists all workplace IDs and names available in your account. Use the ID to configure your default workplace.
holded-tt workplaces
holded-tt employee
Shows your merged employee profile by combining the Holded employee and personal-info endpoints.
holded-tt employee
holded-tt organization
Lists organization employees from the Holded Teamzone organization endpoint.
holded-tt organization
holded-tt track
Registers working days on Holded for a date range. Weekends and workplace holidays are skipped automatically.
# Register a full month
holded-tt track --from 2026-04-01 --to 2026-04-30
# Register today only
holded-tt track --today
# Preview without submitting
holded-tt track --from 2026-04-01 --to 2026-04-30 --dry-run
# Override defaults for a single run
holded-tt track --from 2026-04-01 --to 2026-04-30 \
--start 08:30 --end 17:00 \
--pause 13:00-14:00 \
--workplace <id>
# Include weekends or holidays
holded-tt track --from 2026-04-01 --to 2026-04-30 \
--include-weekends \
--include-holidays
# Skip confirmation for large submissions
holded-tt track --from 2026-01-01 --to 2026-12-31 --yes
Notes:
- Use either
--todayor both--from YYYY-MM-DDand--to YYYY-MM-DD. holded-tt track --todayuses your configured defaults (workplace_id,start,end,timezone,pause) and still skips weekends and holidays unless you opt in with--include-weekendsor--include-holidays.--pauseis repeatable and must useHH:MM-HH:MMformat.- Submissions larger than 10 resulting days require confirmation unless you pass
--yes. --dry-runstill usesholidays.jsonif that cache already exists.--dry-rundoes not fetch missing holidays from Holded. If the holiday cache is missing, the preview falls back to no holiday filtering for those uncached years.
Subcommands:
# Inspect tracked entries and tracker IDs for a single day
holded-tt track show --date 2026-04-10
# Inspect a date range
holded-tt track show --from 2026-04-07 --to 2026-04-10
# Update a date range, one existing tracker per day
holded-tt track update --from 2026-04-07 --to 2026-04-10 --end 17:00
# Include weekends or holidays when updating a range
holded-tt track update --from 2026-04-07 --to 2026-04-10 \
--include-weekends --include-holidays \
--end 17:00
# Update a single existing tracker by its tracker ID
holded-tt track update --date 2026-04-10 --tracker-id <tracker_id> --end 17:00
# Replace the pause windows explicitly
holded-tt track update --date 2026-04-10 --tracker-id <tracker_id> \
--start 08:30 --end 17:00 \
--pause 14:00-14:30
Notes for track show and track update:
track show --dateuses Holded's single-day timetracking endpoint.track show --from/--touses the range endpoint.track showis intended to help you discover thetracker.idvalues returned by Holded and inspect the currentTime,Worked,Pauses,Status,Approved,Method, andRemotefields.track updateupdates either a single tracker (--date+--tracker-id) or a date range (--from/--toor--today).- Single-tracker updates require both
--tracker-idand either--dateor--today. - Range updates filter weekends and workplace holidays by default, just like
holded-tt track. Use--include-weekendsand--include-holidaysto opt in. These flags only affect range updates. - Range updates are strict: each target day must have exactly one existing tracker. Days with zero trackers, multiple trackers, or running trackers raise an error.
- Range updates are processed one tracker at a time, not atomically. If a later day fails, earlier days in the same command may already have been updated.
- If
--startor--endis omitted duringtrack update, the CLI keeps the existing value from Holded. - If
--pauseis omitted duringtrack update, the CLI keeps the existing pauses. If you pass--pause, it replaces the current pauses with the provided list. - Existing pauses are displayed in
track showand update previews asHH:MM -> HH:MM.
holded-tt clock
Real-time clock-in, clock-out, pause, and resume.
# Show current tracker status
holded-tt clock
holded-tt clock status
# Start and stop a live tracker
holded-tt clock in
holded-tt clock out
# Pause and resume the active tracker
holded-tt clock pause
holded-tt clock resume
holded-tt clock without a subcommand behaves like a status check.
holded-tt export
Exports time-tracking records for a date range as PDF or Excel.
The PDF is the official Holded report. The Excel replicates the official layout: company, employee, month title, and a row per calendar day with schedule, hours, workplace, and approval status.
# Export as PDF (default)
holded-tt export --from 2026-04-01 --to 2026-04-30
# Export as Excel
holded-tt export --from 2026-04-01 --to 2026-04-30 --format xlsx
# Include company name in the Excel header
holded-tt export --from 2026-04-01 --to 2026-04-30 --format xlsx --company "ACME S.L."
# Save to a specific path
holded-tt export --from 2026-04-01 --to 2026-04-30 --out ~/Desktop/abril.pdf
If --out is omitted, the file is saved to the current directory as holded-tt-{from}_{to}.{format}.
holded-tt config
Inspects or updates local defaults. Run without arguments to show current values.
holded-tt config
holded-tt config show
holded-tt config set <key> <value>
Actual built-in defaults used when config.toml is first created:
defaults.workplace_id: emptydefaults.start:08:30defaults.end:17:30defaults.timezone:Europe/Parisdefaults.pause: empty list ([])
Examples below are sample values you may want to change, not the shipped defaults.
Available keys:
| Key | Description | Example |
|---|---|---|
defaults.workplace_id |
Default workplace for track |
a3f9c12b7e4d8... |
defaults.start |
Default work start time (HH:MM) | 09:00 |
defaults.end |
Default work end time (HH:MM) | 18:00 |
defaults.timezone |
Timezone for submitted entries | Europe/Madrid |
defaults.pause |
Default pauses (comma-separated) | 14:00-14:30 |
holded-tt config set defaults.workplace_id <workplace_id>
holded-tt config set defaults.start 09:00
holded-tt config set defaults.end 18:00
holded-tt config set defaults.timezone Europe/Madrid
holded-tt config set defaults.pause 14:00-14:30,17:00-17:15
Session troubleshooting
- Run
holded-tt sessionto inspect whether a saved session exists, whether live validation succeeds, and when it was last refreshed. - Use
holded-tt session --offlineif you only want to inspect the locally saved cookies and timestamp. - If commands report that no saved session is available or that the session is expired, run
holded-tt loginagain. - If
holded-tt loginfails during 2FA, verify the code and try again.
Development
# Install runtime dependencies only
uv sync
# Install dev dependencies
uv sync --dev
# Run the CLI locally
uv run holded-tt --help
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=src --cov-report=term-missing
If you prefer plain pip for local development:
pip install -e .
Security note
session.json stores sensitive authentication cookies. The CLI must never log those cookies, and file permissions are applied on a best-effort basis (0600 where supported).
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file holded_tt-1.1.0.tar.gz.
File metadata
- Download URL: holded_tt-1.1.0.tar.gz
- Upload date:
- Size: 86.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
349e51acbd09a15a3735eb75ce6450ddd8955be2639c1cdec1974f07f8dac0dc
|
|
| MD5 |
e915402305ffc0524724d23d783d6172
|
|
| BLAKE2b-256 |
da695b6493cb0acfa7f968864c234361bb0d3a85f6c8a6f23b138021710591e9
|
Provenance
The following attestation bundles were made for holded_tt-1.1.0.tar.gz:
Publisher:
cd.yml on Villoh/holded-tt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
holded_tt-1.1.0.tar.gz -
Subject digest:
349e51acbd09a15a3735eb75ce6450ddd8955be2639c1cdec1974f07f8dac0dc - Sigstore transparency entry: 1396361300
- Sigstore integration time:
-
Permalink:
Villoh/holded-tt@dce3520d1eeb7a78999e56b55f4df710bf38c730 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/Villoh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@dce3520d1eeb7a78999e56b55f4df710bf38c730 -
Trigger Event:
push
-
Statement type:
File details
Details for the file holded_tt-1.1.0-py3-none-any.whl.
File metadata
- Download URL: holded_tt-1.1.0-py3-none-any.whl
- Upload date:
- Size: 40.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3dadcfe8ff1358f43fd0bf4189db11d459183cb333777b5f5a6b69482398411
|
|
| MD5 |
53fd5137ac5bd2aa61c3b42bdceb3189
|
|
| BLAKE2b-256 |
fd99a191154105cc43df1cef1c812b32e8c0932526152b0cf394c6e59e112578
|
Provenance
The following attestation bundles were made for holded_tt-1.1.0-py3-none-any.whl:
Publisher:
cd.yml on Villoh/holded-tt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
holded_tt-1.1.0-py3-none-any.whl -
Subject digest:
d3dadcfe8ff1358f43fd0bf4189db11d459183cb333777b5f5a6b69482398411 - Sigstore transparency entry: 1396361302
- Sigstore integration time:
-
Permalink:
Villoh/holded-tt@dce3520d1eeb7a78999e56b55f4df710bf38c730 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/Villoh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@dce3520d1eeb7a78999e56b55f4df710bf38c730 -
Trigger Event:
push
-
Statement type: