Copy a week's worth of time entries from Intervals to FreshBooks. Use when asked to sync time entries between Intervals and FreshBooks.
Resources
2Install
npx skillscat add olivoil/om-skills/intervals-to-freshbooks Install via the SkillsCat registry.
Intervals → FreshBooks Time Entry Sync
Copy weekly time entries from Intervals to FreshBooks.
Reading from Intervals: Browser automation via MCP chrome-devtools
Writing to FreshBooks: API via scripts/freshbooks-api.sh
Prerequisites
- Chrome/Chromium running with
--remote-debugging-port=9222 - Intervals weekly timesheet open:
https://bhi.intervalsonline.com/time/ - chrome-devtools MCP server configured
- FreshBooks API credentials configured in
~/.config/freshbooks/credentials.json
Workflow
Phase 1: Verify Prerequisites
- Call
list_pagesto find Intervals browser tab - Find Intervals tab (URL contains
intervalsonline.com/time/) - If missing, inform user and stop
Phase 2: Read from Intervals
- Select the Intervals tab using
select_page - Run
scripts/read-intervals.jsusingevaluate_script - Display the entries to user for review
The script reads from the summary table which has this structure:
td.col-timesheet-clientprojectcontains "Client\nProject"- Following cells contain: Billable, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Total
Output format:
{
success: true,
week: "January 05, 2026",
entries: [
{ client: "Technomic", project: "Ignite App...", billable: true,
hours: { mon: 7.5, tue: 4.5, wed: 4.5, thu: 0, fri: 4.5, sat: 0, sun: 0 },
totalHours: 21 }
],
grandTotal: 47.75
}Phase 3: Map Entries
For each Intervals entry, determine the FreshBooks destination:
- Client is always required for invoicing (defaults to "EXSquared")
- Project is optional - use when work is for a specific project
- If unmapped, ask user for the FreshBooks mapping
- Update
references/project-mappings.mdwith new mappings
Mapping examples:
| Intervals Client | Intervals Project | FB Client | FB Project |
|---|---|---|---|
| Technomic | Ignite App... | EXSquared | Technomic |
| EWG - Neuron | Feature Enhancement | EXSquared | EWG |
| EX Squared Services | Meeting | EXSquared | (none) |
| EX Squared Services | Biz Dev / Sales | EXSquared | (none) |
Phase 4: Create FreshBooks Time Entries via API
Use scripts/freshbooks-api.sh to create time entries:
# List available projects and clients
./scripts/freshbooks-api.sh projects
./scripts/freshbooks-api.sh clients
# Create a time entry
./scripts/freshbooks-api.sh create-time-entry \
--project "<name>" | --client "<name>" \
--date <YYYY-MM-DD> \
--hours <hours> \
[--note "<note>"]For each Intervals entry with hours on a given day:
- Check mapping type (project or client)
- Call
create-time-entryfor each day with hours > 0 - Include a note (e.g., the Intervals project name or work type)
Examples:
# With project (client defaults to EXSquared)
./scripts/freshbooks-api.sh create-time-entry \
--project "Technomic" \
--date "2026-01-06" \
--hours 7.5 \
--note "Development"
# Client only - no project (e.g., internal meetings)
./scripts/freshbooks-api.sh create-time-entry \
--date "2026-01-06" \
--hours 1.0 \
--note "Meeting"
# Different client (rare)
./scripts/freshbooks-api.sh create-time-entry \
--client "Rocksauce Studios" \
--date "2026-01-06" \
--hours 1.0Phase 5: Verify
List created entries:
./scripts/freshbooks-api.sh list-time-entries --from "2026-01-05" --to "2026-01-11"Display summary of entries created
Open or refresh FreshBooks in the browser for visual review:
- Use
list_pagesto find FreshBooks tab - If found:
select_pagethennavigate_pagewithtype: "reload" - If not found:
new_pagewith FreshBooks week URL
URL format: https://my.freshbooks.com/#/time-tracking/week?week=YYYY-MM-DD (where YYYY-MM-DD is the Monday of the week)- Use
Scripts
read-intervals.js
Reads the Intervals weekly summary table. No configuration needed.
Run via evaluate_script - returns structured entry data.
freshbooks-api.sh
Shell script for FreshBooks API operations. Supports 1Password op:// references in credentials.
Commands:
projects- List all FreshBooks projectsclients- List all FreshBooks clientsproject-id <name>- Get project ID by nameclient-id <name>- Get client ID by namecreate-time-entry- Create a time entry--client, -c <name>- FreshBooks client (default: EXSquared)--project, -p <name>- FreshBooks project (optional)--date, -d <YYYY-MM-DD>- Date of the entry (required)--hours, -h <hours>- Hours worked (required)--note, -n <note>- Description (optional)
list-time-entries- List time entries--from, -f <YYYY-MM-DD>- Start date--to, -t <YYYY-MM-DD>- End date
Credentials: ~/.config/freshbooks/credentials.json
{
"client_id": "op://Private/Freshbooks API/username",
"client_secret": "op://Private/Freshbooks API/credential",
"redirect_uri": "https://localhost/callback"
}First-time setup:
./scripts/freshbooks-oauth.sh authorize # Get auth URL
./scripts/freshbooks-oauth.sh exchange <code> # Exchange code for tokens
./scripts/freshbooks-oauth.sh me # Test connectionKey Technical Details
Intervals Summary Table
- Rows:
trcontainingtd.col-timesheet-clientproject - Client/Project cell: text split by
\n(Client first, Project second) - Hour cells follow: index 2-8 for Mon-Sun, index 9 for Total
FreshBooks API
Time Entry endpoint: POST /timetracking/business/{business_id}/time_entries
Payload:
{
"time_entry": {
"is_logged": true,
"duration": 27000,
"note": "Development",
"started_at": "2026-01-06T09:00:00.000Z",
"project_id": 12447219,
"identity_id": 123456
}
}durationis in seconds (7.5 hours = 27000 seconds)started_atis the date of the entryproject_idis looked up by project nameidentity_idis the user's FreshBooks identity
Hour Format
- Intervals: decimal hours (e.g., "7.5")
- API: seconds (multiply by 3600)
Common Issues
Project not found: Run
./scripts/freshbooks-api.sh projectsto see available project names.Token expired: The script auto-refreshes tokens, but if it fails, run
./scripts/freshbooks-oauth.sh refresh.Week mismatch: Make sure you're reading the correct week from Intervals.
1Password CLI: If using
op://references, ensure you're signed in (op signin).