Skip to content

Freshbooks API using OAuth

So, I came across a project for work where I had to work with the Freshbooks API. Instead of having users put in their “API URL” and “Authentication Token” everytime (by going to their Freshbooks > My Account > Freshbooks API), which was just inconvenient. I registered my app for Freshbooks OAuth use. Problem was, there wasn’t an great documentation on how to implement Freshbooks and OAuth calls. I tried authenticating with OAuth classes that were already built, but the problem was that Freshbooks expects an Authorization header and not Authorization posted to the body, as far as I could see. So, I read a blog entry on Formstack’s blog and started writing a Freshbooks API with OAuth class. I was able to get Authentication working and grab an access token, etc. but a problem lied where I couldn’t figure out how to send requests to Freshbooks API with an OAuth header. I contacted Michael Mattax ( @mmattax ) via email and he helped me out a little with a cURL function to send requests to Freshbooks API.

So, first off, after expanding the file available for download you’ll see and “index.php” and a folder “src” which contains: “freshbooks.php” and “config.php”.

** Updated October 1st, 2010

Download Source Code

DemoI’m not going to put a demo up, just incase Freshbooks decides to turn on API Rate Limits; Although an example is provided in the source code.


Config.php

Config.php simply defines your Consumer Key, Consumer Secret, and Callback for after they have authorized their account.

define(‘OAUTH_CONSUMER_KEY’, ‘xxxxxx’);
define(‘OAUTH_CONSUMER_SECRET’, ‘aBunchOfRandomLettersAndNumbers’);
define(‘OAUTH_CALLBACK’, ‘http://yoursite.com/freshbooks_callback.php’);

After defining everything in the “config.php” file, you can move on to the “index.php”

I start by starting a session and including the “config.php” and “freshbooks.php” from the “src” folder.

session_start();
require_once(‘src/config.php’);
require_once(‘src/freshbooks.php’);

Then I do a series of if statements.
The first is to check if a $_SESSION[‘oauth_token’] and $_SESSION[‘oauth_token_secret’] exist. (If they do then we can create a connection to the Freshbooks class like so:)

if(isset($_SESSION[‘oauth_token’]) && isset($_SESSION[‘oauth_token_secret’]))
{
$c = new Freshbooks(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, NULL, $_SESSION[‘subdomain’], $_SESSION[‘oauth_token’], $_SESSION[‘oauth_token_secret’]);
}

If there isn’t a $_SESSION[‘oauth_token’] and $_SESSION[‘oauth_token_secret’] then we can check to see if there is a $_GET[‘oauth_token’] and $_GET[‘oauth_verifier’] in the url (this meaning it’s the callback):

else if(isset($_GET[‘oauth_token’]) && isset($_GET[‘oauth_verifier’]))
{
$c = new Freshbooks(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, NULL, $_SESSION[‘subdomain’]);
$access_token = $c->getAccessToken($_GET[‘oauth_token’], $_GET[‘oauth_verifier’]);

$_SESSION[‘oauth_token’] = $access_token[‘oauth_token’];
$_SESSION[‘oauth_token_secret’] = $access_token[‘oauth_token_secret’];

header(“Location: index.php”);
}

We set the $_SESSION[‘oauth_token’] and $_SESSION[‘oauth_token_secret’] then redirect to the “index.php” so that now there are session vars we were talking about in the previous block of code, thus creating a connection to the Freshbooks class and then we’d be able to make calls to the API.

If there is no $_SESSION[‘oauth_token’] or $_SESSION[‘oauth_token_secret’] or $_GET’s as stated above, then no we’re going to need to check if they provided us their subdomain (i.e. http://example.freshbooks.com; “example” would be their subdomain.) if they did, we’ll move on to this chunk code that simply displays a “Login to Freshbooks” link which a user clicks, they put in their username and password then they’ll be redirected to your OAuth Callback URL set in your config (which in this case is handled on the index.php) They send you back 2 parameters in the URL “oauth_token” and “oauth_verifier”; so… the previous block of code will be triggered!

else if(isset($_POST[‘subdomain’]))
{
$_SESSION[‘subdomain’] = $_POST[‘subdomain’];
$c = new Freshbooks(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, OAUTH_CALLBACK, $_SESSION[‘subdomain’]);
echo ‘<a href=”‘.$c->getLoginUrl().'”>Login with Freshbooks!</a>’;
}

If there is no $_SESSION[‘oauth_token’] or $_SESSION[‘oauth_token_secret’] or $_GET’s or subdomain posted, we’ll give them a form where they put in their subdomain:

else
{
echo ‘<form action=”#” method=”POST”>
http://<input type=”text” name=”subdomain” />.freshbooks.com
</form>’;
}

When all is said and done and the user has authenticated with Freshbooks and you get $_SESSION vars and you make a connection with the Freshbooks class that I wrote, you’ll be able to make calls to the API.

i.e.

if(isset($_SESSION[‘oauth_token’]) && isset($_SESSION[‘oauth_token_secret’]))
{
$request = ‘<?xml version=”1.0″ encoding=”utf-8″?><request method=”client.list”><page>1</page><per_page>15</per_page></request>’;
try {
$clients = $c->post($request);
} catch(FreshbooksError $e) {
$error = $e->getMessage();
}
}

You can do a print_r() on $clients and you’ll see the Simple XML object that is returned. If something goes wrong, I have a check in my class for an error or a “status” => fail which will be caught in an Exception and return an error message from Freshbooks.

** Updated October 1st, 2010

NOTE: The update as of October 1st, 2010 changed this Freshbooks OAuth class to Version 2.0. Version 1.0 does not support multiuser usage.

CHANGELOG

Some links to check out:

http://developers.freshbooks.com/ — Freshbooks API Reference

Download Source Code

* Any questions, please drop a comment or email mikeh@ydekproductions.com

13 Comments

  1. Cool! Thanks for the shout-out; I’m glad it’s working!

  2. Hi Michael,

    Thanks for the great lib. I am working on converting your lib to Codeignitor.

    Found one little bug.

    In the createNonce() function I had to add a minus 1, to not get warnings

    $nonce .= $characters[mt_rand(0, strlen($characters)-1)];

    Thanks for publishing this lib.

    ~Spicer

  3. Awesome! And thanks! I’ve been meaning to get around to doing that, actually. Just haven’t found time. Also, I think you should know I’ll be making a edit to the class. Right now, it only allows for access to one account (that being, the consumer key you have in the config.php). When I edit the class, you’ll then need to have the user prompt to input their Freshbooks domain (i.e. http://example.freshbooks.com; “example” being their domain. That will
    Be then the handle in the API calls to authenticate, etc.

  4. Nice! Appreciate the credit.
    A suggestion, in the current version of your code, I’d let them know that it only works with one user as of right now by setting the consumer key. I’ll try and get something up this weekend!

  5. Hey Mike,

    I added multiple user support. Meaning you can select which account you want to access. Via the $this->freshbooks_oauth->set_namespace() call.

    For production at http://www.skyledger.com we store the access keys in a database instead of sessions.

  6. I don’t see anything called OAUTH_CONSUMER_KEY or OAUTH_CONSUMER_SECRET. Freshbooks provided me with an Authentication Token and an OAuth secret. Could you clarify which is which?

    • You’ll want to login to your Freshbooks account, then click “My Account” at the top. Then click “Freshbooks API”.

      On that page, you’ll see “Authentication Token” which is your OAUTH_CONSUMER_KEY and then right below it you’ll see a section “OAuth Developer Access” which you’ll need to check the “Use OAuth” option then “OAuth Secret” will appear and that is your OAUTH_CONSUMER_SECRET

      • Mike, you words like: ‘ you’ll see “Authentication Token” which is your OAUTH_CONSUMER_KEY’ not correct.

        OAUTH_CONSUMER_KEY is you subdomain.

  7. hai i am planning to get contacts from fresh books can you please provide me the code in C#.net

    • Sorry, I do not program in C#

      • Bob Bob

        Downloaded the code, uploaded it to my server, registered for OAuth and updated config but getting “INVALID REQUEST TOKEN” on authentication attempt. Any ideas why?

Leave a Reply

Your email address will not be published. Required fields are marked *