trackhs-calendar is a small Python CLI for syncing vacation-rental bookings from a TrackHS owner portal into Google Calendar.
The intended use case is a rental property calendar that family members or co-owners can subscribe to, so everyone can see when the property is booked.
At a high level, the tool:
- logs into a TrackHS owner portal
- exports the reservations CSV
- filters and normalizes reservation rows
- creates, updates, and deletes managed events in Google Calendar
- optionally sends monitoring alerts
The project uses uv and targets Python 3.13.
How It Works The sync is one-way:
- Fetch the latest reservation export from TrackHS.
- Apply booking filters from built-in defaults or
config.toml. - Render calendar event titles and descriptions from templates.
- Reconcile Google Calendar so managed events match the latest CSV.
The Google sync is idempotent. Each managed event stores its reservationId in private extended properties, so rerunning the tool updates only events owned by this integration.
Project Layout
.env: local secrets and runtime configuration, ignored by git.env.example: checked-in environment templateconfig.toml: optional local behavior override, ignored by gitconfig.toml.example: checked-in behavior exampledownload.csv: local export file, ignored by git
Environment Setup
Copy .env.example to .env and fill in the values you need.
TrackHS:
TRACKHS_BASE_URL: owner portal base URL, for examplehttps://example.trackhs.comTRACKHS_USERNAME: owner portal usernameTRACKHS_PASSWORD: owner portal password
Google:
GOOGLE_CALENDAR_ID: target Google Calendar IDGOOGLE_AUTH_MODE:oauthorservice_accountGOOGLE_CREDENTIALS_FILE: OAuth client JSON pathGOOGLE_TOKEN_FILE: OAuth token cache pathGOOGLE_SERVICE_ACCOUNT_FILE: service account JSON path
Optional monitoring:
TELEGRAM_BOT_TOKEN: Telegram bot tokenTELEGRAM_CHAT_ID: Telegram chat or group IDHEALTHCHECKS_PING_URL: Healthchecks ping URL
Git ignores local secret and auth files by default, including .env, credentials.json, token.json, service-account.json, and download.csv.
Behavior Configuration The app works without a config file by using built-in defaults.
If you want local behavior overrides, create config.toml from config.toml.example. Typical settings are:
- which reservation
typesandstatusesto include - how event
summaryanddescriptionare rendered - sync window and managed event marker
- terminal statuses to retain as calendar history
- notification policy for unknown values and zero-booking runs
sync.historical_statuses defaults to ["Checked Out"]. Within the configured
sync window, these statuses remain eligible even when active-booking filters
exclude them. Events older than the sync window are left untouched rather than
continuously reconciled, and managed events whose checkout date has passed are
preserved if a later TrackHS export no longer contains the reservation.
Template fields available in event strings:
reservation_idstatussource_typeunitguestbooked_datecheck_incheckout
You can also point at a different config file with --config path/to/config.toml.
Google Authentication Two auth modes are supported.
oauth:
- use a Google Cloud desktop OAuth client JSON
- first sync opens a browser for consent
- token refresh state is stored in
GOOGLE_TOKEN_FILE - best for local development and self-hosted runs
service_account:
- use a service account JSON key
- share the target Google Calendar with the service account email
- no browser flow and no token cache
- better for headless deployment such as GitHub Actions
Using a dedicated Google Calendar for the rental is strongly recommended.
Commands Fetch the latest CSV:
uv run --env-file .env trackhs-calendar fetchPreview normalized bookings:
uv run trackhs-calendar preview --prettyDry-run a Google Calendar sync:
uv run --env-file .env trackhs-calendar sync --dry-runRun the full fetch + sync flow:
uv run --env-file .env trackhs-calendar runVerbose dry-run:
uv run --env-file .env trackhs-calendar run --dry-run --verboseMachine-readable JSON output:
uv run --env-file .env trackhs-calendar run --dry-run --jsonOptional date range for TrackHS fetch:
uv run --env-file .env trackhs-calendar fetch \
--start-date 2026-01-01 \
--end-date 2026-12-31Output
Default output from fetch, sync, and run is human-readable text.
Use:
--verbosefor ignored-row samples and per-event action details--jsonfor machine-readable output
Dry-runs do not write to Google Calendar and do not send Healthchecks or Telegram notifications.
Monitoring
If configured, run can:
- send Healthchecks start, success, and failure pings
- send Telegram alerts for creates, updates, deletes, unexpected types or statuses, and zero-booking runs
This is useful for long-running scheduled deployments where you want to know:
- the script stopped running
- the script failed
- reservation data changed
- TrackHS started returning unexpected values
Deployment The recommended scheduled entrypoint is:
uv run --env-file .env trackhs-calendar runThis works well for:
- a local server with
cron,systemd, orlaunchd - GitHub Actions
- other headless schedulers
For GitHub Actions or other ephemeral runners, service_account mode is usually the simpler Google auth option.
GitHub Actions The repository includes one workflow:
.github/workflows/trackhs-calendar-sync.yml: daily sync plus manualworkflow_dispatch, with a scheduled keepalive job to prevent GitHub from auto-disabling the workflow in low-activity repositories
The sync workflow expects these repository secrets:
TRACKHS_BASE_URLTRACKHS_USERNAMETRACKHS_PASSWORDGOOGLE_CALENDAR_IDGOOGLE_SERVICE_ACCOUNT_JSONTELEGRAM_BOT_TOKENTELEGRAM_CHAT_IDHEALTHCHECKS_PING_URLTRACKHS_CALENDAR_CONFIG_TOML
Only the first five are required. TRACKHS_CALENDAR_CONFIG_TOML is optional and can contain the contents of a local config.toml if you want GitHub Actions to use a custom filter or event template.
The workflow passes secrets as environment variables at runtime. It only writes service-account.json and optional config.toml to disk inside the runner.
The workflow runs the default text output, not --verbose or --json, to reduce the chance of printing guest-level details into Actions logs.