PHPackages                             tor2r/laravel-poweroffice-api - 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. tor2r/laravel-poweroffice-api

ActiveLibrary[API Development](/categories/api)

tor2r/laravel-poweroffice-api
=============================

A Laravel client library for communicating with the PowerOffice Go REST API using OAuth 2.0 Client Credentials.

v1.0.0(1mo ago)015↓80%[2 PRs](https://github.com/Tor2r/laravel-poweroffice-api/pulls)MITPHPPHP ^8.2CI passing

Since Apr 20Pushed 1mo agoCompare

[ Source](https://github.com/Tor2r/laravel-poweroffice-api)[ Packagist](https://packagist.org/packages/tor2r/laravel-poweroffice-api)[ Docs](https://github.com/tor2r/laravel-poweroffice-api)[ GitHub Sponsors](https://github.com/Tor2r)[ RSS](/packages/tor2r-laravel-poweroffice-api/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (12)Versions (4)Used By (0)

Laravel PowerOffice API
=======================

[](#laravel-poweroffice-api)

[![Latest Version on Packagist](https://camo.githubusercontent.com/0dbf0068b8101057fed47b6b102fae3c7d82fdf39e4ef6e554d436def626a0ca/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f746f7232722f6c61726176656c2d706f7765726f66666963652d6170692e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/tor2r/laravel-poweroffice-api)[![GitHub Tests Action Status](https://camo.githubusercontent.com/5259b2b8a75c3d7d83c74557d112525f696fdec0341c5971f383504126c34cd2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f746f7232722f6c61726176656c2d706f7765726f66666963652d6170692f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/tor2r/laravel-poweroffice-api/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/567ceefd26d62ee38aa712b16ecc7fc6e0937fff1bf3eb55f0f1fcfe9c8a6987/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f746f7232722f6c61726176656c2d706f7765726f66666963652d6170692f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/tor2r/laravel-poweroffice-api/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/70703ced48e2bd3d6a00a47e413fa5e8ac2bf1ba9262186f236e9ae0171c9764/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f746f7232722f6c61726176656c2d706f7765726f66666963652d6170692e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/tor2r/laravel-poweroffice-api)

A Laravel client library for communicating with the [PowerOffice Go REST API](https://developer.poweroffice.net/) using OAuth 2.0 Client Credentials. Provides a clean, resource-based interface with automatic token caching, retry logic, and comprehensive error handling.

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

[](#installation)

You can install the package via composer:

```
composer require tor2r/laravel-poweroffice-api
```

You can publish the config file with:

```
php artisan vendor:publish --tag="poweroffice-api-config"
```

Configuration
-------------

[](#configuration)

Set these environment variables in your `.env`:

```
POWEROFFICE_ENVIRONMENT=demo          # "demo" or "production"
POWEROFFICE_APP_KEY=your-app-key
POWEROFFICE_CLIENT_KEY=your-client-key
POWEROFFICE_SUBSCRIPTION_KEY=your-subscription-key
```

Tokens are cached for 15 minutes (configurable via `POWEROFFICE_TOKEN_TTL`). The API token expires after 20 minutes -- the 5-minute buffer prevents using an about-to-expire token.

This is the contents of the published config file:

```
return [
    'environment' => env('POWEROFFICE_ENVIRONMENT', 'demo'),

    'app_key' => env('POWEROFFICE_APP_KEY'),
    'client_key' => env('POWEROFFICE_CLIENT_KEY'),
    'subscription_key' => env('POWEROFFICE_SUBSCRIPTION_KEY'),

    'environments' => [
        'production' => [
            'base_url' => 'https://goapi.poweroffice.net/v2',
            'token_url' => 'https://goapi.poweroffice.net/OAuth/Token',
        ],
        'demo' => [
            'base_url' => 'https://goapi.poweroffice.net/Demo/OAuth/Token',
            'token_url' => 'https://goapi.poweroffice.net/Demo/OAuth/Token',
        ],
    ],

    'token_ttl' => env('POWEROFFICE_TOKEN_TTL', 900),
];
```

Usage
-----

[](#usage)

Access everything through the `PowerOfficeApi` facade:

```
use Tor2r\PowerOfficeApi\Facades\PowerOfficeApi;
```

Or inject the client directly:

```
use Tor2r\PowerOfficeApi\PowerOfficeClient;

public function __construct(private PowerOfficeClient $powerOffice) {}
```

Available Methods
-----------------

[](#available-methods)

### Client Methods

[](#client-methods)

MethodDescription`authenticate(): string`Authenticate and return a fresh access token`getAccessToken(): string`Get cached token (authenticates if needed)`flushToken(): void`Clear the cached token`get(string $endpoint, array $query = []): array`Send a GET request`post(string $endpoint, array $data = []): array`Send a POST request`patch(string $endpoint, array $data = []): array`Send a PATCH request### Resources

[](#resources)

MethodDescription`customers(): CustomerResource`Get the customers resource`products(): ProductResource`Get the products resource`projects(): ProjectResource`Get the projects resource`salesOrders(): SalesOrderResource`Get the sales orders resource### [CustomerResource](https://prdm0go0stor0apiv20eurw.z6.web.core.windows.net/?urls.primaryName=Customers)

[](#customerresource)

MethodDescription`get(int $id): array`Get a single customer`getByOrgNr(int $id): array`Get customers by organization nr. OBS! Not unique.`list(array $filters = []): array`List customers with optional filters`create(array $data): array`Create a customer`update(int $id, array $operations): array`Update a customer (JSON Patch RFC 6902 operations)### [ProductResource](https://prdm0go0stor0apiv20eurw.z6.web.core.windows.net/?urls.primaryName=Products%20and%20Product%20Groups)

[](#productresource)

MethodDescription`get(int $id): array`Get a single product`list(array $filters = []): array`List products with optional filters`create(array $data): array`Create a product`update(int $id, array $operations): array`Update a product (JSON Patch RFC 6902 operations)### [ProjectResource](https://prdm0go0stor0apiv20eurw.z6.web.core.windows.net/?urls.primaryName=Projects)

[](#projectresource)

MethodDescription`get(int $id): array`Get a single project`list(array $filters = []): array`List projects with optional filters`create(array $data): array`Create a project`update(int $id, array $operations): array`Update a project (JSON Patch RFC 6902 operations)### [SalesOrderResource](https://prdm0go0stor0apiv20eurw.z6.web.core.windows.net/?urls.primaryName=Sales%20Orders)

[](#salesorderresource)

MethodDescription`get(string $id): array`Get a single sales order (UUID)`getComplete(string $id): array`Get a complete sales order with order lines (UUID)`list(array $filters = []): array`List sales orders with optional filters`create(array $data): array`Create a sales orderExamples
--------

[](#examples)

Each resource links to the corresponding PowerOffice API documentation for a complete list of parameter / attribute names.

### Customers

[](#customers)

```
// Get a single customer
$customer = PowerOfficeApi::customers()->get(12345);

// List customers with filters
$customers = PowerOfficeApi::customers()->list([
    'lastChangedDateTimeOffsetGreaterThan' => '2025-01-01T00:00:00+00:00',
]);

// Create a customer
$customer = PowerOfficeApi::customers()->create([
    'Name' => 'Acme Corp',
    'OrganizationNumber' => '912345678',
    'EmailAddress' => 'invoice@acme.no',
    'MailAddress' => [
        'AddressLine1' => 'Storgata 1',
        'City' => 'Oslo',
        'ZipCode' => '0150',
        'CountryCode' => 'NO',
    ],
]);

// Update a customer (JSON Patch RFC 6902)
$customer = PowerOfficeApi::customers()->update(12345, [
    ['op' => 'replace', 'path' => '/Name', 'value' => 'Acme Corp AS'],
    ['op' => 'replace', 'path' => '/EmailAddress', 'value' => 'new-invoice@acme.no'],
]);
```

### Products

[](#products)

```
// Get a single product
$product = PowerOfficeApi::products()->get(100);

// List all products
$products = PowerOfficeApi::products()->list();

// Create a product
$product = PowerOfficeApi::products()->create([
    'Name' => 'Consulting Hour',
    'Description' => 'Standard consulting rate',
    'UnitPrice' => 1500.00,
    'UnitOfMeasureCode' => 'HUR',
]);

// Update a product (JSON Patch RFC 6902)
$product = PowerOfficeApi::products()->update(100, [
    ['op' => 'replace', 'path' => '/UnitPrice', 'value' => 1750.00],
]);
```

### Projects

[](#projects)

```
// Get a single project
$project = PowerOfficeApi::projects()->get(300);

// List active projects
$projects = PowerOfficeApi::projects()->list([
    'status' => 'Active',
    'excludeArchivedProject' => true,
]);

// Create a project
$project = PowerOfficeApi::projects()->create([
    'Name' => 'Website Redesign',
    'Code' => 'WEB-001',
    'CustomerId' => 12345,
    'StartDate' => '2025-06-01',
    'EndDate' => '2025-12-31',
    'ProjectBillingMethod' => 'TimeAndExpenses',
    'IsBillable' => true,
    'BillableRate' => 1500.00,
    'BudgetedHours' => 200,
]);

// Update a project (JSON Patch RFC 6902)
$project = PowerOfficeApi::projects()->update(300, [
    ['op' => 'replace', 'path' => '/Name', 'value' => 'Website Redesign v2'],
    ['op' => 'replace', 'path' => '/BudgetedHours', 'value' => 250],
]);
```

### Sales Orders

[](#sales-orders)

```
// Get a single sales order (UUID)
$order = PowerOfficeApi::salesOrders()->get('a1b2c3d4-e5f6-7890-abcd-ef1234567890');

// Get a complete sales order with order lines (UUID)
$order = PowerOfficeApi::salesOrders()->getComplete('a1b2c3d4-e5f6-7890-abcd-ef1234567890');

// List sales orders
$orders = PowerOfficeApi::salesOrders()->list([
    'customerNos' => '12345',
]);

// Create a sales order
$order = PowerOfficeApi::salesOrders()->create([
    'CustomerId' => 12345,
    'SalesOrderDate' => '2025-06-01',
    'SalesOrderLines' => [
        [
            'ProductId' => 100,
            'Quantity' => 10,
            'UnitPrice' => 1500.00,
            'Description' => 'Consulting Hours - June',
        ],
        [
            'ProductId' => 200,
            'Quantity' => 1,
            'UnitPrice' => 5000.00,
            'Description' => 'Project setup fee',
        ],
    ],
]);
```

Error Handling
--------------

[](#error-handling)

The client throws specific exceptions based on the API response code:

ExceptionStatus CodeDescription`PowerOfficeValidationException`400, 422Bad request or validation errors`PowerOfficeAuthException`401, 403Unauthorized or forbidden`PowerOfficeNotFoundException`404Resource not found`PowerOfficeConflictException`409Resource in use, cannot be deleted`PowerOfficeApiException`429, 5xxRate limit exceeded or server error`PowerOfficeNotFoundException` and `PowerOfficeConflictException` extend `PowerOfficeApiException`, so you can catch the base class for any API error.

A `204 No Content` response (e.g. successful delete) returns an empty array.

```
use Tor2r\PowerOfficeApi\Exceptions\PowerOfficeApiException;
use Tor2r\PowerOfficeApi\Exceptions\PowerOfficeAuthException;
use Tor2r\PowerOfficeApi\Exceptions\PowerOfficeConflictException;
use Tor2r\PowerOfficeApi\Exceptions\PowerOfficeNotFoundException;
use Tor2r\PowerOfficeApi\Exceptions\PowerOfficeValidationException;

try {
    $customer = PowerOfficeApi::customers()->create($data);
} catch (PowerOfficeValidationException $e) {
    // 400 / 422 -- bad request or validation errors
    $e->errors;   // ['name' => ['Name is required']]
    $e->response; // Illuminate\Http\Client\Response
} catch (PowerOfficeAuthException $e) {
    // 401 / 403 -- unauthorized or forbidden
    $e->context;  // ['body' => '...']
} catch (PowerOfficeNotFoundException $e) {
    // 404 -- resource does not exist
    $e->response; // Illuminate\Http\Client\Response
} catch (PowerOfficeConflictException $e) {
    // 409 -- resource is in use, cannot be deleted
    $e->response; // Illuminate\Http\Client\Response
} catch (PowerOfficeApiException $e) {
    // 429 / 5xx -- rate limit or server error (base class for all API errors)
    $e->response; // Illuminate\Http\Client\Response
    $e->getCode(); // HTTP status code
}
```

Retry Behavior
--------------

[](#retry-behavior)

Requests automatically retry up to 3 times with exponential backoff (500ms, 1000ms, 1500ms) on:

- **401** -- flushes the cached token, re-authenticates, then retries
- **429** -- rate limited, waits and retries
- **5xx** -- server errors, waits and retries

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Credits
-------

[](#credits)

- [Tor L](https://github.com/Tor2r)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance92

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 92.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

Unknown

Total

1

Last Release

50d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/d9415ff224abe8a2149633c6916e03ca1f709e15d0e4266531a8ee34f515caa1?d=identicon)[tor2r](/maintainers/tor2r)

---

Top Contributors

[![Tor2r](https://avatars.githubusercontent.com/u/34723140?v=4)](https://github.com/Tor2r "Tor2r (13 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

laravelTor2rlaravel-poweroffice-api

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/tor2r-laravel-poweroffice-api/health.svg)

```
[![Health](https://phpackages.com/badges/tor2r-laravel-poweroffice-api/health.svg)](https://phpackages.com/packages/tor2r-laravel-poweroffice-api)
```

###  Alternatives

[dedoc/scramble

Automatic generation of API documentation for Laravel applications.

2.1k11.2M113](/packages/dedoc-scramble)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M48](/packages/spatie-laravel-pdf)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

813336.8k3](/packages/defstudio-telegraph)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)[lettermint/lettermint-laravel

Official Lettermint driver for Laravel

1190.2k1](/packages/lettermint-lettermint-laravel)

PHPackages © 2026

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