Freshh for Craft CMS

Installation

Requirements

  • Craft CMS 5.3 or later
  • Craft Commerce 5.0 or later
  • PHP 8.2 or later
  • A FreshBooks account you can create an app in

Freshh has no runtime dependencies beyond Craft's own. There is no build step and no asset bundle.

Install

composer require justinholtweb/craft-freshh
php craft plugin/install freshh

Or find Freshh in the Craft Plugin Store and install it from there.

Create a FreshBooks app

FreshBooks supports the OAuth2 authorization code grant and nothing else — there is no machine-to-machine flow — so the connection is approved once, by a human, in a browser.

  1. Go to my.freshbooks.com/#/developer and create an app.
  2. Open Settings → Plugins → Freshh in the Craft control panel and copy the Redirect URI shown there into the FreshBooks app.

    FreshBooks rejects a redirect URI that is not HTTPS, or that carries a query string. That is why Freshh publishes a plain control-panel path rather than a Craft action URL — action URLs come out as ?p=admin/actions/… on any install without omitScriptNameInUrls, which FreshBooks will not accept.

  3. Copy the app's Client ID and Client Secret back into Freshh's settings and save.

Keep the secret in an environment variable rather than in project config:

# .env
FRESHBOOKS_CLIENT_ID="…"
FRESHBOOKS_CLIENT_SECRET="…"

Then enter $FRESHBOOKS_CLIENT_ID and $FRESHBOOKS_CLIENT_SECRET in the settings fields.

Connect

Press Connect to FreshBooks and approve the connection. Freshh asks only for the scopes it uses — profile, clients, invoices, payments, credit notes, taxes, items and events — so a merchant reading the consent screen can see it never asks for payroll or banking.

If the account has more than one business, Freshh selects the first and the settings screen lets you change it. Reconnecting later keeps whichever business is already chosen, so a reconnect can never quietly move your invoices into a different set of books.

Check it before you trust it

Two buttons on the settings screen are worth pressing before any real order syncs:

  • Test connection makes a real API call and reports what came back.
  • Preview the latest order builds the payload for your most recent completed order and shows it, along with the total FreshBooks would arrive at next to the total the customer actually paid.

The preview runs the same code the queue job runs, so what you are shown is byte-for-byte what FreshBooks receives.

Start cautiously

Leave Sync automatically off at first. Send one order by hand from its order screen, look at the result in FreshBooks, then turn automatic syncing on.

For a store with existing orders, backfill from the console rather than the browser:

php craft freshh/sync/push --dry-run --limit=20   # no connection needed; nothing is sent
php craft freshh/sync/push --limit=500

The dry run is the one thing worth doing before connecting: it shows how your tax, discounts and shipping map, and whether every invoice balances.