Syncs TripIt travel itineraries to Reclaim.ai timezone segments and Google Calendar OOO blocks.
93
97%
Does it follow best practices?
Impact
85%
1.49xAverage score across 4 eval scenarios
Advisory
Suggest reviewing before use
Automatically syncs travel timezones from your TripIt trips to Reclaim.ai, so your scheduling links, habits, and working hours all adjust to wherever you're traveling.
Parses your TripIt iCal feed to extract timezones from flights and hotel stays, builds timezone segments for each trip, and pushes them to Reclaim's travel timezone settings via REST API. Optionally notifies via Telegram when changes are detected.
https://www.tripit.com/feed/ical/private/XXXXXXXX-XXXXXXXXXXXXXXXXXXXX/tripit.icsnpm install
# Dry run — shows what would be synced without making changes
TRIPIT_ICAL_URL="..." RECLAIM_API_TOKEN="..." node sync.mjs dry-run
# Full sync
TRIPIT_ICAL_URL="..." RECLAIM_API_TOKEN="..." node sync.mjs sync
# JSON output (for scripts, agents, or automation)
TRIPIT_ICAL_URL="..." RECLAIM_API_TOKEN="..." node sync.mjs sync --output=jsonThe --output=json flag works with both dry-run and sync modes. When set, the script outputs a single JSON object to stdout instead of human-readable text:
{
"mode": "sync",
"noChanges": false,
"timezoneChanges": [
{ "action": "create", "timezone": "America/Chicago", "from": "2026-04-01", "to": "2026-04-05" }
],
"segments": [
{ "timezone": "America/Chicago", "from": "2026-04-01", "to": "2026-04-05", "label": "KubeCon - Austin" }
],
"ooo": { "created": 2, "deleted": 1, "setToP2": 1 },
"conflicts": [
{ "trip1": "KubeCon", "trip2": "DevOps Days", "overlap": "2026-04-03" }
],
"errors": []
}Human-readable output remains the default.
Build the image:
docker build -t tripit-reclaim-sync .Run the container:
docker run -d \
--name tripit-reclaim-sync \
--restart unless-stopped \
-e TRIPIT_ICAL_URL="https://www.tripit.com/feed/ical/private/YOUR-FEED-ID/tripit.ics" \
-e RECLAIM_API_TOKEN="your-reclaim-api-token" \
tripit-reclaim-syncThe container syncs immediately on startup, then daily at 3:00 AM.
If you're using a NAS or other Docker UI (Portainer, Synology, UGREEN, etc.), the environment variables will appear pre-populated in the container creation form — just fill in the values.
For a NAS or remote host with a different architecture, build for the target platform:
# For x86_64 NAS (Intel/AMD)
docker buildx build --platform linux/amd64 -t tripit-reclaim-sync .
# Export as tar.gz to transfer to the NAS
docker save tripit-reclaim-sync | gzip > tripit-reclaim-sync.tar.gzOn the NAS, load and run:
docker load < tripit-reclaim-sync.tar.gzFor a serverless deployment that runs as a scheduled ECS Fargate task (~$0.01/month), see AWS_DEPLOYMENT.md.
Install the Tessl tile to let any AI agent run the sync on your behalf:
tessl install jbaruch/reclaim-tripit-syncThe tile provides two skills:
sync-tripit — runs the sync with --output=json, interprets the result, and reports changes (or stays silent if nothing changed)onboard-tripit-reclaim — guided credential setup with dry-run validationThe agent downloads and installs the sync tool on first use — no pre-configuration needed beyond setting your environment variables. Telegram and SNS notification variables are not needed when running as a tile; the agent handles reporting.
Automatically creates Google Calendar Out-of-Office events for every future TripIt trip and sets their Reclaim priority to P2 (high) instead of the default P1 (critical).
Reclaim scheduling links respect priority levels. Google Calendar's built-in OOO events sync to Reclaim as P1 (critical), which means ALL your scheduling links treat those days as unavailable. That's usually fine — except when it isn't.
By creating our own OOO events at P2 priority, you get a useful split:
Use case: you're traveling but technically reachable. You want a booking link that says "I'm on a plane but sure, let's talk" for important meetings, while your regular links still show you as out of office.
# Open this URL in your browser (replace YOUR_CLIENT_ID):
# https://accounts.google.com/o/oauth2/v2/auth?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost&response_type=code&scope=https://www.googleapis.com/auth/calendar&access_type=offline&prompt=consent
# After authorizing, you'll be redirected to localhost with a ?code= parameter
# Exchange that code for tokens:
curl -s -X POST https://oauth2.googleapis.com/token \
-d "code=AUTH_CODE_FROM_REDIRECT" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "redirect_uri=http://localhost" \
-d "grant_type=authorization_code" | jq .refresh_tokenGOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKENThe feature auto-activates when all three are present. If any are missing, OOO sync is silently skipped.
On each sync run (after timezone sync):
[TripIt OOO] events in Google CalendarThe events show up in Google Calendar as proper OOO events with autoDeclineMode: declineNone — they mark your calendar as out-of-office without auto-declining meeting invites.
Some trips in TripIt don't need timezone sync or OOO blocks — family members traveling without you, trips you're tracking for logistics but not attending. Two env vars let you skip them:
TRIPIT_IGNORE_TRIPS — comma-separated trip names (case-insensitive substring match)TRIPIT_IGNORE_KEYWORDS — comma-separated keywords that match against any trip name# Skip a specific trip
TRIPIT_IGNORE_TRIPS="Family trip to Paris"
# Skip any trip with a family member's name in it
TRIPIT_IGNORE_KEYWORDS=alice,daniel,nicoleIgnored trips are excluded from both timezone segments and OOO calendar blocks. Their flights and lodging data won't leak into other trips' segments.
Get notified when timezone overrides change. To set up:
https://api.telegram.org/bot<TOKEN>/getUpdatesTELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID environment variablesWhen configured, you'll receive a message listing the new timezone overrides whenever the sync detects changes. If the variables are not set, notifications are silently skipped.
| Variable | Required | Description |
|---|---|---|
TRIPIT_ICAL_URL | Yes | Your private TripIt iCal feed URL |
RECLAIM_API_TOKEN | Yes | Reclaim.ai API token |
TELEGRAM_BOT_TOKEN | No | Telegram bot token for change notifications |
TELEGRAM_CHAT_ID | No | Telegram chat ID to send notifications to |
GOOGLE_CLIENT_ID | No | Google OAuth2 client ID (enables OOO blocks) |
GOOGLE_CLIENT_SECRET | No | Google OAuth2 client secret |
GOOGLE_REFRESH_TOKEN | No | Google OAuth2 refresh token |
TRIPIT_IGNORE_TRIPS | No | Comma-separated trip names to ignore (case-insensitive substring match) |
TRIPIT_IGNORE_KEYWORDS | No | Comma-separated keywords — any trip whose name contains one matches |
The tile is versioned (see .tile/CHANGELOG.md). The agent install URL in
skills/sync-tripit/SKILL.md points at a tagged tarball, so installs pull
the exact version the tile expects rather than whatever happens to be on
main.
.github/workflows/publish-tile.yml runs on every push to main that
touches .tile/**. It uses tesslio/patch-version-publish to:
tile.json is ahead → publish that version as-istile.json back to main with [skip ci]The workflow also creates and pushes the matching vX.Y.Z git tag so
the in-skill curl ... archive/refs/tags/vX.Y.Z.tar.gz URL always
resolves. It pre-tags the user-bumped version BEFORE publish (so
fresh installs of a deliberate minor/major release don't race), and
post-tags any action-bumped patch version too. Subsequent pushes that
don't change the version are no-ops on the tagging side.
The workflow needs a TESSL_TOKEN repo secret. Create one with
tessl api-key create --workspace <ws> --name "ci-publish" --role publisher
and add it under Settings → Secrets and variables → Actions.
For routine changes, just push — the action auto-patches. For a
deliberate minor or major release (e.g., the lodging-primary refactor,
which warranted 0.1.0 → 0.2.0):
# 1. Bump version + propagate to SKILL.md, eval criteria, research.md
.tile/scripts/bump-version.sh 0.3.0
# 2. Add an entry to .tile/CHANGELOG.md, commit, open a PR, mergeThe publish-tile workflow takes over from there: it pushes the
v0.3.0 tag at the merge commit, publishes the tile to the registry,
and tags any auto-bumped patch version too. No manual tagging needed.
v0.3.0) is what the in-skill install URL
resolves to. The skill's
curl ... archive/refs/tags/v<version>.tar.gz step downloads the
runtime library (sync.mjs) at install time.tessl install jbaruch/reclaim-tripit-sync
resolves to. It carries the tile bundle (rules + skills + manifest).Already-installed agent tiles continue to work on whatever version they
were pinned to; only fresh installs and tessl update pick up the new
version.