PHPackages                             amcintosh/freshbooks - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. amcintosh/freshbooks

ActiveLibrary[API Development](/categories/api)

amcintosh/freshbooks
====================

FreshBooks API wrapper

0.9.0(4mo ago)852.1k↑23.4%10[17 issues](https://github.com/amcintosh/freshbooks-php-sdk/issues)MITPHPPHP &gt;=8.0 &lt;8.6CI passing

Since Nov 20Pushed 2mo ago2 watchersCompare

[ Source](https://github.com/amcintosh/freshbooks-php-sdk)[ Packagist](https://packagist.org/packages/amcintosh/freshbooks)[ GitHub Sponsors](https://github.com/amcintosh)[ RSS](/packages/amcintosh-freshbooks/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (8)Dependencies (13)Versions (18)Used By (0)

FreshBooks PHP SDK
==================

[](#freshbooks-php-sdk)

[![Packagist Version](https://camo.githubusercontent.com/b81845bb9888c309bce3caaaebc7a584e1f6736bb16a0a4742d15623946a0dab/68747470733a2f2f62616467656e2e6e65742f7061636b61676973742f762f616d63696e746f73682f6672657368626f6f6b73)](https://packagist.org/packages/amcintosh/freshbooks)[![Packagist PHP Version Support](https://camo.githubusercontent.com/1f1e78cb6027c2dc8e9462c6ae7f1614516b53f1c7173f0b67604903cbe1b82d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f616d63696e746f73682f6672657368626f6f6b73)](https://camo.githubusercontent.com/1f1e78cb6027c2dc8e9462c6ae7f1614516b53f1c7173f0b67604903cbe1b82d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f616d63696e746f73682f6672657368626f6f6b73)[![GitHub Workflow Status](https://camo.githubusercontent.com/a524743adc09c58dcea62af0c50af715139dbea15c545c43d75de383059a0aa4/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f616d63696e746f73682f6672657368626f6f6b732d7068702d73646b2f72756e2d74657374732e796d6c3f6272616e63683d6d61696e)](https://github.com/amcintosh/freshbooks-php-sdk/actions?query=workflow%3A%22Run+Tests%22)

A FreshBooks PHP SDK to allow you to more easily utilize the [FreshBooks API](https://www.freshbooks.com/api). This library is not directly maintained by FreshBooks and [community contributions](CONTRIBUTING.md) are welcome.

Installation
------------

[](#installation)

Install it via Composer.

```
composer require amcintosh/freshbooks
```

Requires a [PSR-18 implementation](https://packagist.org/providers/psr/http-client-implementation) client. If you do not already have a compatible client, you can install one with it.

```
composer require amcintosh/freshbooks php-http/guzzle7-adapter
```

Usage
-----

[](#usage)

See the [full documentation](https://amcintosh.github.io/freshbooks-php-sdk/) or check out some [examples](examples).

This SDK makes use of the [spryker/decimal-object](https://packagist.org/packages/spryker/decimal-object) package. All monetary amounts are represented as as `Spryker\DecimalObject\Decimal`, so it is recommended that you refer to [their documentation](https://github.com/spryker/decimal-object/tree/master/docs).

```
use Spryker\DecimalObject\Decimal;

$this->assertEquals(Decimal::create('41.94'), $invoice->amount->amount);
```

### Configuring the API client

[](#configuring-the-api-client)

You can create an instance of the API client in one of two ways:

- By providing your application's OAuth2 `clientId` and `clientSecret` and following through the auth flow, which when complete will return an access token.
- Or if you already have a valid access token, you can instantiate the client with that token, however token refresh flows will not function without the application id and secret.

```
use amcintosh\FreshBooks\FreshBooksClient;
use amcintosh\FreshBooks\FreshBooksClientConfig;

$conf = new FreshBooksClientConfig(
    clientSecret: 'your secret',
    redirectUri: 'https://some-redirect',
);

$freshBooksClient = new FreshBooksClient('your application id', $conf);
```

and then proceed with the auth flow (see below).

Or

```
use amcintosh\FreshBooks\FreshBooksClient;
use amcintosh\FreshBooks\FreshBooksClientConfig;

$conf = new FreshBooksClientConfig(
    accessToken: 'a valid token',
);

$freshBooksClient = new FreshBooksClient('your application id', $conf);
```

#### Authoization flow

[](#authoization-flow)

*This is a brief summary of the OAuth2 authorization flow and the methods in the FreshBooks API Client around them. See the [FreshBooks API - Authentication](https://www.freshbooks.com/api/authentication) documentation.*

First, instantiate your Client with `clientId`, `clientSecret`, and `redirectUri` as above.

To get an access token, the user must first authorize your application. This can be done by sending the user to the FreshBooks authorization page. Once the user has clicked accept there, they will be redirected to your `redirectUri` with an access grant code. The authorization URL can be obtained by calling `$freshBooksClient->getAuthRequestUri()`. This method also accepts a list of scopes that you wish the user to authorize your application for.

```
$authUrl = $freshBooksClient->getAuthRequestUri(['user:profile:read', 'user:clients:read']);
```

Once the user has been redirected to your `redirectUri` and you have obtained the access grant code, you can exchange that code for a valid access token.

```
$authResults = $freshBooksClient->getAccessToken($accessGrantCode);
```

This call both sets the `accessToken`, `refreshToken`, and `tokenExpiresAt` fields on you Client's FreshBooksClientConfig instance and returns those values.

```
echo $authResults->accessToken;  // Your token
echo $authResults->refreshToken; // Your refresh token
echo $authResults->createdAt;    // When the token was created (as a DateTime)
echo $authResults->expiresIn;    // How long the token is valid for (in seconds)
echo $authResults->getExpiresAt; // When the token expires (as a DateTime)

echo $freshBooksClient->getConfig()->accessToken;    // Your token
echo $freshBooksClient->getConfig()->refreshToken;   // Your refresh token
echo $freshBooksClient->getConfig()->tokenExpiresAt; // When the token expires (as a DateTime)
```

When the token expires, it can be refreshed with the `refreshToken` value in the FreshBooksClient:

```
$authResults = $freshBooksClient->refreshAccessToken();
echo $authResults->accessToken;  // Your new token
```

or you can pass the refresh token yourself:

```
$authResults = $freshBooksClient->refreshAccessToken($storedRefreshToken);
echo $authResults->accessToken;  // Your new token
```

### Current User

[](#current-user)

FreshBooks users are uniquely identified by their email across the entire product. One user may act on several Businesses in different ways, and the Identity model is how to keep track of it. Each unique user has an Identity, and each Identity has Business Memberships which define the permissions they have.

See [FreshBooks API - Business, Roles, and Identity](https://www.freshbooks.com/api/me_endpoint) and [FreshBooks API - The Identity Model](https://www.freshbooks.com/api/identity_model).

The current user can be accessed by:

```
$identity = $freshBooksClient->currentUser();
echo $identity->email // prints the current user's email

// Print name and role of each business the user is a member of
foreach ($identity->businessMemberships as $businessMembership) {
    echo $businessMembership->business.name;
    echo $businessMembership->role; // eg. owner
}
```

### Making API Calls

[](#making-api-calls)

Each resource in the client has provides calls for `get`, `list`, `create`, `update` and `delete` calls. Please note that some API resources are scoped to a FreshBooks `accountId` while others are scoped to a `businessId`. In general these fall along the lines of accounting resources vs projects/time tracking resources, but that is not precise.

```
$client = $freshBooksClient->clients()->get($accountId, $clientId);
$project = $freshBooksClient->projects()->get($businessId, $projectId);
```

#### Get and List

[](#get-and-list)

Get calls return a single `DataModel` class with data accessible via properties.

```
$client = $freshBooksClient->clients()->get($accountId, $clientId);

echo $client->organization; // 'FreshBooks'
$client->only('organization')->toArray(); // ['organization' => 'FreshBooks'];
```

`visState` numbers correspond with various states. See [FreshBooks API - Active and Deleted Objects](https://www.freshbooks.com/api/active_deleted)for details.

```
use amcintosh\FreshBooks\Model\VisState;

echo $client->visState; // '0'
echo $client->visState == VisState::ACTIVE ? 'Is Active' : 'Not Active'; // 'Is Active'
```

List calls return a class containing containing an array of the resource's `DataModel` class.

```
$clients = $freshBooksClient->clients()->list($accountId);

echo $clients->clients[0]->organization; // 'FreshBooks'

foreach ($clients->clients as $client) {
    echo $client->organization;
}
```

#### Create, Update, and Delete

[](#create-update-and-delete)

API calls to create and update take either a `DataModel` object, or an array of the resource data. A successful call will return a `DataModel` class as if a `get` call.

*Note*: When using the array of data, you need to specify the field as it exists in the FreshBooks API. There are API fields that are translated to more intuitive names in the data models. For example `fname` = `firstName`, or `bus_phone` = `businessPhone`.

Create:

```
$clientData = new Client();
$clientData->organization = 'FreshBooks';
$clientData->firstName = 'Gordon';
$clientData->businessPhone = '416-444-4445';

$newClient = $freshBooksClient->clients()->create($accountId, model: $clientData);

echo $newClient->organization;  // 'FreshBooks'
echo $newClient->firstName;     // 'Gordon'
echo $newClient->businessPhone; // '416-444-4445'
```

or

```
$clientData = array(
    'organization' => 'FreshBooks',
    'fname' => 'Gordon',
    'bus_phone' => '416-444-4445'
);

$newClient = $freshBooksClient->clients()->create($accountId, data: $clientData);

echo $newClient->organization;  // 'FreshBooks'
echo $newClient->firstName;     // 'Gordon'
echo $newClient->businessPhone; // '416-444-4445'
```

Update:

```
$clientData->organization = 'New Org';
$clientData->firstName = 'Gord';

$newClient = $freshBooksClient->clients()->update($accountId, $clientData->id, model: $clientData);

echo $newClient->organization; // 'New Org'
echo $newClient->firstName;    // 'Gord'
```

or

```
$clientData = array(
    'organization' => 'Really New Org',
    'fname' => 'Gord',
);

$newClient = $freshBooksClient->clients()->update($accountId, $clientId, data: $clientData);

echo $newClient->organization; // 'Really New Org'
echo $newClient->firstName;    // 'Gord'
```

Delete:

```
$client = $freshBooksClient->clients()->delete($accountId, $clientId);

echo $client->visState; // '1'
echo $client->visState == VisState::ACTIVE ? 'Is Active' : 'Not Active'; // 'Not Active'
```

#### Error Handling

[](#error-handling)

Calls made to the FreshBooks API with a non-2xx response are wrapped in a `FreshBooksException`. This exception class contains the error message, HTTP response code, FreshBooks-specific error number if one exists, and the HTTP response body.

Example:

```
use amcintosh\FreshBooks\Exception\FreshBooksException;

try {
    $client = $freshBooksClient->clients()->get($accountId, 134);
} catch (FreshBooksException $e) {
    echo $e->getMessage();     // 'Client not found'
    echo $e->getCode();        // 404
    echo $e->getErrorCode();   // 1012
    echo $e->getRawResponse(); // '{"response": {"errors": [{"errno": 1012,
                               // "field": "userid", "message": "Client not found.",
                               // "object": "client", "value": "134"}]}}'
}
```

TODO: this Not all resources have full CRUD methods available. For example expense categories have `list` and `get`calls, but are not deletable. If you attempt to call a method that does not exist, the SDK will raise a `FreshBooksNotImplementedError` exception, but this is not something you will likely have to account for outside of development.

#### Pagination, Filters, and Includes

[](#pagination-filters-and-includes)

`list` calls take a list of builder objects that can be used to paginate, filter, and include optional data in the response. See [FreshBooks API - Parameters](https://www.freshbooks.com/api/parameters) documentation.

##### Pagination

[](#pagination)

Pagination results are included in `list` responses:

```
$clients = $freshBooksClient->clients()->list($accountId);

echo $clients->pages()->page    // 1
echo $clients->pages()->pages   // 1
echo $clients->pages()->perPage // 30
echo $clients->pages()->total   // 6
```

To make a paginated call, first create a `PaginateBuilder` that can be passed into the `list` method.

```
use amcintosh\FreshBooks\Builder\PaginateBuilder;

$paginator = new PaginateBuilder(2, 4);

$clients = $freshBooksClient->clients()->list($accountId, builders: [$paginator]);

echo $clients->pages()->page    // 2
echo $clients->pages()->pages   // 2
echo $clients->pages()->perPage // 4
echo $clients->pages()->total   // 6
```

`PaginateBuilder` has chainable methods `page` and `perPage` to set the values.

```
$paginator = new PaginateBuilder(1, 3);
echo $paginator->page;    // 1
echo $paginator->perPage; // 3

$paginator->page(2)->perPage(4);
echo $paginator->page;    // 2
echo $paginator->perPage; // 4
```

##### Filters

[](#filters)

To filter which results are return by `list` method calls, construct a `FilterBuilder` and pass that in the list of builders to the `list` method.

```
use amcintosh\FreshBooks\Builder\FilterBuilder;

$filters = new FilterBuilder();
$filters->equals('userid', 123);

$clients = $freshBooksClient->clients()->list($accountId, builders: [$filters]);
```

Filters can be built with the methods: `equals`, `inList`, `like`, `between`, `boolean`, and `datetime`which can be chained together.

Please see [FreshBooks API - Active and Deleted Objects](https://www.freshbooks.com/api/active_deleted)for details on filtering active, archived, and deleted resources.

```
$filters = new FilterBuilder();
$filters->inList('clientids', [123, 456]);
// Creates `&search[clientids][]=123&search[clientids][]=456`

$filters = new FilterBuilder();
$filters->like('email_like', '@freshbooks.com');
// Creates `&search[email_like]=@freshbooks.com`

$filters = new FilterBuilder();
$filters->between('amount', 1, 10);
// Creates `&search[amount_min]=1&search[amount_max]=10`

$filters = new FilterBuilder();
$filters->between('amount', min=15); // For just minimum
// Creates `&search[amount_min]=15`

$filters = new FilterBuilder();
$filters->between('amount_min', 15); // Alternatively
// Creates `&search[amount_min]=15`

$filters = new FilterBuilder();
$filters->between("start_date", min: new DateTime('2020-10-17'))
// Creates `&search[start_date]=2020-10-17`

$filters = new FilterBuilder();
$filters->boolean('complete', false); // Boolean filters are mostly used on Project-like resources
// Creates `&complete=false`

$filters = new FilterBuilder();
$filters->equals('vis_state', VisState::ACTIVE)->between('updated', new DateTime('2020-10-17'), new DateTime('2020-11-21'));
// Chaining filters
// Creates `&search[vis_state]=0&search[updated_min]=2020-10-17&search[updated_max]=2020-11-21`
```

##### Includes

[](#includes)

To include additional relationships, sub-resources, or data in a response an `IncludesBuilder`can be constructed.

```
use amcintosh\FreshBooks\Builder\IncludesBuilder;

$includes = new IncludesBuilder();
$includes->include("outstanding_balance");
```

Which can then be passed into `list` or `get` calls:

```
$clients = $freshBooksClient->clients()->list($accountId, builders: [$includes]);
echo $clients->clients[0]->outstanding_balance->amount; // '100.00'
echo $clients->clients[0]->outstanding_balance->code; // 'USD'

$client = $freshBooksClient->clients()->get($accountId, $clientId, $includes);
echo $client->outstanding_balance->amount; // '100.00'
echo $client->outstanding_balance->code; // 'USD'
```

Includes can also be passed into `create` and `update` calls to include the data in the response of the updated resource:

```
$clientData = array(
    'email' => 'john.doe@abcorp.com'
);

$newClient = $freshBooksClient->clients()->create($accountId, data: $clientData);

echo $client->outstanding_balance->amount; // null, new client has no balance
```

##### Sorting

[](#sorting)

To sort the results of a list call by supported fields (see the documentation for that resource) a `SortBuilder` can be used.

```
use amcintosh\FreshBooks\Builder\SortBuilder;

$sort = new SortBuilder();
$sort->ascending("invoice_date");

$invoices = $freshBooksClient->invoices()->list($accountId, builders: [$sort]);
```

to sort by the invoice date in ascending order, or:

```
use amcintosh\FreshBooks\Builder\SortBuilder;

$sort = new SortBuilder();
$sort->descending("invoice_date");

$invoices = $freshBooksClient->invoices()->list($accountId, builders: [$sort]);
```

for descending order.

Development
-----------

[](#development)

### Testing

[](#testing)

To run all tests:

```
make test
```

Run a specific test:

```
 ./vendor/bin/phpunit --filter testCreateValidationError
```

### Documentations

[](#documentations)

You can generate the documentation via:

```
make generate-docs
```

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance61

Regular maintenance activity

Popularity39

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 56.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~188 days

Recently: every ~333 days

Total

9

Last Release

131d ago

PHP version history (5 changes)0.1.0PHP ^8.0

0.3.0PHP &gt;=8.0 &lt;8.2

0.6.0PHP &gt;=8.0 &lt;8.3

0.8.0PHP &gt;=8.0 &lt;8.4

0.9.0PHP &gt;=8.0 &lt;8.6

### Community

Maintainers

![](https://www.gravatar.com/avatar/8a27b1a0048c8cd34ba83a6c43d88fd60d518c75be6320a6237dea0c76a90b4d?d=identicon)[amcintosh](/maintainers/amcintosh)

---

Top Contributors

[![amcintosh](https://avatars.githubusercontent.com/u/779525?v=4)](https://github.com/amcintosh "amcintosh (62 commits)")[![actions-user](https://avatars.githubusercontent.com/u/65916846?v=4)](https://github.com/actions-user "actions-user (43 commits)")[![dominikb](https://avatars.githubusercontent.com/u/28777818?v=4)](https://github.com/dominikb "dominikb (2 commits)")[![devkumar2313](https://avatars.githubusercontent.com/u/72178142?v=4)](https://github.com/devkumar2313 "devkumar2313 (1 commits)")[![stickler-ci](https://avatars.githubusercontent.com/u/16011037?v=4)](https://github.com/stickler-ci "stickler-ci (1 commits)")

---

Tags

freshbooksfreshbooks-apihacktoberfestphp-sdksdk

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/amcintosh-freshbooks/health.svg)

```
[![Health](https://phpackages.com/badges/amcintosh-freshbooks/health.svg)](https://phpackages.com/packages/amcintosh-freshbooks)
```

###  Alternatives

[openai-php/client

OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API

5.8k22.6M232](/packages/openai-php-client)[mailgun/mailgun-php

The Mailgun SDK provides methods for all API functions.

1.1k28.9M168](/packages/mailgun-mailgun-php)[getbrevo/brevo-php

Official Brevo provided RESTFul API V3 php library

963.1M35](/packages/getbrevo-brevo-php)[theodo-group/llphant

LLPhant is a library to help you build Generative AI applications.

1.5k311.5k5](/packages/theodo-group-llphant)[deeplcom/deepl-php

Official DeepL API Client Library

2616.2M66](/packages/deeplcom-deepl-php)[jolicode/slack-php-api

An up to date PHP client for Slack's API

2534.4M12](/packages/jolicode-slack-php-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
