A TypeScript client library for the TimeCamp API.
Source code: https://github.com/timecamp-org/timecamp-api-ts
npm install timecamp-apiimport { TimeCampAPI } from 'timecamp-api'; const timecampApi = new TimeCampAPI("your-api-key"); // With custom configuration const timecampApi = new TimeCampAPI("your-api-key", { clientName: 'my-awesome-app', timeout: 15000 }); // Get current user const user = await timecampApi.user.get(); console.log(user); // Invite a user to your account await timecampApi.users.invite({ email: 'newuser@example.com', name: 'John Doe' }); // Get tasks const tasksResponse = await timecampApi.tasks.getActiveUserTasks({ user: 'me', includeFullBreadcrumb: true }); if (tasksResponse.success) { console.log(tasksResponse.data); // Array of non-archived tasks } // Get all tasks including archived ones const allTasksResponse = await timecampApi.tasks.getAll(); if (allTasksResponse.success) { console.log(allTasksResponse.data); // Active + archived tasks } // Get favourite tasks for the task picker widget const favourites = await timecampApi.tasks.getFavorites(); console.log(favourites.data.favourites); // Manage favourites await timecampApi.tasks.addFavorite(77390460); await timecampApi.tasks.removeFavorite(77189336); // Update a task and assign users await timecampApi.tasks.update({ task_id: 12345, name: 'Updated Task Name', user_ids: '100,200,300', // Comma-separated user IDs role: 5 // Role ID to assign }); // Tags and Tag Lists const tagLists = await timecampApi.tags.getTagLists(); const tagListId = await timecampApi.tags.createTagList({ name: 'Project Types' }); const tagId = await timecampApi.tags.createTag({ list: tagListId, name: 'Development' }); // Timer operations const timerStatus = await timecampApi.timer.status(); const startedTimer = await timecampApi.timer.start(); const stoppedTimer = await timecampApi.timer.stop(); // Time entries operations const timeEntries = await timecampApi.timeEntries.get({ date_from: '2024-01-01', date_to: '2024-01-31' }); const newEntry = await timecampApi.timeEntries.create({ date: '2024-01-15', duration: 3600, // 1 hour in seconds description: 'Working on API integration', start_time: '09:00:00', end_time: '10:00:00', tags: [{ tagId: 1 }, { tagId: 4 }] // Optional tags }); // Manage tags on time entries const entryTags = await timecampApi.timeEntries.getTags(entryId); await timecampApi.timeEntries.addTags(entryId, [5, 6]); await timecampApi.timeEntries.removeTags(entryId, [4]); const updatedEntry = await timecampApi.timeEntries.update(entryId, { description: 'Updated description', duration: 7200 // 2 hours }); const deleteResult = await timecampApi.timeEntries.delete(entryId); // Computer Activities (max 7 days, includes application names) const activities = await timecampApi.computerActivities.get({ date_from: '2024-01-01', date_to: '2024-01-07', }); // Each activity includes application_name resolved automatically // Computer activities for specific users const userActivities = await timecampApi.computerActivities.get({ date_from: '2024-01-01', date_to: '2024-01-03', user_ids: '123,456', }); // Billing Rates await timecampApi.billingRates.setTaskRate(12345, { rateTypeId: 1, value: 150 }); await timecampApi.billingRates.setUserRate(100, { rateTypeId: 1, value: 100 }); await timecampApi.billingRates.setTaskUserRate(12345, 100, { rateTypeId: 1, value: 175 }); const taskRates = await timecampApi.billingRates.getTaskRates(12345); // Groups const groups = await timecampApi.groups.getAll(); const newGroup = await timecampApi.groups.create({ name: 'Development Team', parent_id: 123 }); await timecampApi.groups.update({ group_id: newGroup.group_id, name: 'Dev Team' }); // Clients const clients = await timecampApi.clients.getAll(); const client = await timecampApi.clients.create({ organizationName: 'Acme Corp', firstName: 'John', lastName: 'Doe', email: 'john@acme.com', }); await timecampApi.clients.update({ clientId: client.clientId, organizationName: 'Acme Corp Updated' }); // Invoices const invoices = await timecampApi.invoices.getAll(); const invoice = await timecampApi.invoices.create({ clientId: client.clientId, issueDate: '2026-02-10', addDate: '2026-02-10', noteToClient: 'From 2026-01-26 to 2026-02-28', currencyId: 1, invoiceNumber: '1223', entries: [ { invoiceId: -1, invoiceEntryId: -1, description: '2026-01-26 07:53 ', type: 0, duration: 19980, quantity: 5.55, unitCost: 109, taxId: 198, name: '[ORG] Administration - Ewelina Łagos', subTotal: 604.95, ttEntriesIds: [263222996], }, ], });new TimeCampAPI(apiKey: string, config?: TimeCampAPIConfig)apiKey: Your TimeCamp API keyconfig: Optional configuration objectbaseURL: Custom API base URL (default: 'https://app.timecamp.com/third_party/api')timeout: Request timeout in milliseconds (default: 10000)
| Method | Description | Parameters | Returns |
|---|---|---|---|
user.get() | Get information about the current user | None | Promise<TimeCampUser> |
users.getAll() | Get all users | None | Promise<Record<string, any>> |
users.invite() | Invite a user to TimeCamp account | params: TimeCampUserInviteRequest | Promise<TimeCampUserInviteResponse> |
users.byId(id) | Chainable selector for a user resource | id: number | { getAllCustomFields, getCustomField, setCustomField, updateCustomField, deleteCustomField } |
tasks.byId(id) | Chainable selector for a task resource | id: number | { getAllCustomFields, getCustomField, setCustomField, updateCustomField, deleteCustomField } |
timeEntries.byId(id) | Chainable selector for a time entry resource | id: number | { getAllCustomFields, getCustomField, setCustomField, updateCustomField, deleteCustomField } |
customFields.getAll() | List all custom fields templates (v3) | None | Promise<{ data: TimeCampCustomFieldTemplate[] }> |
customFields.add(payload) | Create a custom field template (v3) | { name, resourceType, fieldType, required?, status?, defaultValue?, fieldOptions? } | Promise<{ data: TimeCampCustomFieldTemplate }> |
customFields.delete(templateId) | Remove a custom field template (v3) | templateId: number | Promise<{ data: string }> |
tasks.getAll() | Get every task including archived | None | Promise<TasksAPIResponse> |
tasks.getActiveUserTasks(options?: GetActiveUserTasksOptions) | Get all non-archived tasks | options: { user?: string; includeFullBreadcrumb?: boolean; } | Promise<TasksAPIResponse> |
tasks.add(params) | Create a new task | params: TimeCampCreateTaskRequest | Promise<TimeCampCreateTaskResponse> |
tasks.update(params) | Update an existing task | params: TimeCampUpdateTaskRequest | Promise<TimeCampCreateTaskResponse> |
tasks.getFavorites() | Fetch task picker favourites and suggestions | None | Promise<TimeCampTaskFavoritesResponse> |
tasks.addFavorite(taskId) | Mark a task as favourite for the picker | taskId: number | Promise<TimeCampTaskFavoriteMutationResponse> |
tasks.removeFavorite(taskId) | Remove a task from favourites | taskId: number | Promise<TimeCampTaskFavoriteMutationResponse> |
tags.getTagLists(options?) | Get all tag lists | options?: TimeCampGetTagListsOptions | Promise<TimeCampTagListsResponse> |
tags.getTagList(tagListId) | Get specific tag list with tags | tagListId: number | Promise<TimeCampTagListWithTags> |
tags.createTagList(params) | Create new tag list | params: { name: string } | Promise<number> |
tags.updateTagList(tagListId, params) | Update tag list | tagListId: number, params: { name?, archived? } | Promise<{ message: string }> |
tags.getTagListTags(tagListId) | Get only tags from tag list | tagListId: number | Promise<{ [tagId: string]: TimeCampTagItem }> |
tags.createTag(params) | Create new tag | params: { list: number, name: string } | Promise<number> |
tags.getTag(tagId) | Get tag data | tagId: number | Promise<TimeCampTagItem> |
tags.updateTag(tagId, params) | Update tag | tagId: number, params: { name?, archived? } | Promise<{ message: string }> |
timer.start() | Start a new timer | data?: TimerStartRequest | Promise<any> |
timer.stop() | Stop the currently running timer | data?: TimerStopRequest | Promise<any> |
timer.status() | Get the current timer status | None | Promise<any> |
timeEntries.get() | Get time entries | params?: TimeCampTimeEntriesRequest | Promise<TimeCampTimeEntry[]> |
timeEntries.create() | Create a new time entry | entry: TimeCampCreateTimeEntryRequest | Promise<TimeCampCreateTimeEntryResponse> |
timeEntries.update() | Update an existing time entry | id: number, data: Partial<TimeCampCreateTimeEntryRequest> | Promise<TimeCampCreateTimeEntryResponse> |
timeEntries.delete() | Delete a time entry | id: number | Promise<{success: boolean, message: string}> |
timeEntries.getTags(entryId) | Get tags for time entry | entryId: number | Promise<TimeCampEntryTagsResponse> |
timeEntries.addTags(entryId, tagIds) | Add tags to time entry | entryId: number, tagIds: number[] | Promise<string[]> |
timeEntries.removeTags(entryId, tagIds) | Remove tags from time entry | entryId: number, tagIds: number[] | Promise<string[]> |
computerActivities.get(params) | Get computer activities with app names | params: TimeCampComputerActivitiesRequest | Promise<TimeCampComputerActivity[]> |
billingRates.getTaskRates(taskId, rateTypeId?) | Get billing rates for task | taskId: number, rateTypeId?: string | Promise<TimeCampBillingRatesResponse> |
billingRates.setTaskRate(taskId, data) | Set/update task billing rate | taskId: number, data: TimeCampSetRateRequest | Promise<TimeCampBillingRate> |
billingRates.getUserRates(userId, rateTypeId?) | Get billing rates for user | userId: number, rateTypeId?: string | Promise<TimeCampBillingRatesResponse> |
billingRates.setUserRate(userId, data) | Set/update user billing rate | userId: number, data: TimeCampSetRateRequest | Promise<TimeCampBillingRate> |
billingRates.getTaskUserRates(taskId, userId, rateTypeId?) | Get task-user billing rates | taskId: number, userId: number, rateTypeId?: string | Promise<TimeCampBillingRatesResponse> |
billingRates.setTaskUserRate(taskId, userId, data) | Set/update task-user rate | taskId: number, userId: number, data: TimeCampSetRateRequest | Promise<TimeCampBillingRate> |
billingRates.getGroupRates(groupId, rateTypeId?) | Get billing rates for group | groupId: number, rateTypeId?: string | Promise<TimeCampBillingRatesResponse> |
billingRates.setGroupRate(groupId, data) | Set/update group billing rate | groupId: number, data: TimeCampSetRateRequest | Promise<TimeCampBillingRate> |
billingRates.getRateTypes() | Get all rate types (Internal API) | None | Promise<TimeCampRateType[]> |
groups.getAll() | Get all groups | None | Promise<TimeCampGroupsResponse[]> |
groups.create(params) | Create a new group | params: { name: string, parent_id?: number } | Promise<TimeCampGroup> |
groups.update(params) | Update an existing group | params: { group_id: number, name?, parent_id? } | Promise<void> |
groups.delete(groupId) | Delete a group | groupId: number | Promise<void> |
clients.getAll() | Get all clients | None | Promise<TimeCampClientsResponse> |
clients.create(params) | Create a new client | params: TimeCampCreateClientRequest | Promise<TimeCampClient> |
clients.update(params) | Update an existing client | params: TimeCampUpdateClientRequest | Promise<TimeCampClient> |
clients.delete(clientId) | Delete a client | clientId: number | Promise<void> |
invoices.getAll() | Get all invoices | None | Promise<TimeCampInvoicesResponse> |
invoices.create(params) | Create a new invoice from time entries | params: TimeCampCreateInvoiceRequest | Promise<TimeCampInvoice> |
invoices.update(params) | Update an existing invoice | params: TimeCampUpdateInvoiceRequest | Promise<TimeCampInvoice> |
invoices.delete(invoiceId) | Delete an invoice | invoiceId: number | Promise<void> |
Get information about the current user.
Returns: Promise<TimeCampUser>
interface TimeCampUser { user_id: string; email: string; register_time: string; display_name: string; synch_time: string; root_group_id: string; }Get all non-archived tasks accessible to a user.
Parameters:
options.user(optional): Defaults to'me'. Pass a numerical user ID string to fetch tasks for a different user.options.includeFullBreadcrumb(optional): Defaults totrue. Controls whether full breadcrumb information is included in the API response.
Returns: Promise<TasksAPIResponse>
interface TasksAPIResponse { success: boolean; data?: TimeCampTask[]; message?: string; error?: string; } interface TimeCampTask { task_id: number; parent_id: number; assigned_by?: number; name: string; external_task_id?: string; external_parent_id?: string; task_key?: string | null; level: number; archived: number; keywords?: string; budgeted?: number; budget_unit: string; root_group_id?: number; billable: number; note?: string; public_hash?: string | null; add_date?: string; modify_time?: string; color?: string; user_access_type: number; users?: { [userId: string]: { user_id: number; role_id: number; }; }; groups?: string[]; roles?: string[]; perms?: { [permId: string]: number; }; canTrackTime?: boolean; [key: string]: any; }Get all tasks including archived ones.
Returns: Promise<TasksAPIResponse>
Create a new task in TimeCamp. This method allows you to create tasks with various parameters including external IDs for integrations.
Parameters:
params: Task creation parametersname: Task name (required)parent_id: Parent task ID as number (optional)external_task_id: External task ID for integrations like Xero (optional)external_parent_id: External parent task ID (optional)budgeted: Budget value in the unit specified bybudget_unit(optional)note: Task description/note (optional)archived: 0 for active, 1 for archived (optional, default: 0)billable: 0 for non-billable, 1 for billable (optional, default: 1)budget_unit: 'hours', 'fee', or '' (optional, default: 'hours')user_ids: Comma-separated user IDs to add to task (optional, e.g., "22,521,2,25")role: Role ID to assign to users if user_ids is provided (optional)keywords: Task keywords, comma-separated (optional, e.g., "IT, R&D")tags: (deprecated) Use keywords instead (optional)
Returns: Promise<TimeCampCreateTaskResponse>
interface TimeCampCreateTaskRequest { name: string; // required parent_id?: number; external_task_id?: string; external_parent_id?: string; budgeted?: number; note?: string; archived?: 0 | 1; billable?: 0 | 1; budget_unit?: 'hours' | 'fee' | ''; user_ids?: string; role?: number; keywords?: string; tags?: string; // deprecated } interface TimeCampCreateTaskResponse { [taskId: string]: { task_id: number; parent_id: number; name: string; external_task_id: string | null; external_parent_id: string | null; level: number; add_date: string; archived: number; color: string; tags: string; budgeted: number; checked_date: string | null; root_group_id: number; billable: number; budget_unit: string; note: string | null; keywords: string; // ... additional fields }; }Example:
// Create a simple task const task = await api.tasks.add({ name: 'Development Task' }); // Create a task with external ID (for integrations) const taskWithExternal = await api.tasks.add({ name: 'Xero Invoice Task', external_task_id: 'xero_g8g89s78ds8', external_parent_id: 'xero_2b5b26tb295bb9' }); // Create a child task with full parameters const childTask = await api.tasks.add({ name: 'Backend Development', parent_id: 123456, // number type budgeted: 1000, budget_unit: 'hours', billable: 1, note: 'Development task for API integration', keywords: 'API, Backend, Development' }); // Access the created task data const taskId = Object.keys(task)[0]; const taskData = task[taskId]; console.log(`Created task: ${taskData.name} (ID: ${taskData.task_id})`);Update an existing task. This method supports all the same parameters as tasks.add() and is particularly useful for assigning users to tasks.
Parameters:
params: Task update parameterstask_id: Task ID to update (required)name: Task name (optional)parent_id: Parent task ID (optional)user_ids: Comma-separated user IDs to assign to task (optional, e.g., "22,521,2,25")role: Role ID to assign to users if user_ids is provided (optional)- All other parameters from
tasks.add()are also supported
Returns: Promise<TimeCampCreateTaskResponse>
interface TimeCampUpdateTaskRequest { task_id: number; // required name?: string; parent_id?: number; user_ids?: string; // Comma-separated user IDs role?: number; // Role ID for assigned users // ... all other optional parameters from TimeCampCreateTaskRequest }Example:
// Update task name await api.tasks.update({ task_id: 12345, name: 'Updated Task Name' }); // Assign users to a task await api.tasks.update({ task_id: 12345, user_ids: '100,200,300', // Assign users 100, 200, and 300 role: 5 // Assign them role ID 5 }); // Update multiple properties including user assignment await api.tasks.update({ task_id: 12345, name: 'Development Sprint 1', user_ids: '100,200', role: 5, billable: 1, budgeted: 40, budget_unit: 'hours' });List all users visible to the authenticated account.
Returns: Promise<Record<string, any>>
Invite a user to your TimeCamp account. The method resolves and returns the invited user's user_id, and when a name parameter is provided it will also update the user's display name after the invite is successful.
Parameters:
params: User invitation parametersemail: Email address of the user to invite (required)name: Display name for the user (optional). If provided, an additional API call will be made to update the user's display name after the invite.group_id: ID of the group to add the user to (optional, defaults to current user's root group)
Returns: Promise<TimeCampUserInviteResponse>
Retry Behavior: This method automatically retries up to 3 times with a 5-second delay when encountering a 429 (rate limit) error.
User ID Resolution & Display Name Update: The method will:
- Send the invitation
- Poll the group user list (up to 10 times with 2-second delays) to find the new user's ID
- If a
nameis provided, make an additional POST request toapi/userwith form-encoded data to update the user's display name - Return the response with the
user_idincluded
Note: There is typically a 2-4 second delay between when the invite succeeds and when the user appears in the group user list, which is why the method includes retry logic. If the user ID cannot be resolved after retries, the method throws an error.
interface TimeCampUserInviteRequest { email: string; name?: string; group_id?: number; } interface TimeCampUserInviteResponse { statuses: { [email: string]: { status: string; // e.g., "Invite", "Already exists", etc. }; }; user_id: string; // Always included after the user is resolved }Example:
// Invite user with automatic group assignment and set display name const result = await timecampApi.users.invite({ email: 'newuser@example.com', name: 'John Doe' }); // Response: { // statuses: { 'newuser@example.com': { status: 'Invite' } }, // user_id: '123456' // } // Invite user to a specific group with display name const result2 = await timecampApi.users.invite({ email: 'newuser@example.com', name: 'John Doe', group_id: 12345 }); // Invite user without setting a display name (skips name update) const result3 = await timecampApi.users.invite({ email: 'another@example.com', group_id: 12345 }); // Check the invite status and user ID console.log(result.statuses['newuser@example.com'].status); // "Invite" console.log(result.user_id); // "123456"Convenience helpers to manage Custom Fields for users, tasks and time entries.
// List all templates const templates = await timecampApi.customFields.getAll() // Create and delete a template const created = await timecampApi.customFields.add({ name: 'Customer Priority', resourceType: 'user', fieldType: 'string', required: false, defaultValue: '' }) await timecampApi.customFields.delete(created.data.id) // Users await timecampApi.users.getAll() await timecampApi.users.byId(123).getAllCustomFields() await timecampApi.users.byId(123).getCustomField(66) await timecampApi.users.byId(123).setCustomField(66, 'In Progress') await timecampApi.users.byId(123).updateCustomField(66, 'Done') await timecampApi.users.byId(123).deleteCustomField(66) // Tasks await timecampApi.tasks.byId(456).getAllCustomFields() await timecampApi.tasks.byId(456).getCustomField(66) await timecampApi.tasks.byId(456).setCustomField(66, '5') await timecampApi.tasks.byId(456).deleteCustomField(66) // Time Entries await timecampApi.timeEntries.byId(789).getAllCustomFields()Manage tags and tag lists for organizing time entries.
// Get all tag lists const tagLists = await timecampApi.tags.getTagLists(); // Get tag lists with options const tagLists = await timecampApi.tags.getTagLists({ archived: 0, // Exclude archived tags: 1, // Include tags in response exclude_empty_tag_lists: 1 // Exclude empty tag lists }); // Get specific tag list with all its tags const tagList = await timecampApi.tags.getTagList(8); console.log(tagList.name); // "My tag list" console.log(tagList.tags); // Object with tag IDs as keys // Create a new tag list const tagListId = await timecampApi.tags.createTagList({ name: 'Project Types' }); // Update a tag list await timecampApi.tags.updateTagList(tagListId, { name: 'Updated Tag List Name', archived: 0 }); // Get only the tags from a tag list (without tag list details) const tags = await timecampApi.tags.getTagListTags(8);// Create a new tag in a tag list const tagId = await timecampApi.tags.createTag({ list: 52, // Tag list ID name: 'Development' }); // Get tag details const tag = await timecampApi.tags.getTag(13); console.log(tag.name); // "Development" console.log(tag.tagListId); // "52" // Update a tag await timecampApi.tags.updateTag(13, { name: 'Backend Development', archived: 0 });Add, retrieve, and remove tags from time entries.
// Create time entry with tags const entry = await timecampApi.timeEntries.create({ date: '2024-01-09', duration: 3600, start_time: '09:00', end_time: '10:00', description: 'Development work', task_id: 12345, tags: [ { tagId: 1 }, { tagId: 4 } ] }); // Get tags for a time entry const entryTags = await timecampApi.timeEntries.getTags(101434259); // Returns: { '101434259': [{ tagListName, tagListId, tagId, name, mandatory }] } // Add tags to an existing time entry await timecampApi.timeEntries.addTags(101434259, [13, 14]); // Returns: ['13'] (IDs of successfully added tags) // Remove tags from time entry await timecampApi.timeEntries.removeTags(101434259, [15]); // Returns: ['15'] (IDs of successfully removed tags)Retrieve computer activity tracking data with application names automatically resolved.
// Get activities for current user (default: 'me') const activities = await timecampApi.computerActivities.get({ date_from: '2024-01-01', date_to: '2024-01-07', }); // Get activities for specific users const activities = await timecampApi.computerActivities.get({ date_from: '2024-01-01', date_to: '2024-01-03', user_ids: '123,456', }); // Each activity includes the resolved application_name for (const activity of activities) { console.log(`${activity.date} ${activity.start_time} - ${activity.application_name}: ${activity.window_title}`); }Important Notes:
- Maximum date range is 7 days. Exceeding this will throw an error.
user_idsdefaults to'me'(current authenticated user)- Multiple users result in separate API calls combined into one result
- Application names are automatically fetched via the
/applicationendpoint and merged into each activity
Computer Activity Types:
interface TimeCampComputerActivitiesRequest { date_from: string; // YYYY-MM-DD format (required) date_to: string; // YYYY-MM-DD format (required) user_ids?: string; // Comma-separated user IDs or 'me' (default: 'me') } interface TimeCampComputerActivity { user_id: string; application_id: string; end_time: string; time_span: number; window_title_id: string; end_date: string; task_id: string; entry_id: string; updated_at: string; update_date: string; application_name?: string; // Resolved from /application endpoint application_info?: string; // Additional info (e.g. domain) }Manage billing rates for tasks, users, groups, and task-user combinations.
// Get all billing rates for a task const taskRates = await timecampApi.billingRates.getTaskRates(12345); // Returns: { '12345': [{ rateId, rateTypeId, value, refType, addDate, refId }] } // Get specific rate type for a task const taskRates = await timecampApi.billingRates.getTaskRates(12345, '1,2'); // Comma-separated rate type IDs // Set or update a task billing rate const rate = await timecampApi.billingRates.setTaskRate(12345, { rateTypeId: 1, value: 150, addDate: '2024-01-09' // Optional });// Get all billing rates for a user const userRates = await timecampApi.billingRates.getUserRates(100); // Set or update a user billing rate const rate = await timecampApi.billingRates.setUserRate(100, { rateTypeId: 1, value: 100 });Task-user rates override both task and user rates for a specific user on a specific task.
// Get task-user specific rates const taskUserRates = await timecampApi.billingRates.getTaskUserRates(12345, 100); // Set task-user specific rate (overrides task and user rates) const rate = await timecampApi.billingRates.setTaskUserRate(12345, 100, { rateTypeId: 1, value: 175 // Higher rate for this user on this specific task });// Get all billing rates for a group const groupRates = await timecampApi.billingRates.getGroupRates(50); // Set or update a group billing rate const rate = await timecampApi.billingRates.setGroupRate(50, { rateTypeId: 1, value: 125 });Warning: This is an undocumented internal API method.
// Get all rate types const rateTypes = await timecampApi.billingRates.getRateTypes();Billing Rate Types:
interface TimeCampSetRateRequest { rateTypeId: number; // Rate type identifier value: number; // Rate value (e.g., hourly rate) addDate?: string; // Optional date in YYYY-MM-DD format } interface TimeCampBillingRate { rateId: number; rateTypeId: number; value: string; refType: string; // 'task', 'user', 'task_user', or 'group' addDate: string; refId: string; } interface TimeCampRateType { rateTypeId: number; name: string; isCost: boolean; }Manage groups (departments, teams) in your TimeCamp organization.
// Get all groups const groups = await timecampApi.groups.getAll(); console.log(groups); // [{ group_id: 530222, name: 'People', parent_id: 0 }] // Create a new group under a parent const newGroup = await timecampApi.groups.create({ name: 'Development Team', parent_id: 530222 }); console.log(newGroup.group_id); // 390673 console.log(newGroup.root_group_id); // 530222 // Create a root group (parent_id defaults to 0 if not provided) const rootGroup = await timecampApi.groups.create({ name: 'New Organization' }); // Update a group's name await timecampApi.groups.update({ group_id: 390673, name: 'Backend Development' }); // Move a group to a different parent await timecampApi.groups.update({ group_id: 390673, parent_id: 123456 // New parent group ID }); // Update both name and parent await timecampApi.groups.update({ group_id: 390673, name: 'Backend Team', parent_id: 123456 }); // Delete a group await timecampApi.groups.delete(390673);Important Notes:
- Maximum group tree depth is 4 levels
- Root groups have
parent_id = 0(there can only be one root group) - When a group is deleted, all its subgroups are moved to the root group
- Root groups cannot be deleted
Group Types:
interface TimeCampCreateGroupRequest { name: string; // Group name (required) parent_id?: number; // Parent group ID (optional, defaults to 0 for root) } interface TimeCampGroup { group_id: number; name: string; parent_id: number; admin_id?: number; root_group_id?: number; } interface TimeCampUpdateGroupRequest { group_id: number; // Group ID to update (required) name?: string; // New name (optional) parent_id?: number; // New parent ID (optional) }Manage clients for invoicing. Requires the invoicing module to be enabled.
// List all clients const clients = await timecampApi.clients.getAll(); console.log('Clients:', clients); // Create a client const client = await timecampApi.clients.create({ organizationName: 'Acme Corp', firstName: 'John', lastName: 'Doe', email: 'john@acme.com', }); console.log('Created client:', client); // Update a client const updatedClient = await timecampApi.clients.update({ clientId: client.clientId, organizationName: 'Acme Corp Updated', address: '123 Main St, New York, NY', }); console.log('Updated client:', updatedClient); // Get tasks assigned to a client const clientTasks = await timecampApi.clients.getTasks(client.clientId); // Get tasks for multiple clients at once const multiClientTasks = await timecampApi.clients.getTasks([1412133, 1412134]); // Remove tasks from a client await timecampApi.clients.removeTasks(client.clientId, [101, 102]); // Delete a client (will fail if client has invoices) await timecampApi.clients.delete(client.clientId);Important Notes:
organizationNameis required when creating a client- Clients cannot be deleted if they have associated invoices
Client Types:
interface TimeCampClient { clientId: number; firstName: string; lastName: string; organizationName: string; address: string; currencyId: number; email: string; rootGroupId: number; addedBy: number; added: string; } interface TimeCampCreateClientRequest { organizationName: string; // required firstName?: string; lastName?: string; address?: string; currencyId?: number; email?: string; } interface TimeCampUpdateClientRequest { clientId: number; // required organizationName?: string; firstName?: string; lastName?: string; address?: string; currencyId?: number; email?: string; }Create and manage invoices from time entries. Requires the invoicing module to be enabled.
// List all invoices const invoices = await timecampApi.invoices.getAll(); console.log('Invoices:', invoices); // Create an invoice from time entries const invoice = await timecampApi.invoices.create({ clientId: 1412133, issueDate: '2026-02-10', addDate: '2026-02-10', noteToClient: 'From 2026-01-26 to 2026-02-28', currencyId: 1, invoiceNumber: '1223', entries: [ { invoiceId: -1, invoiceEntryId: -1, description: '2026-01-26 07:53 ', type: 0, duration: 19980, quantity: 5.55, unitCost: 109, taxId: 198, name: '[ORG] Administration - Ewelina Łagos', subTotal: 604.95, ttEntriesIds: [263222996], }, ], }); console.log('Created invoice:', invoice); // Update an existing invoice const updatedInvoice = await timecampApi.invoices.update({ invoiceId: invoice.invoiceId, noteToClient: 'Updated note', status: 1, }); console.log('Updated invoice:', updatedInvoice); // Delete an invoice await timecampApi.invoices.delete(invoice.invoiceId);Key Concepts:
- When creating a new invoice, set
invoiceId: -1on each entry (this is done automatically by thecreatemethod) - Set
invoiceEntryId: -1for new entries ttEntriesIdslinks invoice entries to time tracking entries by their IDsdurationis in seconds,quantityis in hours (duration / 3600)subTotal = quantity * unitCost
Invoice Types:
interface TimeCampInvoice { invoiceId: number; clientId: number; invoiceNumber: string; description: string; issueDate: string; dueDate: string; editDate: string; status: number; sentDate: string; viewedDate: string; addDate: string; paidDate: string; noteToClient: string; pass: string; poNumber: string; userId: number; currencyId: number; rootGroupId: string; publicHash: string; storedFileId: number | null; quote: boolean; entries: TimeCampInvoiceEntry[]; } interface TimeCampInvoiceEntry { invoiceId: number; invoiceEntryId: number; description: string; type: number; quantity: number; duration: number; unitCost: number; taxId: number; name: string; subTotal?: number; ttEntriesIds?: number[]; } interface TimeCampCreateInvoiceRequest { clientId: number; // required issueDate: string; // required entries: TimeCampCreateInvoiceEntryRequest[]; // required invoiceNumber?: string; description?: string; addDate?: string; dueDate?: string; noteToClient?: string; currencyId?: number; status?: number; userId?: number; rootGroupId?: number; quote?: boolean; // ... additional optional fields }Fetch every task visible to the authenticated account, including archived tasks.
Returns: Promise<TasksAPIResponse>
Start a new timer.
Parameters:
data(optional): Timer start configurationtask_id: ID of the task to track (optional)note: Description note for the timer (optional)started_at: Custom start time in ISO 8601 format (optional, defaults to current time)
Returns: Promise<TimerEntry>
interface TimerStartRequest { task_id?: number; note?: string; started_at?: string; // ISO 8601 format }Stop the currently running timer.
Parameters:
data(optional): Timer stop configurationstopped_at: Custom stop time in ISO 8601 format (optional, defaults to current time)
Returns: Promise<TimerEntry>
interface TimerStopRequest { stopped_at?: string; // ISO 8601 format }Get the current timer status.
Returns: Promise<TimerStatus>
interface TimerStatus { timer_id?: number; task_id?: number; start_time?: string; running: boolean; duration?: number; } interface TimerEntry { id: number; task_id: number; user_id: number; name: string; note: string; start_time: string; end_time: string | null; duration: number; locked: boolean; billable: boolean; invoiced: boolean; approved: boolean; }Get time entries with optional filtering.
Parameters:
params(optional): Filtering parametersuser_ids: Filter by user ID, to get only user entries use "me" (optional)task_id: Filter by task ID (optional)date_from: Start date in YYYY-MM-DD format (optional)date_to: End date in YYYY-MM-DD format (optional)format: Response format, 'json' is automatically set (optional)
Returns: Promise<TimeCampTimeEntry[]>
interface TimeCampTimeEntriesRequest { user_id?: string; task_id?: string; date_from?: string; date_to?: string; format?: string; } interface TimeCampTimeEntry { id: number; duration: number; // Duration in seconds user_id: string; user_name: string; task_id: string; task_note?: string; last_modify: string; date: string; start_time: string; end_time: string; locked: string; name: string; addons_external_id: string; billable: number; invoiceId: string; color: string; description: string; tags: TimeCampTag[]; hasEntryLocationHistory: boolean; } interface TimeCampTag { tagListName: string; tagListId: string; tagId: string; name: string; mandatory: string; }Create a new time entry.
Parameters:
entry: Time entry datadate: Date in YYYY-MM-DD format (required)duration: Duration in seconds (required)start_time: Start time in HH:MM:SS format (required)end_time: End time in HH:MM:SS format (required)task_id: ID of the task (optional)description: Description of the work done (optional)
Returns: Promise<TimeCampCreateTimeEntryResponse>
interface TimeCampCreateTimeEntryRequest { date: string; duration: number; // in seconds task_id?: number; description?: string; start_time: string; end_time: string; user_id?: number; billable?: boolean; tags?: Array<{ tagId: number }>; // Optional tags to add on creation } interface TimeCampCreateTimeEntryResponse { success: boolean; id?: string; message: string; }Update an existing time entry.
Parameters:
id: ID of the time entry to update (required)data: Partial time entry data to update (supports partial updates)
Returns: Promise<TimeCampCreateTimeEntryResponse>
Delete a time entry.
Parameters:
id: ID of the time entry to delete (required)
Returns: Promise<{success: boolean, message: string}>
Based on the TimeCamp API documentation.
- Added computer activities tracking (
computerActivities.get()) with automatic application name resolution
- ✅ Added clients management (
clients.getAll(),clients.create(),clients.update(),clients.delete(),clients.getTasks(),clients.removeTasks()) - ✅ Added invoices management (
invoices.getAll(),invoices.create(),invoices.update(),invoices.delete()) - ✅ Invoice creation supports linking time entries via
ttEntriesIds
- ✅ Added
tasks.update()method for updating tasks and assigning users - ✅ Added complete tags and tag lists management (
tags.*methods) - ✅ Added tag support for time entries (create with tags, add/remove tags)
- ✅ Added billing rates management for tasks, users, groups, and task-user combinations
- ✅ Added groups management (
groups.getAll(),groups.create(),groups.update(),groups.delete())
- Rename description to note in TimeCampTimeEntry
- Many more endpoints to be added
MIT