API Documentation

Overview
Time Tracking

Extended REST API
Clients
Client Contacts
Projects
Tasks
People
Expenses
Expense Tracking
User Assignment
Task Assignment
Reports
Invoices
Invoice Messages
Invoice Payments
Invoice Categories


Questions? Contact support

Time Tracking API

The Time tracking API allows you to access and manipulate time entries in similar fashion to using the daily timesheet view. This allows developers to create lightweight clients or widgets to track time beyond directly interacting with Harvest through the web browser.


Retrieving entries and projects/tasks for a day

GET /daily

Retrieves entries for the today paired with the projects and tasks that can be added to the timesheet by the requesting user.

GET /daily/#{day_of_the_year}/#{year}

Optionally you may ask for entries for a given day by passing in date composed by the year and the day of the year (1..366).

HTTP Response: 200 Success
<daily>
  <for_day type="date">Wed, 18 Oct 2006</for_day>
  <day_entries>
    <day_entry>
      <id type="integer">195168</id>
      <client>Iridesco</client>
      <project>Harvest</project>
      <task>Backend Programming</task>
      <!-- Includes running timer if any -->
      <hours type="float">2.06</hours>
      <notes>Test api support</notes>
      <!-- OPTIONAL returned only if a timer is running -->
      <timer_started_at type="datetime">
        Wed, 18 Oct 2006 09:53:06 -0000
      </timer_started_at>
      <created_at type="datetime">Wed, 18 Oct 2006 09:53:06 -0000</created_at>
    </day_entry>
    <day_entry>
      ...
    </day_entry>
  </day_entries>
  <!-- These are the project-task combinations that can be added to
  the timesheet. Not present in readonly timesheets or for users
  without assigned projects. -->
  <projects>
    <project>
      <name>Click and Type</name>
      <code></code>
      <id type="integer">3</id>
      <client>AFS</client>
      <tasks>
        <task>
          <name>Security support</name>
          <id type="integer">14</id>
          <billable type="boolean">true</billable>
        </task>
        <task>
          ...
        </task>
      </tasks>
    </project>
    <project>
      ...
    </project>
  </projects>
</daily>


Retrieving a single entry

GET /daily/show/#{day_entry_id}

Retrieves the selected entry.

HTTP Response: 200 Success
<timer>
  <day_entry>
    <id type="integer">195168</id>
    <client>Iridesco</client>
    <project>Harvest</project>
    <task>Backend Programming</task>
    <hours>0.00</hours>
    <notes>Test api support</notes>

    <!-- OPTIONAL returned only if the timer is running -->
    <timer_started_at type="datetime">
      Wed, 17 Oct 2006 10:45:07 +0000
    </timer_started_at>
  </day_entry>
</timer>


Toggling a timer

GET /daily/timer/#{day_entry_id}

Starts and stops a timer for a selected entry.

HTTP Response: 200 Success
<timer>
  <!-- day_entry with toggled timer -->
  <day_entry>
    <id type="integer">195168</id>
    <client>Iridesco</client>
    <project>Harvest</project>
    <task>Backend Programming</task>
    <hours>2.06</hours>
    <notes>Test api support</notes>
    <!-- OPTIONAL returned if the timer was started -->
    <timer_started_at type="datetime">
      Wed, 18 Oct 2006 10:45:07 +0000
    </timer_started_at>
  </day_entry>
  <!-- OPTIONAL if a timer for another day_entry was stopped by side
  effect, returns a confirmed value for the timer. Protects against
  different clock rates on the client side. -->
  <hours_for_previously_running_timer type="float">
    0.87
  </hours_for_previously_running_timer>
</timer>


Creating an entry

POST /daily/add

Create an entry on the daily screen

HTTP Response: 201 Created

You need to POST the following:

<request>
  <notes>Test api support</notes>
  <hours>3</hours>
  <project_id type="integer">3</project_id>
  <task_id type="integer">14</task_id>
  <spent_at type="date">Tue, 17 Oct 2006</spent_at>
</request>

Note: you can transmit a blank string as hours if you want to start
a timer against the new day_entry record. For example: <hours> </hours>

You'll get the following reply:

<timer>
  <!-- new entry -->
  <day_entry>
    <id type="integer">195168</id>
    <client>Iridesco</client>
    <project>Harvest</project>
    <task>Backend Programming</task>
    <hours>0.00</hours>
    <notes>Test api support</notes>

    <!-- OPTIONAL returned only if a timer was started -->
    <timer_started_at type="datetime">
      Wed, 17 Oct 2006 10:45:07 +0000
    </timer_started_at>
  </day_entry>

  <!-- OPTIONAL returned only if a timer for another -->
  <!--          day_entry was stopped as a result    -->
  <hours_for_previously_running_timer type="float">
    0.87
  </hours_for_previously_running_timer>
</timer>

If your account uses timestamp timers, you may alternatively POST a started_at and ended_at time:

<request>
  <notes>Test api support</notes>
  <started_at>8:00am</started_at>
  <ended_at>9:00am</ended_at>
  <project_id type="integer">3</project_id>
  <task_id type="integer">14</task_id>
  <spent_at type="date">Tue, 17 Oct 2006</spent_at>
</request>

Note: you can transmit blank strings as started_at and ended_at
if you want to start a timer against the new day_entry record.
For example: <started_at> </started_at>





Deleting an entry

DELETE /daily/delete/#{day_entry_id}

Deletes a day entry.

HTTP Response: 200 Success


Updating an entry

POST /daily/update/#{day_entry_id}

Updates the note, effort, project or task for a day entry. All sensible values are overwritten for the day entry with the data provided in your request.

HTTP Response: 200 Success

You need to POST the following:

<request>
  <notes>New notes</notes>
  <hours>1.07</hours>
  <spent_at type="date">Tue, 17 Oct 2006</spent_at>
  <project_id>52234</project_id>
  <task_id>67567</task_id>
</request>

If your account uses timestamp timers, you may alternatively POST a started_at and ended_at times in lieu of hours.