Skip to content

Getting started

From zero to your first Pennylane API calls in a few minutes.

1. Install the package

composer require goatandcow7/pennylane-sdk

PHP 8.2 or newer is required. The SDK talks HTTP through PSR-18; if no PSR-18 client and PSR-17 factories are already in your project, add Guzzle:

composer require guzzlehttp/guzzle

and allow the discovery plugin once in composer.json:

"config": {
    "allow-plugins": {
        "php-http/discovery": true
    }
}

2. Get an API token

The SDK talks to two different Pennylane APIs, each with its own token:

Company API (you manage one company's accounting and invoicing):

  1. Log into Pennylane with an admin account.
  2. Go to Settings > Connectivity > Developers.
  3. Create a token: give it a name, pick the permissions (read only, or read and write) and an expiration date.
  4. Copy it immediately: Pennylane shows it only once.

Firm API (you are an accounting firm working across client companies): generate a Firm token from your firm account settings.

3. Configure the token

The recommended way is an environment variable, so the token never lands in your code:

export PENNYLANE_API_TOKEN="your-token"        # Company API
export PENNYLANE_FIRM_API_TOKEN="your-token"   # Firm API
<?php

use Pennylane\Sdk\Pennylane;

$client = new Pennylane(); // picks up PENNYLANE_API_TOKEN

You can also pass it explicitly, for instance when one process handles several companies:

$client = new Pennylane(apiToken: 'your-token');

4. First calls

Retrieve the identity tied to the current token:

$me = $client->me->retrieve();
print_r($me);

If the token is valid you get your company profile back. If not, the SDK throws Pennylane\Sdk\Exception\AuthenticationException.

List invoices:

foreach ($client->customerInvoices->list(limit: 20) as $invoice) {
    echo $invoice->invoiceNumber . ' ' . $invoice->currencyAmount . PHP_EOL;
}

Create a product:

use Pennylane\Sdk\Exception\ValidationException;

try {
    $product = $client->products->create(
        label: 'Consulting day',
        priceBeforeTax: '450.00',
        vatRate: 'FR_200',
    );
    echo $product->id . PHP_EOL;
} catch (ValidationException $e) {
    echo $e->getMessage() . PHP_EOL;
}

Amounts are always passed as numeric strings ('450.00'), never as PHP floats: floats would lose decimal precision on accounting data, and the SDK rejects them.

5. Error handling basics

Every exception the SDK throws derives from Pennylane\Sdk\Exception\PennylaneException, so catching it is enough to handle any SDK failure in one place:

use Pennylane\Sdk\Exception\PennylaneException;

try {
    $client->customerInvoices->finalize($invoiceId);
} catch (PennylaneException $e) {
    echo $e->getMessage() . PHP_EOL;
}

For finer grained handling, catch the specific subclass instead (AuthenticationException, PermissionDeniedException, ValidationException, RateLimitException...). See the errors and retries guide for the full hierarchy and the retry policy.

6. Explore the resources

The client exposes one property per API resource, in camelCase. A few examples:

Property What it manages
$client->customerInvoices Sales invoices and credit notes: create, finalize, send, import PDFs and e-invoices
$client->quotes Quotes, and turning them into invoices
$client->customers Customers, with ->companies (B2B) and ->individuals (B2C)
$client->products Product catalog
$client->supplierInvoices Purchase invoices, imports, payment status
$client->transactions Bank transactions, matching with invoices
$client->ledgerEntries Accounting entries
$client->ledgerEntryLines Entry lines, lettering (lettrage)
$client->trialBalance Trial balance (balance générale)
$client->exports FEC, general ledger and analytical exports
$client->changelogs Change feeds to sync data incrementally

The full list is in the design contract.

7. Client options

$client = new Pennylane(
    apiToken: null,        // default: PENNYLANE_API_TOKEN env var
    baseUrl: null,         // override for proxies
    httpClient: null,      // bring your own PSR-18 client
    requestFactory: null,  // bring your own PSR-17 request factory
    streamFactory: null,   // bring your own PSR-17 stream factory
    autoThrottle: true,    // pace requests under the API rate limit
    maxRetries: 3,         // automatic retries (429, transient network errors)
    timeout: 60.0,         // seconds, needs Guzzle or an injected client
);

Where to go next