PHPackages                             kurusa/fortnox-php-client - 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. kurusa/fortnox-php-client

ActiveLibrary[API Development](/categories/api)

kurusa/fortnox-php-client
=========================

PHP client for the Fortnox API with OAuth2 support.

00PHP

Since May 28Pushed 1w agoCompare

[ Source](https://github.com/Kurusa/fortnox-php-client)[ Packagist](https://packagist.org/packages/kurusa/fortnox-php-client)[ RSS](/packages/kurusa-fortnox-php-client/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

Fortnox PHP Client
==================

[](#fortnox-php-client)

PHP client for the Fortnox API with OAuth2 support.

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

[](#installation)

```
composer require kurusa/fortnox-php-client
```

Token store
-----------

[](#token-store)

The client does not decide where OAuth tokens are stored. Implement `TokenStoreInterface` in your application.

```
use Kurusa\Fortnox\Contracts\TokenStoreInterface;
use Kurusa\Fortnox\ValueObjects\OAuthToken;

final class ArrayTokenStore implements TokenStoreInterface
{
    private ?OAuthToken $token = null;

    public function get(): OAuthToken
    {
        return $this->token;
    }

    public function save(OAuthToken $token): void
    {
        $this->token = $token;
    }
}
```

Basic setup
-----------

[](#basic-setup)

```
use Kurusa\Fortnox\Config\FortnoxConfig;
use Kurusa\Fortnox\Fortnox;

$tokenStore = new ArrayTokenStore();

$config = new FortnoxConfig(
    clientId: config('fortnox.client_id'),
    clientSecret: config('fortnox.client_secret'),
    redirectUri: config('fortnox.redirect_uri'),
);

$fortnox = new Fortnox(
    config: $config,
    tokenStore: $tokenStore,
);
```

OAuth flow
----------

[](#oauth-flow)

### Authorization URL

[](#authorization-url)

```
use Kurusa\Fortnox\Enums\AccessType;
use Kurusa\Fortnox\Enums\Scope;

$authorizationUrl = $fortnox
    ->oauth()
    ->authorizationUrl(
        scopes: [
            Scope::Article,
            Scope::CompanyInformation,
            Scope::Customer,
            Scope::Invoice,
            Scope::Order,
        ],
        state: bin2hex(random_bytes(16)),
        accessType: AccessType::Offline,
    );

echo (string) $authorizationUrl;
```

The user should be redirected to the generated URL. After approval, Fortnox redirects back to your configured redirect URI with a `code` and `state`.

### Exchange authorization code for token

[](#exchange-authorization-code-for-token)

```
$token = $fortnox
    ->oauth()
    ->exchangeCode($_GET['code']);

$tokenStore->save($token);
```

The returned object is an `OAuthToken`:

```
$token->accessToken;
$token->refreshToken;
$token->expiresIn;
$token->scope;
```

### Refresh token

[](#refresh-token)

```
$currentToken = $tokenStore->get();

$newToken = $fortnox
    ->oauth()
    ->refresh($currentToken->refreshToken);

$tokenStore->save($newToken);
```

When refreshing a Fortnox token, store the newly returned refresh token. The old refresh token should not be reused.

API usage
---------

[](#api-usage)

API methods return `FortnoxResponse`.

```
$response->statusCode;
$response->data;
$response->headers;
$response->successful();
```

Example:

```
$response = $fortnox->customers()->list();

if ($response->successful()) {
    print_r($response->data);
}
```

Customers
---------

[](#customers)

```
use Kurusa\Fortnox\Data\Customers\CreateCustomerData;
use Kurusa\Fortnox\Data\Customers\UpdateCustomerData;

$response = $fortnox->customers()->list();

$response = $fortnox->customers()->getByNumber('1');

$response = $fortnox->customers()->create(new CreateCustomerData(
    name: 'Example Customer',
    email: 'customer@example.test',
    phone: '+37200000000',
    address1: 'Example Street 1',
    zipCode: '12345',
    city: 'Tallinn',
    country: 'Estonia',
));

$response = $fortnox->customers()->update('1', new UpdateCustomerData(
    email: 'new-email@example.test',
    phone: '+37211111111',
));

$response = $fortnox->customers()->deleteByNumber('1');
```

Articles
--------

[](#articles)

```
use Kurusa\Fortnox\Data\Articles\CreateArticleData;
use Kurusa\Fortnox\Data\Articles\UpdateArticleData;

$response = $fortnox->articles()->list();

$response = $fortnox->articles()->getByNumber('SKU-001');

$response = $fortnox->articles()->create(new CreateArticleData(
    description: 'Example article',
    articleNumber: 'SKU-001',
    unit: 'pcs',
    salesPrice: 10.50,
    active: true,
));

$response = $fortnox->articles()->update('SKU-001', new UpdateArticleData(
    description: 'Updated article',
    salesPrice: 12.00,
));
```

Orders
------

[](#orders)

```
use Kurusa\Fortnox\Data\Orders\CreateOrderData;
use Kurusa\Fortnox\Data\Orders\OrderRowData;

$response = $fortnox->orders()->list();

$response = $fortnox->orders()->getByDocumentNumber('1001');

$response = $fortnox->orders()->create(new CreateOrderData(
    customerNumber: '1',
    rows: [
        new OrderRowData(
            articleNumber: 'SKU-001',
            deliveredQuantity: 2,
            price: 10.50,
        ),
    ],
));
```

Invoices
--------

[](#invoices)

```
use Kurusa\Fortnox\Data\Invoices\CreateInvoiceData;
use Kurusa\Fortnox\Data\Invoices\InvoiceRowData;

$response = $fortnox->invoices()->list();

$response = $fortnox->invoices()->getByDocumentNumber('1001');

$response = $fortnox->invoices()->create(new CreateInvoiceData(
    customerNumber: '1',
    rows: [
        new InvoiceRowData(
            articleNumber: 'SKU-001',
            deliveredQuantity: 2,
            price: 10.50,
        ),
    ],
));

$response = $fortnox->invoices()->bookkeep('1001');
```

Company information
-------------------

[](#company-information)

```
$response = $fortnox->companyInformation()->get();
```

Errors
------

[](#errors)

API errors throw `FortnoxApiException`.

```
use Kurusa\Fortnox\Exceptions\FortnoxApiException;

try {
    $response = $fortnox->customers()->list();
} catch (FortnoxApiException $exception) {
    $statusCode = $exception->statusCode;
    $response = $exception->response;
}
```

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance64

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

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

### Community

Maintainers

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

---

Top Contributors

[![Kurusa](https://avatars.githubusercontent.com/u/51157689?v=4)](https://github.com/Kurusa "Kurusa (13 commits)")

### Embed Badge

![Health badge](/badges/kurusa-fortnox-php-client/health.svg)

```
[![Health](https://phpackages.com/badges/kurusa-fortnox-php-client/health.svg)](https://phpackages.com/packages/kurusa-fortnox-php-client)
```

###  Alternatives

[facebook/php-business-sdk

PHP SDK for Facebook Business

90923.5M35](/packages/facebook-php-business-sdk)[exsyst/swagger

A php library to manipulate Swagger specifications

35916.3M7](/packages/exsyst-swagger)[hubspot/api-client

Hubspot API client

24015.5M18](/packages/hubspot-api-client)[botman/driver-telegram

Telegram driver for BotMan

93452.6k6](/packages/botman-driver-telegram)

PHPackages © 2026

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