GET List task time entries
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries
Returns a list of time-entries based on the provided workspace ID, task ID and filter params. With include_subtasks=true, a task that sums subtask totals also lists the entries of the subtasks whose time is summed into its totals, each under its own task_id; a task that does not sum subtask totals lists its own entries only, whatever the flag.
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries \
-H "Content-Type: application/json" \
-u <email>:<password>
req, err := http.NewRequest(http.MethodGet,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries")
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.SetBasicAuth("<email>", "<password>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries')
req = Net::HTTP::Get.new(uri)
req['Content-Type'] = "application/json"
req.basic_auth '<email>', '<password>'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64.encode(<email>:<password>)}`
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.get('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries', headers={'content-type': 'application/json', 'Authorization' : 'Basic %s' % b64encode(b"<email>:<password>").decode("ascii")})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new().basic_auth("<email>", "<password>");
let json = client.request(Method::GET, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries".to_string())
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
| task_id | integer | true | task ID |
Query
| name | type | required | description |
|---|
| page | integer | false | page number |
| per_page | integer | false | results per page |
| order_by | []string | false | order by |
| type | string | false | time entry type |
| archived | boolean | false | filter in/out archived TEs |
| include_subtasks | boolean | false | also list entries of the subtasks summed into this task's totals (default false) |
Response
200
| Name | Type | Description |
|---|
| data | Array of object | - |
| page | integer | - |
| per_page | integer | - |
data
| Name | Type | Description |
|---|
| archived_at | string | - |
| billable | boolean | - |
| billable_source | string | Whether billable was set on the entry (manual) or resolved from its task, else its project (task_default). Valid values: manual, task_default. |
| calendar_event_id | integer | - |
| calendar_id | integer | CalendarID is the calendar service's calendar PK. It stays set after a calendar deselect clears calendar_event_id. |
| created_at | string | - |
| deleted_at | string | - |
| description | string | - |
| duration | integer | Tracked duration in seconds; omitted while the timer is running and for an entry that is only planned. |
| id | integer | - |
| pattern_generated | boolean | PatternGenerated is the response-facing derivative of GeneratedByTaskID: true when the entry was materialized by a series pattern. |
| pattern_managed | boolean | PatternManaged reports that the pattern engine owns this entry. Server-owned; present in responses, never bindable from a payload. |
| planned_at | string | - |
| planned_duration | integer | Planned duration in seconds. |
| planned_start | string | Planned start, for time planned on the calendar. |
| project_id | integer | - |
| start | string | Start of the tracked time; omitted for an entry that is only planned. |
| tag_ids | Array of integer | TagIDs is the entry's manual tag override. NULL (nil) means no override: the effective tags fall through to the linked task's tags. A non-nil value — including an empty array — is entry-authoritative. |
| tags | Array of object | Tags is the hydrated effective tag set: the override when TagIDs is set, else the linked task's active tags. Read-only. |
| task_id | integer | null |
| time_block_id | integer | - |
| timezone | string | Timezone is the entry creator's IANA timezone snapshotted at creation; NULL when unresolved. |
| toggl_user_id | integer | - |
| tracked_at | string | - |
| type | string | The type of time entry: either an activity or a break. Valid values: activity, break. |
| updated_at | string | - |
| workspace_id | integer | - |
| Name | Type | Description |
|---|
| color | string | - |
| id | integer | - |
| name | string | - |
400
Invalid request
403
Insufficient permissions
500
Internal Server Error
POST Create a new time entry
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries
Creates a new time entry in the specified workspace.
An optional user_id field can be provided to create a time entry on behalf of another user.
When user_id differs from the authenticated user, the caller must have manage_time_entries permission.
An optional project_id overrides the task's project for this entry. When omitted, the entry inherits the task's project.
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl -X POST https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries \
-H "Content-Type: application/json" \
-d '\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","repeat_time_entry":"boolean","start":"string","tag_ids":[\{\}],"tracked_at":"string","type":"string","user_id":"integer"\}' \
-u <email>:<password>
bytes, err := json.Marshal('\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","repeat_time_entry":"boolean","start":"string","tag_ids":[\{\}],"tracked_at":"string","type":"string","user_id":"integer"\}')
if err != nil {
print(err)
}
req, err := http.NewRequest(http.MethodPost,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries", bytes.NewBuffer(bytes))
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.SetBasicAuth("<email>", "<password>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries')
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = "application/json"
req.body = \{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","repeat_time_entry":"boolean","start":"string","tag_ids":[\{\}],"tracked_at":"string","type":"string","user_id":"integer"\}.to_json
req.basic_auth '<email>', '<password>'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries", {
method: "POST",
body: \{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","repeat_time_entry":"boolean","start":"string","tag_ids":[\{\}],"tracked_at":"string","type":"string","user_id":"integer"\},
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64.encode(<email>:<password>)}`
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.post('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries', json=\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","repeat_time_entry":"boolean","start":"string","tag_ids":[\{\}],"tracked_at":"string","type":"string","user_id":"integer"\}, headers={'content-type': 'application/json', 'Authorization' : 'Basic %s' % b64encode(b"<email>:<password>").decode("ascii")})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new().basic_auth("<email>", "<password>");
let json = client.request(Method::POST, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/tasks/{task_id}/time-entries".to_string())
.json(&serde_json::json!(\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","repeat_time_entry":"boolean","start":"string","tag_ids":[\{\}],"tracked_at":"string","type":"string","user_id":"integer"\}))
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
| task_id | integer | true | task ID |
Query
| name | type | required | description |
|---|
| from_recurring_task_id | integer | false | DECLARED FOR PR 4/12, NOT ENABLED: scope the planned-time operation to a recurring series. Any supplied value is rejected until the implementing PR lands. |
| return_occurrence | boolean | false | DECLARED FOR PR 12, NOT ENABLED: answer 200 with the exact occurrence instead of the default 204. Any active use is rejected until the implementing PR lands. |
Body
| Name | Type | Description |
|---|
| billable | boolean | - |
| calendar_event_id | integer | - |
| description | string | - |
| duration | integer | - |
| planned_at | string | - |
| planned_duration | integer | - |
| planned_start | string | - |
| project_id | integer | - |
| repeat_time_entry | boolean | RepeatTimeEntry is the declared composite planned-time repeat input (initial selection, PR 11). Any supplied value — true, false or JSON null — is rejected until then; the contract is declared in docs/recurring-pr2a-additive-schema.md. json.RawMessage keeps absent distinguishable from explicit null on decode and is omitted on marshal when never set. |
| start | string | - |
| tag_ids | Array of integer | TagIDs sets a manual tag override at creation (entry-authoritative, including an explicit empty array). Absent/null = no override: the entry inherits the linked task's tags. |
| tracked_at | string | - |
| type | string | The type of time entry. This could either be an activity, or a break. Valid values: activity, break. |
| user_id | integer | - |
Response
201
Time entry created successfully
400
Invalid request
403
Insufficient permissions
422
Required field is missing (error=required_fields_missing) or is being cleared on a grandfathered entry (error=required_fields_deletion)
500
Internal Server Error
GET List task time entries
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries
Returns a list of time-entries based on the provided workspace ID and filter params.
Only the caller's own time entries are returned, whatever their role. To read other members' time, use the reports query endpoint (POST /reports/workspaces/{workspace_id}/query). For guests, entries on tasks or projects they cannot access, and entries with neither, are also left out.
Returns every entry that overlaps the date_from–date_to range: a tracked entry by its start and duration, a planned-only entry by its planned_start and planned_duration. A running timer has no duration yet, so it is returned only if it started within the range.
Entries without a task are left out unless include_taskless=true.
Results are paginated; the response has no total count, so keep requesting pages until one returns no entries. A page can hold fewer than per_page entries while more pages follow. For guests, even an empty page can be followed by more; use the stream endpoint to read a whole range.
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries \
-H "Content-Type: application/json" \
-u <email>:<password>
req, err := http.NewRequest(http.MethodGet,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries")
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.SetBasicAuth("<email>", "<password>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries')
req = Net::HTTP::Get.new(uri)
req['Content-Type'] = "application/json"
req.basic_auth '<email>', '<password>'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64.encode(<email>:<password>)}`
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.get('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries', headers={'content-type': 'application/json', 'Authorization' : 'Basic %s' % b64encode(b"<email>:<password>").decode("ascii")})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new().basic_auth("<email>", "<password>");
let json = client.request(Method::GET, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries".to_string())
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
Query
| name | type | required | description |
|---|
| date_from | string | true | range start, as an RFC 3339 timestamp, e.g. 2026-09-01T00:00:00Z (percent-encode a + offset) |
| date_to | string | true | range end, as an RFC 3339 timestamp, e.g. 2026-09-30T23:59:59Z |
| task_id | integer | false | only entries of this task |
| status_id | integer | false | only entries whose task has this status |
| project_id | integer | false | only entries of this project (the entry's own project, else its task's) |
| type | string | false | time entry type |
| page | integer | false | page number, starting at 1 (default 1) |
| per_page | integer | false | results per page (default 20, max 100) |
| order_by | []string | false | sort columns, repeated or comma-separated; prefix - for descending. Ties are broken by id |
| archived | boolean | false | true for archived entries only, false for unarchived only; omit for both |
| include_taskless | boolean | false | include entries without a task (default false) |
| project_completed | boolean | false | true for entries on a completed project only, false for non-completed only; omit for both. An entry counts as completed when its own project or its task's project is completed |
| project_draft | boolean | false | true for entries on a draft project only, false for non-draft only; omit for both. Checks the same two projects as project_completed |
| expand_calendar_event | boolean | false | hydrate calendar event details inline |
Response
200
| Name | Type | Description |
|---|
| data | Array of object | - |
| page | integer | - |
| per_page | integer | - |
data
| Name | Type | Description |
|---|
| archived_at | string | - |
| billable | boolean | - |
| billable_source | string | Whether billable was set on the entry (manual) or resolved from its task, else its project (task_default). Valid values: manual, task_default. |
| calendar_event | object | - |
| calendar_event_id | integer | - |
| calendar_id | integer | CalendarID is the calendar service's calendar PK. It stays set after a calendar deselect clears calendar_event_id. |
| created_at | string | - |
| deleted_at | string | - |
| description | string | - |
| duration | integer | Tracked duration in seconds; omitted while the timer is running and for an entry that is only planned. |
| id | integer | - |
| pattern_generated | boolean | PatternGenerated is the response-facing derivative of GeneratedByTaskID: true when the entry was materialized by a series pattern. |
| pattern_managed | boolean | PatternManaged reports that the pattern engine owns this entry. Server-owned; present in responses, never bindable from a payload. |
| planned_at | string | - |
| planned_duration | integer | Planned duration in seconds. |
| planned_start | string | Planned start, for time planned on the calendar. |
| project | object | - |
| project_id | integer | - |
| start | string | Start of the tracked time; omitted for an entry that is only planned. |
| tag_ids | Array of integer | TagIDs is the entry's manual tag override. NULL (nil) means no override: the effective tags fall through to the linked task's tags. A non-nil value — including an empty array — is entry-authoritative. |
| tags | Array of object | Tags is the hydrated effective tag set: the override when TagIDs is set, else the linked task's active tags. Read-only. |
| task | object | - |
| task_id | integer | null |
| time_block_id | integer | - |
| timezone | string | Timezone is the entry creator's IANA timezone snapshotted at creation; NULL when unresolved. |
| toggl_user_id | integer | - |
| tracked_at | string | - |
| type | string | The type of time entry: either an activity or a break. Valid values: activity, break. |
| updated_at | string | - |
| workspace_id | integer | - |
calendar_event
| Name | Type | Description |
|---|
| all_day | boolean | - |
| background_color | string | null |
| end_time | string | - |
| foreground_color | string | null |
| html_link | string | - |
| id | integer | - |
| meeting_link | string | null |
| provider | string | - |
| start_time | string | - |
| title | string | - |
project
| Name | Type | Description |
|---|
| archived_at | string | - |
| billable | boolean | - |
| client | object | - |
| color | string | - |
| custom_field_values | Array of object | CustomFieldValues are the parent project's CF values, hydrated by callers that surface them (currently the task list, via task.service.hydrateTaskProjectCustomFieldValues which routes through customfield.Service.GetFieldsByIDs and respects the PermissionViewWorkspaceProjectCustomFields gate). Producers that don't hydrate (e.g. timeentry) leave the slice empty; omitempty hides it on those responses. |
| draft | boolean | - |
| id | integer | - |
| is_template | boolean | - |
| name | string | - |
| permissions | Array of string | - |
| private | boolean | - |
| rate | object | - |
| recurring_project_id | integer | RecurringProjectID links an instance to its recurring template, so clients can lane an entry under a collapsed series. |
client
| Name | Type | Description |
|---|
| id | integer | - |
| name | string | - |
custom_field_values
| Name | Type | Description |
|---|
| custom_field_id | integer | - |
| custom_field_name | string | - |
| field_type | string | - |
| selected_options | Array of object | - |
| value | object | - |
selected_options
| Name | Type | Description |
|---|
| is_deleted | boolean | - |
| option_id | integer | - |
| option_name | string | - |
rate
| Name | Type | Description |
|---|
| billable | boolean | - |
| currency | string | - |
| end_at | string | - |
| has_more_rates | boolean | - |
| hourly_rate | number | - |
| project_color | string | - |
| project_created_at | string | - |
| project_id | integer | - |
| project_name | string | - |
| project_rate_id | integer | - |
| start_at | string | - |
| workspace_rate_id | integer | - |
| Name | Type | Description |
|---|
| color | string | - |
| id | integer | - |
| name | string | - |
task
| Name | Type | Description |
|---|
| allocation_unit | string | Valid values: percent, flat. |
| archived_at | string | - |
| assignee_user_ids | Array of integer | - |
| assignees | Array of object | A unified column for all task entity assignments |
| auto_log_time | boolean | - |
| billable | boolean | null |
| client | object | - |
| color | string | - |
| created_at | string | - |
| custom_field_values | Array of object | - |
| deleted_at | string | - |
| description | string | - |
| end_date | string | - |
| estimate_type | string | Valid values: daily, total. |
| estimated_mins | integer | - |
| id | integer | - |
| is_recurring_series | boolean | IsRecurringSeries marks a canonical series root (S). Server-owned and read-only: no current API path can set it, and the response only exposes the marker while series behavior stays disabled. |
| is_template | boolean | - |
| metadata | object | - |
| name | string | - |
| notes | string | - |
| parent_task_id | integer | - |
| parent_task_name | string | - |
| pinned | boolean | - |
| position | integer | - |
| priority | string | Valid values: none, low, medium, high. |
| priority_at | string | - |
| private | boolean | - |
| project | object | - |
| project_id | integer | - |
| recurrence_date | string | - |
| recurring_rule | string | The rule of the task's series (RRULE with DTSTART); omitted for a normal task and once the series has stopped. |
| recurring_series_id | integer | The series this task is an occurrence of; omitted for a normal task. Only the server sets it. |
| recurring_task_id | integer | - |
| rollup_enabled | boolean | - |
| rrule | string | - |
| source | string | - |
| source_template_task_id | integer | - |
| start_date | string | - |
| status | object | - |
| status_id | integer | - |
| status_updated_at | string | - |
| tag_ids | Array of integer | - |
| tags | Array of object | - |
| toggl_user_id | integer | - |
| updated_at | string | - |
| workspace_id | integer | - |
assignees
| Name | Type | Description |
|---|
| id | integer | - |
| type | string | Valid values: user, team. |
client
| Name | Type | Description |
|---|
| id | integer | - |
| name | string | - |
custom_field_values
| Name | Type | Description |
|---|
| custom_field_id | integer | - |
| custom_field_name | string | - |
| field_type | string | - |
| selected_options | Array of object | - |
| value | object | - |
selected_options
| Name | Type | Description |
|---|
| is_deleted | boolean | - |
| option_id | integer | - |
| option_name | string | - |
| Name | Type | Description |
|---|
| all_day | boolean | - |
| calendar_event_id | integer | - |
| calendar_id | integer | - |
| extension_source | string | Browser-extension provenance: the source/config that produced the task and the page URL it was tracked from. Written by the extension's start-from-description flow; ExtensionURL is queryable via FindByURL. |
| extension_url | string | - |
| external_id | string | - |
| ical_uid | string | - |
| last_asserted_track_project_id | integer | LastAssertedTrackProjectID is the Track planned_task.project_id that the Focus→Track mirror last successfully wrote for this task. Track→Focus uses it to distinguish a stale echo (Track still holds this value after a Focus-side project move) from an intentional Track-side project move. Internal mirror state, written by toggl_api focus_sync_worker; not user-facing. |
| meeting_link | string | - |
| project_assignment | object | - |
| updated_at | string | - |
project_assignment
| Name | Type | Description |
|---|
| accuracy | number | - |
| confirmed_at | string | - |
| match_tier | string | Valid values: exact_name, similar_name. |
| matched_name | string | - |
| normalized_name | string | - |
| origin | string | Valid values: manual, auto_suggestion, confirmed_suggestion, integration, legacy, explicit_mapping. |
| suggested_at | string | - |
project
| Name | Type | Description |
|---|
| archived_at | string | - |
| billable | boolean | - |
| client | object | - |
| color | string | - |
| custom_field_values | Array of object | CustomFieldValues are the parent project's CF values, hydrated by callers that surface them (currently the task list, via task.service.hydrateTaskProjectCustomFieldValues which routes through customfield.Service.GetFieldsByIDs and respects the PermissionViewWorkspaceProjectCustomFields gate). Producers that don't hydrate (e.g. timeentry) leave the slice empty; omitempty hides it on those responses. |
| draft | boolean | - |
| id | integer | - |
| is_template | boolean | - |
| name | string | - |
| permissions | Array of string | - |
| private | boolean | - |
| rate | object | - |
| recurring_project_id | integer | RecurringProjectID links an instance to its recurring template, so clients can lane an entry under a collapsed series. |
client
| Name | Type | Description |
|---|
| id | integer | - |
| name | string | - |
custom_field_values
| Name | Type | Description |
|---|
| custom_field_id | integer | - |
| custom_field_name | string | - |
| field_type | string | - |
| selected_options | Array of object | - |
| value | object | - |
rate
| Name | Type | Description |
|---|
| billable | boolean | - |
| currency | string | - |
| end_at | string | - |
| has_more_rates | boolean | - |
| hourly_rate | number | - |
| project_color | string | - |
| project_created_at | string | - |
| project_id | integer | - |
| project_name | string | - |
| project_rate_id | integer | - |
| start_at | string | - |
| workspace_rate_id | integer | - |
status
| Name | Type | Description |
|---|
| emoji | string | - |
| id | integer | - |
| name | string | - |
| type | string | Valid values: todo, done, in_progress, blocked. |
| Name | Type | Description |
|---|
| color | string | - |
| id | integer | - |
| name | string | - |
400
Invalid request
403
Insufficient permissions
500
Internal Server Error
POST Create new time entries in bulk
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk
Creates new time entries in bulk for the specified workspace.
An optional user_id field can be provided per entry to create time entries on behalf of another user.
When user_id differs from the authenticated user, the caller must have manage_time_entries permission.
task_id is optional; omitting it creates a taskless entry, optionally scoped to project_id.
When both task_id and project_id are provided, project_id overrides the task's project for the entry.
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl -X POST https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk \
-H "Content-Type: application/json" \
-d '[\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","start":"string","tag_ids":[\{\}],"task_id":"integer","tracked_at":"string","type":"string","user_id":"integer"\}]' \
-u <email>:<password>
bytes, err := json.Marshal('[\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","start":"string","tag_ids":[\{\}],"task_id":"integer","tracked_at":"string","type":"string","user_id":"integer"\}]')
if err != nil {
print(err)
}
req, err := http.NewRequest(http.MethodPost,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk", bytes.NewBuffer(bytes))
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.SetBasicAuth("<email>", "<password>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk')
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = "application/json"
req.body = [\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","start":"string","tag_ids":[\{\}],"task_id":"integer","tracked_at":"string","type":"string","user_id":"integer"\}].to_json
req.basic_auth '<email>', '<password>'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk", {
method: "POST",
body: [\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","start":"string","tag_ids":[\{\}],"task_id":"integer","tracked_at":"string","type":"string","user_id":"integer"\}],
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64.encode(<email>:<password>)}`
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.post('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk', json=[\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","start":"string","tag_ids":[\{\}],"task_id":"integer","tracked_at":"string","type":"string","user_id":"integer"\}], headers={'content-type': 'application/json', 'Authorization' : 'Basic %s' % b64encode(b"<email>:<password>").decode("ascii")})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new().basic_auth("<email>", "<password>");
let json = client.request(Method::POST, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk".to_string())
.json(&serde_json::json!([\{"billable":"boolean","calendar_event_id":"integer","description":"string","duration":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","start":"string","tag_ids":[\{\}],"task_id":"integer","tracked_at":"string","type":"string","user_id":"integer"\}]))
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
Body
| Name | Type | Description |
|---|
| items | Array of object | - |
items
| Name | Type | Description |
|---|
| billable | boolean | - |
| calendar_event_id | integer | - |
| description | string | - |
| duration | integer | - |
| planned_at | string | - |
| planned_duration | integer | - |
| planned_start | string | - |
| project_id | integer | - |
| start | string | - |
| tag_ids | Array of integer | TagIDs sets a manual tag override at creation (entry-authoritative, including an explicit empty array). Absent/null = no override: the entry inherits the linked task's tags. |
| task_id | integer | - |
| tracked_at | string | - |
| type | string | The type of time entry. This could either be an activity, or a break. Valid values: activity, break. |
| user_id | integer | - |
Response
201
Time entries created successfully
400
Invalid request
403
Insufficient permissions
422
One or more time entries violate required-field constraints (error=bulk_required_fields_missing)
500
Internal Server Error
DELETE Delete time entries in bulk
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk
Deletes time entries by the provided IDs.
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl -X DELETE https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk \
-H "Content-Type: application/json" \
-u <email>:<password>
req, err := http.NewRequest(http.MethodPut,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk")
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.SetBasicAuth("<email>", "<password>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk')
req = Net::HTTP::Delete.new(uri)
req['Content-Type'] = "application/json"
req.basic_auth '<email>', '<password>'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64.encode(<email>:<password>)}`
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.delete('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk', headers={'content-type': 'application/json', 'Authorization' : 'Basic %s' % b64encode(b"<email>:<password>").decode("ascii")})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new().basic_auth("<email>", "<password>");
let json = client.request(Method::DELETE, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk".to_string())
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
Query
| name | type | required | description |
|---|
| ids | []integer | true | TimeEntry IDs |
Response
204
No Content
400
Invalid request
403
Insufficient permissions
404
Time entry does not exist
500
Internal Server Error
PATCH Partial bulk update time entry
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk
Partial updates an existing time entry
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl -X PATCH https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk \
-H "Content-Type: application/json" \
-d '[\{"billable":"boolean","description":"string","duration":"integer","id":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","reset_billable_to_default":"boolean","start":"string","tag_ids":[\{\}],"task_id":"integer","time_block_id":"integer","tracked_at":"string","type":"string"\}]'
bytes, err := json.Marshal('[\{"billable":"boolean","description":"string","duration":"integer","id":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","reset_billable_to_default":"boolean","start":"string","tag_ids":[\{\}],"task_id":"integer","time_block_id":"integer","tracked_at":"string","type":"string"\}]')
if err != nil {
print(err)
}
req, err := http.NewRequest(http.MethodGet,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk", bytes.NewBuffer(bytes))
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk')
req = Net::HTTP::Patch.new(uri)
req['Content-Type'] = "application/json"
req.body = [\{"billable":"boolean","description":"string","duration":"integer","id":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","reset_billable_to_default":"boolean","start":"string","tag_ids":[\{\}],"task_id":"integer","time_block_id":"integer","tracked_at":"string","type":"string"\}].to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk", {
method: "PATCH",
body: [\{"billable":"boolean","description":"string","duration":"integer","id":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","reset_billable_to_default":"boolean","start":"string","tag_ids":[\{\}],"task_id":"integer","time_block_id":"integer","tracked_at":"string","type":"string"\}],
headers: {
"Content-Type": "application/json"
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.patch('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk', json=[\{"billable":"boolean","description":"string","duration":"integer","id":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","reset_billable_to_default":"boolean","start":"string","tag_ids":[\{\}],"task_id":"integer","time_block_id":"integer","tracked_at":"string","type":"string"\}], headers={'content-type': 'application/json'})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let json = client.request(Method::PATCH, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk".to_string())
.json(&serde_json::json!([\{"billable":"boolean","description":"string","duration":"integer","id":"integer","planned_at":"string","planned_duration":"integer","planned_start":"string","project_id":"integer","reset_billable_to_default":"boolean","start":"string","tag_ids":[\{\}],"task_id":"integer","time_block_id":"integer","tracked_at":"string","type":"string"\}]))
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
Body
| Name | Type | Description |
|---|
| items | Array of object | - |
items
| Name | Type | Description |
|---|
| billable | boolean | - |
| description | string | null |
| duration | integer | - |
| id | integer | - |
| planned_at | string | null |
| planned_duration | integer | null |
| planned_start | string | null |
| project_id | integer | null |
| reset_billable_to_default | boolean | - |
| start | string | null |
| tag_ids | Array of integer | TagIDs is tri-state: absent leaves the stored override untouched; explicit null clears the override (reset to task inheritance); an array (including empty) sets a manual, entry-authoritative override. |
| task_id | integer | null |
| time_block_id | integer | null |
| tracked_at | string | null |
| type | string | Valid values: activity, break. |
Response
204
No Content
400
Invalid request
403
Insufficient permissions
404
Time entry does not exist
422
One or more time entries violate required-field constraints (error=bulk_required_fields_missing)
500
Internal Server Error
PATCH Restore time entries in bulk
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk/restore
Restores time entries by the provided IDs.
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl -X PATCH https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk/restore \
-H "Content-Type: application/json" \
-u <email>:<password>
req, err := http.NewRequest(http.MethodGet,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk/restore")
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.SetBasicAuth("<email>", "<password>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk/restore')
req = Net::HTTP::Patch.new(uri)
req['Content-Type'] = "application/json"
req.basic_auth '<email>', '<password>'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk/restore", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64.encode(<email>:<password>)}`
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.patch('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk/restore', headers={'content-type': 'application/json', 'Authorization' : 'Basic %s' % b64encode(b"<email>:<password>").decode("ascii")})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new().basic_auth("<email>", "<password>");
let json = client.request(Method::PATCH, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/bulk/restore".to_string())
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
Query
| name | type | required | description |
|---|
| ids | []integer | true | TimeEntry IDs |
Response
204
No Content
400
Invalid request
403
Insufficient permissions
404
Time entry does not exist
500
Internal Server Error
GET List task time entries, streaming the response
https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/stream
Returns a list of time-entries based on the provided workspace ID and filter params.
Only the caller's own time entries are returned, whatever their role. To read other members' time, use the reports query endpoint (POST /reports/workspaces/{workspace_id}/query). For guests, entries on tasks or projects they cannot access, and entries with neither, are also left out.
Returns every entry that overlaps the date_from–date_to range: a tracked entry by its start and duration, a planned-only entry by its planned_start and planned_duration. A running timer has no duration yet, so it is returned only if it started within the range.
Entries without a task are left out unless include_taskless=true.
Returns every matching entry as a single JSON array, without pagination.
- cURL
- Go
- Ruby
- JavaScript
- Python
- Rust
curl https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/stream \
-H "Content-Type: application/json" \
-u <email>:<password>
req, err := http.NewRequest(http.MethodGet,
"https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/stream")
if err != nil {
print(err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.SetBasicAuth("<email>", "<password>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
print(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
print(err)
}
fmt.Print(string(body))
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/stream')
req = Net::HTTP::Get.new(uri)
req['Content-Type'] = "application/json"
req.basic_auth '<email>', '<password>'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(req)
end
puts JSON.parse(res.body)
fetch("https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/stream", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Basic ${base64.encode(<email>:<password>)}`
},
})
.then((resp) => resp.json())
.then((json) => {
console.log(json);
})
.catch(err => console.error(err));
import requests
from base64 import b64encode
data = requests.get('https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/stream', headers={'content-type': 'application/json', 'Authorization' : 'Basic %s' % b64encode(b"<email>:<password>").decode("ascii")})
print(data.json())
extern crate tokio;
extern crate serde_json;
use reqwest::{Client};
use reqwest::header::{CONTENT_TYPE};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new().basic_auth("<email>", "<password>");
let json = client.request(Method::GET, "https://focus.toggl.com/api/organizations/{organization_id}/workspaces/{workspace_id}/time-entries/stream".to_string())
.header(CONTENT_TYPE, "application/json")
.send()
.await?
.json()
.await?;
println!("{:#?}", json);
Ok(())
}
Parameters
Path
| name | type | required | description |
|---|
| organization_id | integer | true | organization ID |
| workspace_id | integer | true | workspace ID |
Query
| name | type | required | description |
|---|
| date_from | string | true | range start, as an RFC 3339 timestamp, e.g. 2026-09-01T00:00:00Z (percent-encode a + offset) |
| date_to | string | true | range end, as an RFC 3339 timestamp, e.g. 2026-09-30T23:59:59Z |
| task_id | integer | false | only entries of this task |
| status_id | integer | false | only entries whose task has this status |
| project_id | integer | false | only entries of this project (the entry's own project, else its task's) |
| type | string | false | time entry type |
| order_by | []string | false | sort columns, repeated or comma-separated; prefix - for descending. Ties are broken by id |
| archived | boolean | false | true for archived entries only, false for unarchived only; omit for both |
| include_taskless | boolean | false | include entries without a task (default false) |
| project_completed | boolean | false | true for entries on a completed project only, false for non-completed only; omit for both. An entry counts as completed when its own project or its task's project is completed |
| project_draft | boolean | false | true for entries on a draft project only, false for non-draft only; omit for both. Checks the same two projects as project_completed |
| expand_calendar_event | boolean | false | hydrate calendar event details inline |
Response
200
| Name | Type | Description |
|---|
| items | Array of object | - |
items
| Name | Type | Description |
|---|
| archived_at | string | - |
| billable | boolean | - |
| billable_source | string | Whether billable was set on the entry (manual) or resolved from its task, else its project (task_default). Valid values: manual, task_default. |
| calendar_event | object | - |
| calendar_event_id | integer | - |
| calendar_id | integer | CalendarID is the calendar service's calendar PK. It stays set after a calendar deselect clears calendar_event_id. |
| created_at | string | - |
| deleted_at | string | - |
| description | string | - |
| duration | integer | Tracked duration in seconds; omitted while the timer is running and for an entry that is only planned. |
| id | integer | - |
| pattern_generated | boolean | PatternGenerated is the response-facing derivative of GeneratedByTaskID: true when the entry was materialized by a series pattern. |
| pattern_managed | boolean | PatternManaged reports that the pattern engine owns this entry. Server-owned; present in responses, never bindable from a payload. |
| planned_at | string | - |
| planned_duration | integer | Planned duration in seconds. |
| planned_start | string | Planned start, for time planned on the calendar. |
| project | object | - |
| project_id | integer | - |
| start | string | Start of the tracked time; omitted for an entry that is only planned. |
| tag_ids | Array of integer | TagIDs is the entry's manual tag override. NULL (nil) means no override: the effective tags fall through to the linked task's tags. A non-nil value — including an empty array — is entry-authoritative. |
| tags | Array of object | Tags is the hydrated effective tag set: the override when TagIDs is set, else the linked task's active tags. Read-only. |
| task | object | - |
| task_id | integer | null |
| time_block_id | integer | - |
| timezone | string | Timezone is the entry creator's IANA timezone snapshotted at creation; NULL when unresolved. |
| toggl_user_id | integer | - |
| tracked_at | string | - |
| type | string | The type of time entry: either an activity or a break. Valid values: activity, break. |
| updated_at | string | - |
| workspace_id | integer | - |
calendar_event
| Name | Type | Description |
|---|
| all_day | boolean | - |
| background_color | string | null |
| end_time | string | - |
| foreground_color | string | null |
| html_link | string | - |
| id | integer | - |
| meeting_link | string | null |
| provider | string | - |
| start_time | string | - |
| title | string | - |
project
| Name | Type | Description |
|---|
| archived_at | string | - |
| billable | boolean | - |
| client | object | - |
| color | string | - |
| custom_field_values | Array of object | CustomFieldValues are the parent project's CF values, hydrated by callers that surface them (currently the task list, via task.service.hydrateTaskProjectCustomFieldValues which routes through customfield.Service.GetFieldsByIDs and respects the PermissionViewWorkspaceProjectCustomFields gate). Producers that don't hydrate (e.g. timeentry) leave the slice empty; omitempty hides it on those responses. |
| draft | boolean | - |
| id | integer | - |
| is_template | boolean | - |
| name | string | - |
| permissions | Array of string | - |
| private | boolean | - |
| rate | object | - |
| recurring_project_id | integer | RecurringProjectID links an instance to its recurring template, so clients can lane an entry under a collapsed series. |
client
| Name | Type | Description |
|---|
| id | integer | - |
| name | string | - |
custom_field_values
| Name | Type | Description |
|---|
| custom_field_id | integer | - |
| custom_field_name | string | - |
| field_type | string | - |
| selected_options | Array of object | - |
| value | object | - |
selected_options
| Name | Type | Description |
|---|
| is_deleted | boolean | - |
| option_id | integer | - |
| option_name | string | - |
rate
| Name | Type | Description |
|---|
| billable | boolean | - |
| currency | string | - |
| end_at | string | - |
| has_more_rates | boolean | - |
| hourly_rate | number | - |
| project_color | string | - |
| project_created_at | string | - |
| project_id | integer | - |
| project_name | string | - |
| project_rate_id | integer | - |
| start_at | string | - |
| workspace_rate_id | integer | - |
| Name | Type | Description |
|---|
| color | string | - |
| id | integer | - |
| name | string | - |
task
| Name | Type | Description |
|---|
| allocation_unit | string | Valid values: percent, flat. |
| archived_at | string | - |
| assignee_user_ids | Array of integer | - |
| assignees | Array of object | A unified column for all task entity assignments |
| auto_log_time | boolean | - |
| billable | boolean | null |
| client | object | - |
| color | string | - |
| created_at | string | - |
| custom_field_values | Array of object | - |
| deleted_at | string | - |
| description | string | - |
| end_date | string | - |
| estimate_type | string | Valid values: daily, total. |
| estimated_mins | integer | - |
| id | integer | - |
| is_recurring_series | boolean | IsRecurringSeries marks a canonical series root (S). Server-owned and read-only: no current API path can set it, and the response only exposes the marker while series behavior stays disabled. |
| is_template | boolean | - |
| metadata | object | - |
| name | string | - |
| notes | string | - |
| parent_task_id | integer | - |
| parent_task_name | string | - |
| pinned | boolean | - |
| position | integer | - |
| priority | string | Valid values: none, low, medium, high. |
| priority_at | string | - |
| private | boolean | - |
| project | object | - |
| project_id | integer | - |
| recurrence_date | string | - |
| recurring_rule | string | The rule of the task's series (RRULE with DTSTART); omitted for a normal task and once the series has stopped. |
| recurring_series_id | integer | The series this task is an occurrence of; omitted for a normal task. Only the server sets it. |
| recurring_task_id | integer | - |
| rollup_enabled | boolean | - |
| rrule | string | - |
| source | string | - |
| source_template_task_id | integer | - |
| start_date | string | - |
| status | object | - |
| status_id | integer | - |
| status_updated_at | string | - |
| tag_ids | Array of integer | - |
| tags | Array of object | - |
| toggl_user_id | integer | - |
| updated_at | string | - |
| workspace_id | integer | - |
assignees
| Name | Type | Description |
|---|
| id | integer | - |
| type | string | Valid values: user, team. |
client
| Name | Type | Description |
|---|
| id | integer | - |
| name | string | - |
custom_field_values
| Name | Type | Description |
|---|
| custom_field_id | integer | - |
| custom_field_name | string | - |
| field_type | string | - |
| selected_options | Array of object | - |
| value | object | - |
selected_options
| Name | Type | Description |
|---|
| is_deleted | boolean | - |
| option_id | integer | - |
| option_name | string | - |
| Name | Type | Description |
|---|
| all_day | boolean | - |
| calendar_event_id | integer | - |
| calendar_id | integer | - |
| extension_source | string | Browser-extension provenance: the source/config that produced the task and the page URL it was tracked from. Written by the extension's start-from-description flow; ExtensionURL is queryable via FindByURL. |
| extension_url | string | - |
| external_id | string | - |
| ical_uid | string | - |
| last_asserted_track_project_id | integer | LastAssertedTrackProjectID is the Track planned_task.project_id that the Focus→Track mirror last successfully wrote for this task. Track→Focus uses it to distinguish a stale echo (Track still holds this value after a Focus-side project move) from an intentional Track-side project move. Internal mirror state, written by toggl_api focus_sync_worker; not user-facing. |
| meeting_link | string | - |
| project_assignment | object | - |
| updated_at | string | - |
project_assignment
| Name | Type | Description |
|---|
| accuracy | number | - |
| confirmed_at | string | - |
| match_tier | string | Valid values: exact_name, similar_name. |
| matched_name | string | - |
| normalized_name | string | - |
| origin | string | Valid values: manual, auto_suggestion, confirmed_suggestion, integration, legacy, explicit_mapping. |
| suggested_at | string | - |
project
| Name | Type | Description |
|---|
| archived_at | string | - |
| billable | boolean | - |
| client | object | - |
| color | string | - |
| custom_field_values | Array of object | CustomFieldValues are the parent project's CF values, hydrated by callers that surface them (currently the task list, via task.service.hydrateTaskProjectCustomFieldValues which routes through customfield.Service.GetFieldsByIDs and respects the PermissionViewWorkspaceProjectCustomFields gate). Producers that don't hydrate (e.g. timeentry) leave the slice empty; omitempty hides it on those responses. |
| draft | boolean | - |
| id | integer | - |
| is_template | boolean | - |
| name | string | - |
| permissions | Array of string | - |
| private | boolean | - |
| rate | object | - |
| recurring_project_id | integer | RecurringProjectID links an instance to its recurring template, so clients can lane an entry under a collapsed series. |
client
| Name | Type | Description |
|---|
| id | integer | - |
| name | string | - |
custom_field_values
| Name | Type | Description |
|---|
| custom_field_id | integer | - |
| custom_field_name | string | - |
| field_type | string | - |
| selected_options | Array of object | - |
| value | object | - |
selected_options
| Name | Type | Description |
|---|
| is_deleted | boolean | - |
| option_id | integer | - |
| option_name | string | - |
rate
| Name | Type | Description |
|---|
| billable | boolean | - |
| currency | string | - |
| end_at | string | - |
| has_more_rates | boolean | - |
| hourly_rate | number | - |
| project_color | string | - |
| project_created_at | string | - |
| project_id | integer | - |
| project_name | string | - |
| project_rate_id | integer | - |
| start_at | string | - |
| workspace_rate_id | integer | - |
status
| Name | Type | Description |
|---|
| emoji | string | - |
| id | integer | - |
| name | string | - |
| type | string | Valid values: todo, done, in_progress, blocked. |
| Name | Type | Description |
|---|
| color | string | - |
| id | integer | - |
| name | string | - |
400
Invalid request
403
Insufficient permissions
500
Internal Server Error