List worklogs
GEThttps://
Returns booked hours, newest first by date.
Access
- Token scope
- api:
read: times Without it:
403 insufficient-token-scope. - Permission
- hours::
read Without it:
403 insufficient-permission. - Rate cost
- 1 unit, read bucket.
view=detailmakes it 3. Adds:with_total=1+2, each include +1,q+2. - Also
If-None-Matchanswers304when nothing changed. Never cached:private, no-store. Unknown query parameters are refused, never ignored.
Both are checked, scope first. A token never does more than its user. How access is checked
A caller sees only their own entries unless they hold times::update_others or times::delete_others, and that narrowing is applied before any client filter, before pagination and before the count, so filter[user_id] widens nothing on its own. meta.page.total is null unless you ask for it with with_total=1, which adds 2 read units, because worklogs are usually an organisation's largest collection. q matches anywhere in description. project and work_item are on every row already, so naming either in ?include= is a 400; budget, task and user are the includable relations, and each additionally requires the read permission and token scope of its own prefix. There is no duration string and no is_in_review flag: format minutes yourself, and read review state as (exceeded_budget || billable_change_reason) && reviewed_by == null && invoice_id == null.
Query parameters
Unknown parameters are refused with 400, naming the parameter.
43 parameters in 6 groups
Views
Pagination
Cursor pagination only. Follow links.next.
Rows per page. At most 25 when include names a to-many relation; a larger value is 400 invalid-query-parameter.
Default: 25
Filters
- =
- equals
- [in]
- any of a comma-separated list
- [null]
trueorfalse: whether it is empty- [gte][lte][lt]
- at least, at most, less than
Budget the hours are invoiced against.
A comma-separated list of at most 100 values.
Creation timestamp. Pair with sort=id for a stable full export.
The calendar day the work happened. Combine gte and lte for a range.
The invoice these hours were billed on. Use the null operator, filter[invoice_id][null]=true, to find uninvoiced hours.
A comma-separated list of at most 100 values.
Project the hours are booked against.
A comma-separated list of at most 100 values.
The reviewer who settled the entry. Use filter[reviewed_by][null]=true to find entries nobody has reviewed.
A comma-separated list of at most 100 values.
The sales-invoice line these hours were billed on.
A comma-separated list of at most 100 values.
Task the hours are booked on.
A comma-separated list of at most 100 values.
Last-write timestamp. Use for incremental sync.
Whose hours to return. Accepts the literal me, and defaults to the authenticated user. Naming another employee requires the times::update_others permission.
A comma-separated list of at most 100 values.
Search
Sorting
| Key | Sorts by |
|---|---|
date | The calendar day the work happened, newest first. |
id | Id, ascending. An id never changes, so use this for a stable full export. |
updated_at | Last-write timestamp; pair with the id tiebreaker for sync. |
Prefix a key with - for descending.
Includes
| Path | Adds | Cost |
|---|---|---|
budget | The budget the hours are invoiced against. | +1 |
task | The task the hours are booked on, limited to the tasks you can see on the tasks collection. | +1 |
user | The employee the hours belong to. | +1 |
Request headers
A correlation id of your own. It is recorded in our logs next to the server's request id but never returned: the response header always carries the server's id. A value that does not match the pattern is discarded silently, never rejected.
Response 200
Returns a page of TimeList rows.
The rows, each a TimeList.
per_page, next_cursor, prev_cursor, total.
self, next, prev.
Each row is a TimeList: 23 fields.
Headers
ETag- A weak validator (
W/"…") of the rendered page. RateLimit- The IETF
httpapi-ratelimit-headersform of your remaining allowance on the tightest bucket this request touched. RateLimit-Policy- The ceiling and window of that bucket, as
<limit>;w=<seconds>. X-Request-Id- The server-generated ULID correlating this response with our logs.
Every response also carries Cache-Control, Vary, X-Content-Type-Options, X-Plnnrly-Api-Version, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Headers and versioning
The project the hours are booked against, always expanded. Its customer_id is the customer to fetch with GET /api/v2/customers/{customer}; the customer itself is not nested.
Budget the hours are invoiced against. Derived from the task, never accepted on a write; null when no task is set or the budget is deleted.
The budget the hours are invoiced against. Expanding it with ?include=budget additionally requires the budgets::read permission and a token scope covering the budgets prefix.
Task the hours are booked on; null when the entry is not attached to one, or once that task is deleted.
The task the hours are booked on. Expanding it with ?include=task additionally requires the kanban::manage_self permission and a token scope covering the kanban prefix, and is limited to the tasks you can see on the tasks collection.
Work item the hours are attached to; null when the entry is not attached to one, or once that work item is deleted.
The work item the hours are attached to, always expanded.
Employee the hours belong to. Assigned on create (from the authenticated user, or from on_behalf_of_user_id) and never writable afterwards; reassigning a worklog is a DELETE plus a POST.
The employee the hours belong to. Expanding it with ?include=user additionally requires the users::read permission and a token scope covering the users prefix.
Whether these hours are excluded from invoicing. Inferred from the task's is_billable flag (false without a task) on create and again whenever task_id is sent. Sending a value that differs from the inferred or current one requires billable_change_reason and puts the entry in a reviewer's queue; without it the write is a 422 on /billable_change_reason.
Whether the hours count as overtime. Sending it without the times::write_overtime permission leaves it false and adds an input_ignored warning rather than failing the write.
Whether the entry was booked knowing it exceeds its budget. Set on create only, and true whenever a reason is sent; setting it puts the entry in a reviewer's queue.
Why the budget overrun was accepted. Sending it on create marks the entry as exceeding its budget. Once a reviewer has settled the entry it can no longer be changed: a PATCH that does answers 409 reason_reviewed.
Why the inferred billability was overridden. Required whenever is_not_billable is sent with a value that differs from the inferred or current one, on create and on PATCH alike; the override puts the entry in a reviewer's queue, reopening one a reviewer had already settled.
The reviewer who settled the entry, once one has. Set by the review queue in plnnrly; this API exposes no way to write it.
The invoice these hours were billed on, once they have been. While it is set, the entry is locked: a write answers 409 with conflict.reason locked_by_invoice.
The sales-invoice line these hours were billed on, once they have been. Set by the invoicing flow.
Errors
8 errors every operation can return: 304, 400, 401, 404, 406, 429, 500, 503
304 Not modified.
400 Bad request.
idempotency-key-requiredinvalid-cursorinvalid-query-parametermalformed-requestunsupported-parameter
401 Unauthenticated.
404 Not found.
406 Not acceptable.
429 Too many requests.
500 Server error.
503 Service unavailable.
This path allows GET, POST. Anything else answers 405.
Something wrong or missing on this page? Tell us