A TypeScript client library for the TimeCamp API.
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);
// 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'
});
const updatedEntry = await timecampApi.timeEntries.update(entryId, {
description: 'Updated description',
duration: 7200 // 2 hours
});
const deleteResult = await timecampApi.timeEntries.delete(entryId);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.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> |
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}> |
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})`);List all users visible to the authenticated account.
Returns: Promise<Record<string, any>>
Invite a user to your TimeCamp account. When a name parameter is provided, the method will automatically 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.
Display Name Update: When a name is provided, the method will:
- Send the invitation
- Poll the user list (up to 10 times with 2-second delays) to find the new user's ID
- Make an additional POST request to
api/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 users list, which is why the method includes retry logic.
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; // Included when name is provided and update succeeds
}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()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?: string;
description?: string;
start_time: string;
end_time: string;
}
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.
- Rename description to note in TimeCampTimeEntry
- Many more endpoints to be added
MIT