Skip to content
× FreshBooks App Logo
Official App
Free - Google Play
Get it
You're currently on our US site. Select your regional site here:


Industry Standard

The new FreshBooks uses OAuth2 for authentication. OAuth2 is an authorization framework that enables applications to obtain limited access to user accounts over HTTP, and is used by services like Google, Facebook, Stripe, and Slack. This method creates a token that lasts for 12 hours to keep your account secure and connected. For more details about how OAuth2 itself, check out: the official documentation.

Use a Library!

If you’re a developer of a web application using Python, Ruby, or Javascript, it’s recommended that you use a popular library written in your language of choice to manage the details of connecting via OAuth2. Such libraries typically provide configuration options for you to include your client_id, client_secret, and redirect_uri, then allow you to make higher level calls in your program to initiate or refresh authorization.

If you wish to manage that complexity yourself, you can request a Bearer and Refresh token pair from our auth/token endpoint using the process described in the code pane on this page.

Basic Flow

  1. A user visits your Authorization link, which you supply on your website, and which we supply clickable versions of on your app management page.
  2. They are sent to an Authorization page we host.
  3. The user logs in, sees the Scopes your App will have access to and clicks ‘Authorize’.
  4. They are redirected back to your website with a code parameter in the URL.
  5. You send us a request from your server containing the code, your client id, client secret, and the redirect uri the user followed, as seen in the code pane on this page.
  6. We return to you a Bearer and Refresh token pair.
  7. You make successful calls on that user’s behalf. Congrats!

Redirect URI Limitations

For security reasons, redirect URIs must specify HTTPS as their protocol. While testing/developing your application, if you are unable to set up a self signed certificate to use or to work in a secured public test environment, you may manually change the URL to HTTP in your browser to complete the connection.

Redirect URIs also cannot contain query string parameters. Your application may instead url-encode arbitrary data and pass into an Authorize link via an additional state parameter. That state parameter and its value will be inserted into the Redirect URI when the client is sent there after granting permission to your application to continue.

Multiple Redirect URIs are allowed per application, specified on separate lines of the Redirect URI field on the developer page.

Refresh Tokens and Token Lifespans

Bearer Tokens are not long lived. They last for 12 hours, after which they are no longer able to authenticate requests, and may produce different errors depending upon the service being called. The remedy to use in these situations is a Refresh Token.

Refresh Tokens live forever, but are one-time-use, and only one Refresh Token can be alive at any time per user per application. A new Refresh Token is generated every time a Bearer Token is issued for a given user of a given application, and all old Refresh Tokens immediately become invalid. Make sure that whenever you receive a new Bearer Token, you write its companion Refresh token down somewhere safe to refresh your access, or you’ll have to re-authorize your application again manually.

Bearer Tokens don’t interfere with each other’s lives the same way, so you could have several valid Bearer Tokens at any given point in time. They only expire when you manually POST to the token/revoke endpoint or when their time runs out.

Making a Refresh Token Call

Make a Refresh call by following the same template shown in the code pane on this page, but supply a grant type of ‘refresh_token’ instead of ‘authorization’, and instead of a ‘code’ argument, supply a ‘refresh_token’ argument with the most recent Refresh Token you were granted.

Authorization URL

Authorization URLs for your FreshBooks integration will look like the following:<REDIRECT_URL>&client_id=<CLIENT_ID>

Ask us!

OAuth can get pretty complicated. If you’ve given this all a try and read as much documentation as you can find and you’re still running into problems, shoot us an email with information about what you’re doing, what you tried, and what went wrong, and we’ll do our best to help.

Good luck!

Authorizing with OAuth2

Request to

curl -X POST
  -H "Content-Type: application/json"
  -d ` {
  "grant_type": "authorization_code",//we do not support client_credentials grant type
  "client_secret": "LOTS_OF_LETTERS_AND_NUMBERS",
  "redirect_uri": ""
} `


   "access_token": "lots_of_letters_and_numbers",
   "token_type": "bearer",
   "expires_in": 43200,
   "refresh_token": "same_as_the_bearer_token",
   "created_at": 1586211696

Authorizing with OAuth2 (Using Library)

Request: POST

oauth2_session = OAuth2Session('<client_id>', redirect_url="https://localhost:3000/example")
authorization_url, state = oauth2_session.authorization_url('')
session['oauth_state'] = state

From the callback (https://localhost:3000/example)

freshbooks = OAuth2Session('<client_id>', token=session['oauth_state'],
  'Api-Version': 'alpha',
  'User-Agent': 'FreshBooks API (python) 1.0.0',
  'Content-Type': 'application/json'
token = freshbooks.fetch_token('',
client_secret='<client_secret>', authorization_response=request.url)


  "access_token": "lots_of_letters_and_numbers",
  "token_type": "bearer",
  "expires_in": 43200,
  "refresh_token": "same_as_the_bearer_token",
  "created_at": 1424471407

Authorizing with OAuth2 (requests)


url = ""
headers = {'Api-Version': 'alpha', 'Content-Type': 'application/json'}
payload = {'grant_type': 'authorization_code', 'client_secret': '<client_secret>','code': '<Authorization code>', 'client_id': '<client_id>', 'redirect_uri': 'https://localhost:3000/example'}
res =, data=json.dumps(payload), headers=headers)


  "access_token": "lots_of_letters_and_numbers",
  "token_type": "bearer",
  "expires_in": 43200,
  "refresh_token": "same_as_the_bearer_token",
  "created_at": 1424471407

Revoking a Refresh Token


url = ""
headers = {'Api-Version': 'alpha', 'Content-Type': 'application/json',
'Authorization': 'Bearer <client_id>'}
payload = {
   'client_id': '<insert your client id>',
   'client_secret': '<insert your secret>',
   'token': '<insert your refresh token>'

Authorizing with OAuth2

Request to


$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "",
    "grant_type": "authorization_code",
    "client_secret": "<insert your secret>",
    "code": "<insert your authorization code>",
    "client_id": "<insert your client id>",
    "redirect_uri": "https://localhost:3000/just_an_example"
    "api-version: alpha",
    "cache-control: no-cache",
    "content-type: application/json"

$response = curl_exec($curl);
$err = curl_error($curl);


if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;

$response should be:

  "access_token" => "lots_of_letters_and_numbers",
  "token_type" => "bearer",
  "expires_in" => 43200,
  "refresh_token" => "same_as_the_bearer_token",
  "created_at" => 1472471407