# GET /api/reports/timesheets > Reporting - Logged hours Time logged over a date range, grouped by team member, project, task or day. Timesheets live in the project_time table, and the duration of an entry is what RISE itself computes: the difference between start_time and end_time plus the manually entered hours column. Totals therefore match the CRM's own timesheet screens to the second. The range is applied to start_time, which RISE stores in UTC, so a caller that reports in a local timezone should shift its window accordingly. A running timer has no end_time yet and contributes nothing until it is stopped. Every group carries both total_seconds and hours: seconds are exact and safe to sum further, hours are rounded to two decimals for display. Groups are returned largest first, except for group_by=day which is returned in date order so it can be plotted directly. Ordering is fully deterministic - ties are broken on the group key - so paging never repeats or skips a group. The groups list is paged, identically for all four groupings. limit selects the page size, at most 200, offset selects the page, and data.pagination reports limit, offset, returned, truncated and next_offset. truncated true means more groups match the request and next_offset is the offset that returns the following page; truncated false means the list is complete. This matters most for group_by=day, because the range may be up to 366 days long while a page holds at most 200 days: such a request answers with the first 200 days, truncated true and next_offset 200, and never drops the remainder without saying so. data.totals is computed over the whole range by its own aggregate, so it is the same on every page and always exact. - **Endpoint:** `GET /api/reports/timesheets` - **Group:** Reporting - **Since:** v1.0.0 - **Defined in:** `RestApi/Controllers/ReportingController.php` ## Headers | Field | Type | Description | | --- | --- | --- | | `authtoken` | String | Authentication token, generated from admin area | ## Parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `from` | String | no | Start of the range, inclusive, YYYY-MM-DD. Defaults to 29 days before to. | | `to` | String | no | End of the range, inclusive, YYYY-MM-DD. Defaults to the RISE server date. | | `group_by` | String | no | How to group the logged time. Default: `member`. | | `project_id` | Number | no | Restrict to one project. | | `user_id` | Number | no | Restrict to one team member. | | `limit` | Number | no | Groups per page. Default: `200`. | | `offset` | Number | no | Groups to skip, for paging. Default: `0`. | ## Request example ```bash curl -X GET "https://yoursite.com/index.php/api/reports/timesheets" \ -H "authtoken: YOUR_API_TOKEN" ``` ## Responses ### Success - Success-Response: ```json HTTP/1.1 200 OK { "status": true, "data": { "range": { "from": "2026-06-23", "to": "2026-07-22", "days": 30, "date_field": "project_time.start_time" }, "group_by": "member", "totals": { "entries": 412, "total_seconds": 1234800, "hours": 343 }, "groups": [ { "group_id": 3, "label": "Jane Doe", "entries": 88, "total_seconds": 320400, "hours": 89 } ], "pagination": { "limit": 200, "offset": 0, "returned": 12, "truncated": false, "next_offset": null } }, "meta": { "generated_at": "2026-07-22 10:04:11", "timezone": "UTC", "max_groups": 200 } } ``` ### Error - Error-Response: ```json HTTP/1.1 400 Bad Request { "status": false, "messages": { "error": "The \"group_by\" parameter must be one of: member, project, task, day." } } ``` --- Part of the [REST API for RISE CRM](https://risecrm.themesic.com/apiguide/) documentation. Full page: https://risecrm.themesic.com/apiguide/reporting/get-report-timesheets/