Users assigned the view-management-reports right (see User Information) can view any users' time entries. Other users can only view their own time entries.

Users can not create, update or delete another user's time entries.

Time Entry Data

Each time entry can be associated with one job and one activity (if activities are enabled - see Account Information, as well as a note.

There are two types of time entries:

  • Start / end time entries - Start and end times must be specified, and should each include a time zone offset (e.g. UTC-5 should be specified as -05:00). The duration will be calculated as the difference between these times.
  • Duration-only time entries - A start date and duration must be specified.

Durations are in minutes. If seconds are specified in start or end times, they will be truncated rather than rounded.

Show

Request

GET /time_entries/#{time-entry-id}.xml

Optional parameters

include=[users,
         activities,
         jobs,
         job_ancestors,
         billing]

Resources related to time entries, including users, activities, and jobs, can be included in the response. Specify include=jobs,job_ancestors in order to include jobs and their ancestors with each time entry. For example:

GET /time_entries/#{time-entry-id}.xml?include=users,activities,jobs,job_ancestors

Response

Status: 200 OK

<time-entry>
  <id type="integer">3864</id>
  <!-- if start / end time entry -->
  <start-time type="datetime">2007-08-30T08:28:00-04:00</start-time>
  <end-time type="datetime">2007-08-30T13:24:00-04:00</end-time>
  <!-- else if duration-only time entry -->
  <start-date type="date">2007-08-30</start-date>
  <!-- end -->
  <duration type="integer">296</duration>
  <note/>
  <!-- if include=users -->
  <user>
    ...
  </user>
  <!-- else -->
  <user-id type="integer">25</user-id>
  <!-- end -->
  <!-- if include=jobs -->
  <job>
    ...
    <!-- if include=job_ancestors -->
    <parent>
    ...
      <parent>
      ...
      </parent>
    </parent>
    <!-- end -->
  </job>
  <!-- else -->
  <job-id type="integer">29</job-id>
  <!-- end
  <!-- if activities are enabled -->
  <!--   if include=activities -->
  <activity>
    ...
  </activity>
  <!--   else -->
  <activity-id type="integer">16</activity-id>
  <!--   end -->
  <!-- end -->
  <!-- if include=billing (and user has permission to view billing) -->
  <billable type="boolean">true</billable>
  <!--   if billing rate has been assigned -->
  <billing-rate>
    ...
  </billing-rate>
  <!--   end -->
  <!-- end -->
  <!-- if include=timestamps -->
  <created-at type="datetime">2007-08-30T12:28:00Z</created-at>
  <updated-at type="datetime">2007-12-20T06:55:23Z</updated-at>
  <!-- end -->
</time-entry>

List

Request

To request all time entries:

GET /time_entries.xml

As mentioned above, this request will return all the time entries in your account (or the first 1000, whichever is less) if you've been assigned the view-management-reports right. Other users will only see their own time entries.

Optional parameters

The following parameters may be combined in any order to filter and customize the listing of time entries:

start=#{starting offset in array}

end=#{starting offset in array}

include=[users,
         activities,
         jobs,
         job_ancestors,
         billing]

jobs=[none,
      active,
      inactive,
      my_assignments,
      my_active_assignments,
      my_inactive_assignments,
      #{any-job-id}]

activities=[none,
            active,
            inactive,
            my_assignments,
            my_active_assignments,
            my_inactive_assignments,
            #{any-activity-id}]

users=[me,
       active,
       inactive,
       #{any-user-id}]

start_date=#{date in the format YYYYMMDD}

end_date=#{date in the format YYYYMMDD}

utc_offset=#{UTC offset in minutes (assumes 0 if not set)}

Specify start and end to request a specific range of items. For example:

GET /time_entries.xml?start=1&end=4

Resources related to time entries, including users, activities, and jobs, can be included in the response. Specify include=jobs,job_ancestors in order to include jobs and their ancestors with each time entry. For example:

GET /time_entries.xml?include=users,activities,jobs,job_ancestors

Filtering by job-id will find time entries associated with the specified job as well as its descendents. The following example will find all of the current user's time entries associated with jobs 1 or 2 and any descendents of these jobs:

GET /time_entries.xml?jobs=1,2&users=me

Note that parameters of the same filter type are combined with a logical OR, and filters of different types are combined with a logical AND. Therefore, the conditions in the above example could be written in pseudo-code as ((job = 1) OR (job = 2)) AND (user = me).

When specifying a start_date and / or end_date, also specify utc_offset if you want those dates to start at a time other than midnight UTC. The following example requests time entries started on or after midnight, January 1, 2008, UTC-5:00 (i.e. -300 minutes offset from UTC):

GET /time_entries.xml?start_date=20080101&utc_offset=-300

The reason that filtering is done at the date level instead of at a finer scale of hours or minutes is so that "duration only" time entries can be included in the results in a logical manner. As discussed above, these time entries are only associated with a date, and don't have a time zone association.

Response

Status: 200 OK

<time-entries type="array">
  <time-entry>
    ...
  </time-entry>
  ...
</time-entries>

Time Summary

Time summaries return the total duration of time entries, filtered and grouped as specified below.

Request

To request a time summary:

GET /time_entries/summary.xml

Optional parameters

The following parameters may be combined in any order to filter and customize time summaries (note: filters match the filters documented above under List):

group_by=[users,
          activities,
          [jobs1, jobs2, jobs3, jobs4, jobs5]] # jobs are grouped by a specific depth

jobs=[none,
      active,
      inactive,
      my_assignments,
      my_active_assignments,
      my_inactive_assignments,
      #{any-job-id}]

activities=[none,
            active,
            inactive,
            my_assignments,
            my_active_assignments,
            my_inactive_assignments,
            #{any-activity-id}]

users=[me,
       active,
       inactive,
       #{any-user-id}]

start_date=#{date in the format YYYYMMDD}

end_date=#{date in the format YYYYMMDD}

utc_offset=#{UTC offset in minutes (assumes 0 if not set)}

Time summaries can be grouped using the group_by parameter. A maximum of two groupings can be specified. Since jobs are hierarchical, it is necessary to specify the depth to which you want to group jobs. Higher levels will automatically be included. For an account that includes the job types clients, projects and tasks, the following request should return a report grouped by clients, projects, tasks and then users: ``` GET /time_entries/summary.xml?group_by=jobs3,users ```

This example should return a summary of the current user's time entries associated with jobs 1 or 2, grouped by the first two levels of jobs:

GET /time_entries/summary.xml?clients=1,2&users=me&group_by=jobs2

If you haven't done so already, please read the List section above to understand the nuances of the filters.

Response

Responses will vary in structure depending upon the group_by parameter specified. Without specifying any grouping, results will be similar to the following:

Status: 200 OK

<time-summary>
  <duration type="integer">11653</duration>
</time-summary>

The following response is to a request with group_by=jobs2:

Status: 200 OK

<time-summary>
  <jobs>
    <job>
      <id nil="true" type="integer"></id>
      <name nil="true"></name>
      <code nil="true"></code>
      <jobs>
        <job>
          <id nil="true" type="integer"></id>
          <duration nil="true" type="integer">840</duration>
          <name nil="true"></name>
          <code nil="true"></code>
        </job>
      </jobs>
      <duration type="integer">840</duration>
    </job>
    <job>
      <id type="integer">24</id>
      <name>American UFO Association</name>
      <code nil="true"></code>
      <jobs>
        <job>
          <id type="integer">30</id>
          <duration type="integer">3329</duration>
          <name>Area 51 job</name>
          <code nil="true"></code>
        </job>
        <job>
          <id type="integer">31</id>
          <duration type="integer">524</duration>
          <name>UFO #7856</name>
          <code nil="true"></code>
        </job>
      </jobs>
      <duration type="integer">3853</duration>
    </job>
    ...
  </jobs>
  <duration type="integer">11653</duration>
</time-summary>

As shown above, unclassified time will be returned with a blank name and id.

Create "Start / Stop" Time Entry

Request

POST /time_entries.xml

Include the following data with your request:

<time-entry>
  <!-- start and end times (required)
       IMPORTANT: include time zone, such as -05:00 -->
  <start-time>2008-01-08T16:01:00-05:00</start-time>
  <end-time>2008-01-08T16:11:00-05:00</end-time>
  <!-- job-id (optional)-->
  <job-id>28</job-id>
  <!-- activity-id (optional)-->
  <activity-id>16</activity-id>
  <!-- note (optional, max-length=255)-->
  <note>
    <![CDATA[Phone call from June.]]>
  </note>
</time-entry>

Response

Status: 201 Created

<time-entry>
  <id type="integer">#{new-time-entry-id}</id>
  <user-id type="integer">25</user-id>
  <duration type="integer">10</duration>
  <job-id type="integer">28</job-id>
  <activity-id type="integer">16</activity-id>
  <note>
    <![CDATA[Phone call from June.]]>
  </note>
  <!-- if include=timestamps -->
  <start-time type="datetime">2008-01-08T16:01:00-05:00</start-time>
  <end-time type="datetime">2008-01-08T16:11:00-05:00</end-time>
  <!-- end -->
</time-entry>

Create "Duration Only" Time Entry

Request

POST /time_entries.xml

Include the following data with your request:

<time-entry>
  <!-- start date (required) -->
  <start-date>2008-01-08</start-date>
  <!-- duration in minutes (required) -->
  <duration>10</duration>
  <!-- job-id (optional)-->
  <job-id>28</job-id>
  <!-- activity-id (optional)-->
  <activity-id>16</activity-id>
  <!-- note (optional, max-length=255)-->
  <note>
    <![CDATA[Phone call from June.]]>
  </note>
</time-entry>

Response

Status: 201 Created

<time-entry>
  <id type="integer">#{new-time-entry-id}</id>
  <user-id type="integer">25</user-id>
  <start-date type="date">2008-01-08</start-date>
  <duration type="integer">10</duration>
  <job-id type="integer">28</job-id>
  <activity-id type="integer">16</activity-id>
  <note>
    <![CDATA[Phone call from June.]]>
  </note>
  <!-- if include=timestamps -->
  <start-time type="datetime">2008-01-08T16:01:00-05:00</start-time>
  <end-time type="datetime">2008-01-08T16:11:00-05:00</end-time>
  <!-- end -->
</time-entry>

Update

Request

PUT /time_entries/#{time-entry-id}.xml

Include ANY of the following data with your request:

<time-entry>
  <!-- if "start / end" time entry -->
  <start-time>2008-01-08T16:01:00-05:00</start-time>
  <end-time>2008-01-08T16:11:00-05:00</end-time>
  <!-- end -->
  <!-- if "duration-only" time entry -->
  <start-date>2008-01-08</start-date>
  <duration>10</duration>
  <!-- end -->
  <job-id>28</job-id>
  <activity-id>16</activity-id>
  <note>
    <![CDATA[Phone call from June.]]>
  </note>
</time-entry>

In order to change a "duration-only" time entry into a "start / end" time entry, specify an empty start-date and a valid start-time and end-time. In order to do the reverse, specify a valid start-date and duration.

Response

Status: 200 OK

Delete

Request

DELETE /time_entries/#{time-entry-id}.xml

Response

Status: 200 OK

Continue to Expense Categories