TimeTracker

Privacy-focused time tracking for Android, with a REST API for integrations

View the Project on GitHub olivernybroe/TimeTracker

TimeTracker

A simple, privacy-focused time tracking app for Android.

Features

Download

Get it on Google Play


API Documentation

TimeTracker provides a REST API for third-party integrations. Use it to check your tracking status, toggle tracking, query time entries, and read progress metrics from external tools, scripts, or smart home automations.

Base URL:

https://europe-west1-timetracker-22fbb.cloudfunctions.net/api

Authentication

All endpoints require a Firebase ID token in the Authorization header:

Authorization: Bearer <FIREBASE_ID_TOKEN>

To obtain a token, sign in with Google via Firebase Auth and call getIdToken() on the Firebase user object.

Error responses:

Status Body Cause
401 { "error": "Missing or malformed Authorization header" } Header absent or not in Bearer <token> format
401 { "error": "Invalid or expired token" } Token failed verification

Endpoints

GET /tracking/status

Returns the current tracking state.

Response:

{
  "isTracking": false,
  "startTime": 0
}
Field Type Description
isTracking boolean Whether the timer is currently running
startTime number Epoch milliseconds when tracking started, or 0 if idle

Calculating elapsed time: If isTracking is true, compute elapsed milliseconds as Date.now() - startTime. The API intentionally does not return elapsed time because it would be stale by the time you read it.


POST /tracking/toggle

Toggles the tracking state. If idle, starts tracking. If tracking, stops and creates a time entry.

Request body: None required.

Response when starting:

{
  "isTracking": true,
  "startTime": 1738857600000
}

Response when stopping:

{
  "isTracking": false,
  "startTime": 0,
  "entry": {
    "id": "abc123",
    "startTime": 1738857600000,
    "endTime": 1738861200000,
    "type": "WORK",
    "duration": 3600000
  }
}
Field Type Description
isTracking boolean New tracking state after toggle
startTime number Start time if now tracking, 0 if stopped
entry object Only present when stopping. The created time entry
entry.id string Firestore document ID of the new entry
entry.startTime number Epoch ms when the session began
entry.endTime number Epoch ms when the session ended
entry.type string Always "WORK"
entry.duration number Duration in milliseconds

GET /progress

Returns pre-computed progress metrics. This data is automatically recalculated by a Cloud Function whenever time entries, tracking state, or settings change.

Response:

{
  "daily": {
    "hoursTracked": 3.5,
    "goal": 8.0,
    "progress": 0.4375
  },
  "weekly": {
    "hoursTracked": 22.0,
    "goal": 37.5,
    "progress": 0.5867
  },
  "monthly": {
    "hoursTracked": 88.0,
    "goal": 160.0,
    "progress": 0.55
  },
  "tracking": {
    "isTracking": true,
    "startTime": 1738857600000
  },
  "overtimeCarryover": 2.5,
  "weeksHistory": [],
  "lastUpdated": 1738861200000
}
Field Type Description
daily ProgressMetric Today’s progress
weekly ProgressMetric This week’s progress (with overtime adjustment)
monthly ProgressMetric This month’s progress
tracking object Current tracking state
overtimeCarryover number Hours of overtime carried from previous week
weeksHistory array See GET /history for the shape
lastUpdated number\|null Epoch ms when progress was last recomputed

ProgressMetric:

Field Type Description
hoursTracked number Hours tracked in this period (excludes active session)
goal number Target hours for this period
progress number Ratio hoursTracked / goal, clamped to 0.0-1.0

Live progress during tracking: The hoursTracked values do not include the currently active session. To display live progress, add (Date.now() - tracking.startTime) / 3600000 to the hoursTracked values when tracking.isTracking is true.

Error: Returns 404 with { "error": "No progress data found. Start tracking to generate data." } if no computed data exists yet.


GET /entries

Returns time entries with optional filtering and pagination.

Query parameters:

Parameter Type Default Description
start number Filter entries with startTime >= start (epoch ms)
end number Filter entries with startTime <= end (epoch ms)
limit number 500 Max entries to return (capped at 1000)

Example:

GET /entries?start=1738368000000&end=1738972800000&limit=100

Response:

{
  "entries": [
    {
      "id": "abc123",
      "startTime": 1738857600000,
      "endTime": 1738861200000,
      "type": "WORK",
      "duration": 3600000
    }
  ],
  "count": 1
}
Field Type Description
entries array Time entries, sorted by startTime descending
entries[].id string Firestore document ID
entries[].startTime number Epoch ms
entries[].endTime number Epoch ms
entries[].type string Entry type (e.g., "WORK")
entries[].duration number Duration in milliseconds
count number Number of entries returned

GET /history

Returns aggregated weekly history with overtime tracking.

Response:

{
  "weeksHistory": [
    {
      "year": 2026,
      "week": 6,
      "weekStartMillis": 1738540800000,
      "weekEndMillis": 1739145599999,
      "totalDuration": 144000000,
      "standardWeeklyGoal": 40,
      "effectiveWeeklyGoal": 37.5,
      "overtimeFromPreviousWeek": 2.5,
      "overtimeGenerated": 1.0,
      "dailyTotals": {
        "2": 28800000,
        "3": 28800000,
        "4": 28800000,
        "5": 28800000,
        "6": 28800000
      },
      "dailyGoals": {
        "2": 8.0,
        "3": 8.0,
        "4": 8.0,
        "5": 8.0,
        "6": 8.0
      }
    }
  ],
  "overtimeCarryover": 1.0
}
Field Type Description
weeksHistory array Weeks sorted by most recent first
weeksHistory[].year number Year
weeksHistory[].week number ISO week number
weeksHistory[].weekStartMillis number Epoch ms of week start
weeksHistory[].weekEndMillis number Epoch ms of week end
weeksHistory[].totalDuration number Total tracked time in milliseconds
weeksHistory[].standardWeeklyGoal number Base weekly goal in hours
weeksHistory[].effectiveWeeklyGoal number Goal after overtime deduction
weeksHistory[].overtimeFromPreviousWeek number Overtime hours carried in
weeksHistory[].overtimeGenerated number Overtime hours generated this week
weeksHistory[].dailyTotals object Tracked ms per day. Keys are day-of-week ("1"=Sunday, "2"=Monday, …, "7"=Saturday)
weeksHistory[].dailyGoals object Goal hours per day. Same key format as dailyTotals
overtimeCarryover number Current overtime carryover in hours

Error Handling

All endpoints return JSON error objects on failure:

Status Body Cause
401 { "error": "..." } Authentication failed
404 { "error": "..." } Resource not found
500 { "error": "Internal server error" } Unexpected server error

Example: cURL

# Get a Firebase ID token (you'd normally get this from your app)
TOKEN="your-firebase-id-token"

# Check tracking status
curl -H "Authorization: Bearer $TOKEN" \
  https://europe-west1-timetracker-22fbb.cloudfunctions.net/api/tracking/status

# Start/stop tracking
curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://europe-west1-timetracker-22fbb.cloudfunctions.net/api/tracking/toggle

# Get today's progress
curl -H "Authorization: Bearer $TOKEN" \
  https://europe-west1-timetracker-22fbb.cloudfunctions.net/api/progress

# Get this week's entries
curl -H "Authorization: Bearer $TOKEN" \
  "https://europe-west1-timetracker-22fbb.cloudfunctions.net/api/entries?start=1738540800000&end=1739145599999"

# Get weekly history
curl -H "Authorization: Bearer $TOKEN" \
  https://europe-west1-timetracker-22fbb.cloudfunctions.net/api/history

Privacy

TimeTracker is privacy-focused. All data is stored locally by default. Cloud sync via Google Sign-In is completely optional.

Privacy Policy

Contact

Questions? Email [email protected]