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):
- Log into Pennylane with an admin account.
- Go to Settings > Connectivity > Developers.
- Create a token: give it a name, pick the permissions (read only, or read and write) and an expiration date.
- 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¶
- Authentication: token scopes, OAuth, good practices.
- Pagination and filtering: cursors, filters, sorting.
- Errors, retries and rate limits: exception hierarchy, retry policy, throttling.