Time Tracking
The TimeEntry entity represents time entries in InLoox. Via the OData API you can create, update, delete time entries as well as link them to documents and create them from Microsoft Graph calendar events.
All endpoints use the base path /odata/TimeEntry. For the dynamic list view including custom fields, /odata/DynamicTimeEntry is available.
Time entries are always associated with a project. Use ProjectId when filtering to retrieve only time entries for a specific project.
Data Model
TimeEntry
| Property | Type | Description |
|---|---|---|
TimeEntryId | Guid (UUID) | Unique identifier of the time entry (primary key). |
DisplayName | string? | Display name of the time entry. |
StartDateTime | DateTimeOffset? | Start date and time. |
EndDateTime | DateTimeOffset? | End date and time. |
ProjectId | Guid? | Associated project. |
DescriptionHTML | string? | Description as HTML. |
DescriptionText | string? | Description as plain text. |
PhaseId | Guid? | Linked planning phase. |
IsBillable | boolean | Indicates whether the time entry is billable. |
PerformedByContactId | Guid | Contact ID of the person who performed the work. |
ColorFlag | int32? | Color flag (as integer value). |
GroupId | Guid | Group ID of the time entry. |
TaskItemId | Guid? | Linked task. |
IsBilled | boolean | Indicates whether the time entry has already been billed. |
DurationMinutes | int32 | Duration in minutes. |
Endpoints
CRUD Operations
Retrieve all time entries across all projects
/odata/TimeEntrySupports OData query parameters.
Retrieve a single time entry
/odata/TimeEntry({key})| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Create a new time entry
/odata/TimeEntry| Parameter | Type | Required | Description |
|---|---|---|---|
Body | Delta<ApiTimeEntry> | ✅ | JSON object with the time entry properties. |
If PerformedByContactId is not specified, the time entry is recorded for the authenticated user.
Partially update a time entry
/odata/TimeEntry({key})| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Body | Delta<ApiTimeEntry> | ✅ | JSON object with the fields to update. |
Delete a time entry
/odata/TimeEntry({key})| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Deleting a time entry is permanent. If the entry has already been billed, deletion is not possible.
Special Functions
Retrieve the calendar view of time entries for a project
/odata/TimeEntry/GetCalendarTimeEntriesForProject(projectId={projectId},take={take})| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | Guid | ✅ | The ProjectId. |
take | int32 | ✅ | Maximum number of entries to return. |
Create a time entry from a Microsoft Graph calendar event
/odata/TimeEntry/CreateTimeEntryFromGraphEvent| Parameter | Type | Required | Description |
|---|---|---|---|
Body | object | ✅ | JSON object with the following fields: projectId (Guid, required) — The project in which the time entry should be created. graphEventId (string, required) — The ID of the Microsoft Graph calendar event. tagEvent (bool?, optional) — If true, the "InLoox" category is added to the calendar event. taskItemId (Guid?, optional) — Optional task to link with the time entry. |
With CreateTimeEntryFromGraphEvent you can convert Outlook calendar appointments directly into time entries. This requires an active Microsoft Graph integration.
Copy a time entry
/odata/TimeEntry({key})/Copy| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId of the entry to copy. |
Body | object | ✅ | JSON object with the following fields: projectId (Guid?, optional) — Target project for the copied time entry. If not specified, the copy remains in the same project as the original. |
Copy a time entry with new dates
/odata/TimeEntry({key})/CopyWithNewDates| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId of the entry to copy. |
Body | object | ✅ | JSON object with the following fields: projectId (Guid?, optional) — Target project for the copy. If not specified, the copy remains in the same project. startDate (DateTimeOffset?, required) — New start date/time for the copied entry (stored as UTC). endDate (DateTimeOffset?, required) — New end date/time for the copied entry (stored as UTC). |
Documents
Link a document to the time entry
/odata/TimeEntry({key})/AddDocumentToTimeEntry| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Body | object | ✅ | JSON object with the following fields: documentIds (Guid[], required) — Array of document IDs to link with the time entry. |
Remove a document from the time entry
/odata/TimeEntry({key})/RemoveDocumentFromTimeEntry| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Body | object | ✅ | JSON object with the following fields: documentId (Guid, required) — The ID of the document to remove from the time entry. |
Relations
Add a relation to the time entry
/odata/TimeEntry({key})/AddRelation| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Body | object | ✅ | JSON object with the following fields: itemId (Guid, required) — The ID of the item to link. |
Remove a relation from the time entry
/odata/TimeEntry({key})/RemoveRelation| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Body | object | ✅ | JSON object with the following fields: itemId (Guid, required) — The ID of the item whose relation should be removed. |
Comments
Retrieve all time entry comments
/odata/TimeEntryCommentRetrieve a single comment
/odata/TimeEntryComment({key})| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryCommentId. |
Add a comment to the time entry
/odata/TimeEntry({key})/AddNote| Parameter | Type | Required | Description |
|---|---|---|---|
key | Guid | ✅ | The TimeEntryId. |
Body | object | ✅ | JSON object with the following fields: htmlText (string, required) — HTML-formatted comment text. notificationContactIds (Guid[], required) — Contact IDs to be notified (can be an empty array []). |
Delete a comment from the time entry
/odata/TimeEntry/DeleteNote(noteRelationId={noteRelationId})| Parameter | Type | Required | Description |
|---|---|---|---|
noteRelationId | Guid | ✅ | The ID of the comment relation. |
Deleting a comment cannot be undone.
DynamicTimeEntry
The /odata/DynamicTimeEntry endpoint provides a read-only, flattened view that combines data from the time entry, project, planning, task, and permissions. All properties are prefixed (e.g., TimeEntry_DisplayName, Project_Name, TaskItem_Name). Custom fields follow the pattern CF_<FieldName>.
Retrieve the flattened time entry view with project, planning, and task data
/odata/DynamicTimeEntryDynamicTimeEntry supports GET requests only. To create or edit, use the regular /odata/TimeEntry endpoints.
OData Query Examples
Retrieve time entries for a project
GET /odata/TimeEntry?$filter=ProjectId eq 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
Billable but not yet billed entries
GET /odata/TimeEntry?$filter=IsBillable eq true and IsBilled eq false
Time entries for a specific date range
GET /odata/TimeEntry?$filter=StartDateTime ge 2025-01-01T00:00:00Z and EndDateTime le 2025-01-31T23:59:59Z
Create a time entry
POST /odata/TimeEntry
Content-Type: application/json
{
"DisplayName": "API module development",
"ProjectId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"StartDateTime": "2025-01-15T09:00:00Z",
"EndDateTime": "2025-01-15T12:30:00Z",
"IsBillable": true,
"PerformedByContactId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
The DurationMinutes field is calculated server-side from StartDateTime and EndDateTime. You can also set it manually if you do not specify a start/end time.
Time entries by contact
GET /odata/TimeEntry?$filter=PerformedByContactId eq 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'&$top=50&$skip=0&$orderby=StartDateTime desc
C# example
using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// Query time entries for April 2025
var response = await client.GetAsync(
"https://{tenant}.inloox.app/odata/TimeEntry?" +
$"$filter=ProjectId eq {projectId}" +
" and StartDateTime ge 2025-04-01T00:00:00Z" +
" and EndDateTime le 2025-04-30T23:59:59Z" +
"&$select=TimeEntryId,StartDateTime,EndDateTime,DurationMinutes,IsBillable" +
"&$orderby=StartDateTime asc");
var json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json);
// Create a time entry
var payload = new StringContent(
"""
{
"ProjectId": "{project-guid}",
"StartDateTime": "2025-04-15T09:00:00Z",
"EndDateTime": "2025-04-15T12:00:00Z",
"DescriptionHTML": "API integration work",
"IsBillable": true
}
""",
System.Text.Encoding.UTF8,
"application/json");
var createResponse = await client.PostAsync(
"https://{tenant}.inloox.app/odata/TimeEntry", payload);
Always filter time entries by date range (StartDateTime and EndDateTime) for reports to avoid loading the entire history. Combine this with $select to reduce the response size.