[Looking for Charlie's main web site?]

Integrating ColdFusion and the REST API for Harvest (TimeSheet Service); Accessing XML in CF

Note: This blog post is from 2009. Some content may be outdated--though not necessarily. Same with links and subsequent comments from myself or others. Corrections are welcome, in the comments. And I may revise the content as necessary.
Often we will find API docs for a given service without any reference to a CFML example. I just experienced that, and while I offered a suggested example to the vendor, I wanted to share it here in case it never gets posted. At least if someone Googles in the future they may find this. Also, I conclude with some insights into learning more about the power of XML processing in CF.

I'll demonstrate here calling the REST API for the Harvest online timesheet application. (If you would want to point me to another alternative timesheet app, no need as I'm happy. I realize there are many. Indeed, I list many of them on my CF411 site section for time tracking tools and services.)

Anyway, the Harvest API docs pages do show a Ruby example. While I didn't take the time replicate that exactly (it has a few more touches like error-handling, extensibility, etc), I did want to at least show how one could call the REST-based API using CFHTTP.

Some Simple CFHTTP Examples

Here are two simple examples, first, showing calling the who_am_i method:

<cfhttp url="http://subdomain.harvestapp.com/account/who_am_i" username="user@email.com" password="password">
   <cfhttpparam type="header" name="Accept" value="application/xml">
   <cfhttpparam type="header" name="Content-Type" value="application/xml">

Note that I'm using the same nomenclature they do in their docs, in that you would need to change the "subdomain" value to be whatever your own it. Also, like them I've just left the placeholders above for your Harvest username/password. And the API docs show needing to pass in those two headers, which I do in the CFHTTPPARAM.

Getting TimeSheet Entries

The next, more interesting example calls the method to get the timesheet data for a given day (and yes, since the REST API has no state you do need to pass in the username/password on each call. In a more complete app, you might prompt the user for those and store it in a session for reuse, etc.):

<cfhttp url="http://subdomain.harvestapp.com/account/daily/245/2009" username="user@email.com" password="password">
   <cfhttpparam type="header" name="Accept" value="application/xml">
   <cfhttpparam type="header" name="Content-Type" value="application/xml">

Note also that the date is in Julian format (the number of the day in the year). I'm showing Dec 10. Again, I could elaborate this to take in URL variables to change the date. I'll leave that to the reader.

So how do you calculate the date in Julian format? Well, you could of course modify the code to use CF's date/time functions to do a conversion, but if you just want to look up a given day for manually, here's a trick. If you've never noticed it (as a Harvest user), the real web-based Harvest timesheet always also shows the julian date in the URL for a timesheet entry.

Getting Real Data from the Returned XML

The result of both calls is some XML. In the first example, I ignored it. In the second, of course, you need to get the data.

So I pass it to the XMLParse function (new in CF6 and still missed by many) . That converts the XML into an internal CF XML object, which represents the XML as a series of nested structures, arrays, and attributes. If you do a CFDUMP of the result, you'll see what I mean:

<cfset result=xmlparse(cfhttp.filecontent)>
<cfdump var="#result.daily.day_entries#">

And to output the hours for the first entry, one could do this:


How did I figure out that nested set of arrays and structures, you ask?...

Learning More about XML processing in CFML

I won't elaborate here about how to go about accessing the various parts of the (sometimes complex) internal XML object. But you can learn it really quickly. Most CFers are blithely unaware of the power of this feature set (or they get confused and give up). There are two (well, three) places you can learn more.

First, the best resource on CF/XML processing (in my opinion) is a great extended Adobe Devnet article by Nate Weiss. Though from 2002, it's an all-time classic (recovered from the archive.org site).

He introduces the topic assuming you know nothing about XML, and steps quickly into processing it in CFML (reading and writing it), including understanding all those structures, arrays, and attributes. Within just a few pages, you'll be amazed at how much more productive you can be with XML processing in CFML.

Nate even introduced the valuable concepts of using XPath/XMLSearch to make accessing XML even easier and XSLT to do conversions from XML to other formats. He does all this very gently and in a compelling way. I still refer people to it often, to this day (and return to it myself when I'm stumped).

That said, there are of course a few ways that XML processing in CFML has evolved since CF6, so you will want to check out the CF docs as well. Besides the CFML Reference on XMLParse, note that there is also a chapter on the topic in the oft-missed ColdFusion Developer's Guide.

Many only know of the CFML Reference, but they miss out on this 1,000+ page free online book (from Adobe). The CF8 version of this is here and the chapter here. The CF9 version of the manual is here and the chapter is here.

Hope any of that may help someone.

Thanks for taking the time to document this!
I realize this is an old post, but as you say at the beginning, when I searched for Harvest and ColdFusion this was right there waiting to help me. :)
I actually started using Harvest based on a comment you wrote somewhere about it. Now that I have been using it for a few months, I realize that it is great at letting me enter my data without getting in my way, but I wanted more functionality out of the reporting side.
I have never interfaced with a REST web service before, but thanks to this post and the nature of CF in that it makes things so easy, I was able to create a custom export report for myself that I am happy with within a couple of hours.
By the way, your HTML link for "Harvest API docs pages" towards the top of this post has a typo so it isn't clickable.
# Posted By Ken Auenson | 8/18/11 9:11 PM
Thanks for that, Ken, especially the acknowledgment that my hope it would help someone some day did come true. To quote the old A-Team line, "I love it when a plan comes together." :-)

Glad to have helped, and to have turned you on to Harvest. I still use it daily.

And I fixed that typo (it said a href- instead of a href=. Fixed that, and confirmed there are no other instances of that mistake anywhere else. Thanks.)

If you ever come up with something that you may think would be of generic interest, that you may submit to them or post on your own somewhere, feel free to come in here and leave word about it for others. Until again.
# Posted By Charlie Arehart | 8/19/11 10:16 AM
I see that the link to the PDF is no longer active on the Adobe website. I was able to locate the PDF at archive.org. For those looking it is:

Thanks for the nice article Charlie.
# Posted By Bob Lawrence | 1/13/14 9:10 AM
@Bob, thanks. I'll update the blog entry to use that archived link (love the archive.org, and have used it many times here for this very reason), but I will leave your comment, in gratitude for your sharing the observation.
# Posted By Charlie Arehart | 1/13/14 4:42 PM
FWIW, a somewhat more elaborate implementation in CFML:

# Posted By Nathan D | 2/4/14 5:33 PM
Very cool, Nathan. Thanks for sharing (and indeed, for building) that.

So I'm curious if you (or any readers of this blog entry) have put together any cool harvest api calls that perhaps report on info that the current harvest UI just does not provide. I have dozens of requests I want to build to get info I need that the web interface either doesn't provide, or only with lots of steps.

There doesn't seem to be any place where such are listed. (Actually, someone might point out the community hub, https://github.com/h...&-Hacks, but that seems to be more about projects like yours. BTW, have you sought to get yours listed there?)
# Posted By Charlie Arehart | 2/4/14 7:11 PM
The biggest thing I lack in Harvest is good graphs that show trend lines of billing over time. Basically, I want a dashboard I can quickly see progress in. Unfortunately, I haven't yet actually built that around this client. I haven't ever tried to get this listed officially because although I did some testing of it I haven't built proper unit tests to make sure it is and stays robust.
# Posted By Nathan D | 2/4/14 7:17 PM
@Nathan, ah, ok. Maybe someone will pick this up and run with it. CF does indeed graphing so easy, and all the more in CF10 with its HTML 5 charts. It could be a cool exercise for someone to bring the two together. I don't myself want for graphs: I want for more effective information reporting. ;-) So I don't see myself jumping in to help there anytime soon, I'm afraid. But it could be fun.

As for your holding off on sharing on the hub, well, I'd think anything (showing CFML integration there) is better than nothing, and being open source, people understand that it may not necessarily be robust (I think folks judge that by the number of users, number of committers, etc.), so I'd think "go for it". Who knows, it may take time and that would give more chance for others to find your library here or elsewhere. And perhaps they (the harvest hub managers) might even do some vetting themselves before accepting it also.

Anyway, keep up the good work. I may be exploring the library soon for some of my harvest api exploration interests.
# Posted By Charlie Arehart | 2/4/14 7:41 PM
I will note that since writing this in 2009, the Harvest API had evolved to the point that 3 things need to change:

- the url used in the call to the API must now be an HTTPS url
- where I showed calling for a day's timesheet entries, using /account/daily/245/2009", you no longer need "/account" there, so it would be daily/245/2009
- there has since been added an additional querystring option, "?slim=1", which the docs say should cause it to "return only tracked time, and no assignments" ?slim=1", but I find that without it, the result is huge and never easily processed. So you should try always to use it, as in /daily/245/2009?slim=1

And to be clear, this is indeed still referring only to the "v1" Harvest API. There is a new V2 API, and the old one will stop working in Jan 2019 (https://www.getharve...)
# Posted By Charlie Arehart | 12/29/17 6:08 PM
And here's code that uses the new v2 api (more on that at https://help.getharv...).

Note that authentication is quite different in the new API, and you have choices between oauth2 and a "personal access token" (more at https://help.getharv...authentication-api/authentication/authentication/).

I am showing here a quick, simple example of using the token approach (see the docs for more on that, and getting yours), where you would fill in below the values for "your_account_id" and "your_token". The end of the URL shows a rest path which returns info similar to the whoami feature in the v1 API:

<cfhttp url="https://api.harvesta...">
<cfhttpparam type="header" name="Harvest-Account-ID" value="your_acount_id">
<cfhttpparam type="header" name="Authorization" value="Bearer your_token">
<cfhttpparam type="header" name="User-Agent" value="Harvest API Example">

<cfdump var="#DeserializeJSON(cfhttp.filecontent)#">

And note that the result that comes back is a json packet, so the dump deserializes to an array of structs for you. You can read the API docs to learn about different rest URLs. For instance, to get info about a project, you just pass in the projectid by using this as the URL:

# Posted By Charlie Arehart | 12/29/17 6:48 PM
Looks good !
# Posted By Animesh Dutta | 9/26/18 10:54 PM
Copyright ©2020 Charlie Arehart
Carehart Logo
BlogCFC was created by Raymond Camden. This blog is running version 5.005.
(Want to validate the html in this page?)

Managed Hosting Services provided by
Managed Dedicated Hosting