Search by

e-invoice / e-invoice-api

e-invoice

e-invoice.be Peppol API

Package info

github.com/e-invoice-be/e-invoice-php

pkg:composer/e-invoice/e-invoice-api

Statistics

Installs: 2 140

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 5

v0.7.2 2026-10-11 19:16 UTC

README

The PHP SDK page on e-invoice.be

This library gives access to the e-invoice.be Peppol API from any PHP 8.1+ application. With it you can create, validate, send and receive electronic invoices and credit notes on the Peppol network.

e-invoice.be maintains this library. All the code is written and maintained by hand.

To get an API key, make a free account and register your company.

Documentation

Installation

Install the package with Composer:

composer require e-invoice/e-invoice-api

The library uses PSR-18 to send requests and PSR-17 to build them. It finds the installed implementations automatically. If your application has no PSR-18 HTTP client, install one, for example:

composer require symfony/http-client nyholm/psr7

Usage

<?php

use EInvoiceAPI\Client;

$client = new Client(apiKey: getenv('E_INVOICE_API_KEY'));

$document = $client->documents->create(
  documentType: 'INVOICE',
  invoiceID: 'INV-2026-001',
  invoiceDate: '2026-10-11',
  dueDate: '2026-11-10',
  currency: 'EUR',
  vendorName: 'My Company BV',
  vendorTaxID: 'BE0123456789',
  customerName: 'Customer NV',
  customerTaxID: 'BE0987654321',
  customerPeppolID: '0208:0987654321',
  items: [
    [
      'description' => 'Consulting',
      'quantity' => 2,
      'unitPrice' => 100,
      'taxRate' => '21.00',
    ],
  ],
);

var_dump($document->id);

// Send the document on the Peppol network
$client->documents->send($document->id);

Configuration

The constructor of the client has two optional parameters. Each one has an environment variable as fallback.

Parameter Environment variable Default
apiKey E_INVOICE_API_KEY none
baseUrl E_INVOICE_BASE_URL https://api.e-invoice.be
<?php

use EInvoiceAPI\Client;

// Reads E_INVOICE_API_KEY and E_INVOICE_BASE_URL from the environment
$client = new Client;

The client sends the API key as a bearer token in the Authorization header.

Parameters

The methods use named parameters. All optional parameters have a default value, thus you set only the ones that you need.

The parameter names are in camelCase (customerPeppolID). The library converts them to the snake_case names of the REST API (customer_peppol_id). The keys of nested arrays, such as the entries of items, are also in camelCase.

For parameters with a fixed set of values you can give a string or the related enum:

<?php

use EInvoiceAPI\Documents\DocumentType;

$client->documents->create(documentType: DocumentType::CREDIT_NOTE);

Value objects

Some nested parameters accept a value object as an alternative to an array. Use the static with constructor with named parameters to make a value object:

<?php

use EInvoiceAPI\Documents\DocumentAttachmentCreate;

$attachment = DocumentAttachmentCreate::with(
  fileName: 'timesheet.pdf',
  fileData: base64_encode(file_get_contents('timesheet.pdf')),
  fileType: 'application/pdf',
);

$client->documents->create(attachments: [$attachment]);

Builders are also available: (new DocumentAttachmentCreate)->withFileName('timesheet.pdf').

Resources

The client has one property for each resource of the API. Together they cover the 40 endpoints of the API.

Property Methods
$client->documents create, retrieve, delete, createFromPdf, retrieveTimeline, send, validate
$client->documents->attachments retrieve, list, delete, add
$client->documents->ubl createFromUbl, get
$client->inbox list, listInvoices, listCreditNotes
$client->outbox listDraftDocuments, listReceivedDocuments
$client->drafts list
$client->mailbox retrieve, list, downloadAttachment, reprocess
$client->validate validateJson, validatePeppolID, validateUbl
$client->lookup retrieve, retrieveParticipants
$client->conversion createFromPdf, retrieve, retrieveResult
$client->me retrieve
$client->stats retrieve
$client->webhooks create, retrieve, update, list, delete, retrieveHistory, test

Some examples:

<?php

// Make sure that the receiver is on the Peppol network
$check = $client->validate->validatePeppolID(peppolID: '0208:0987654321');
var_dump($check->isValid);

// Search for Peppol participants
$participants = $client->lookup->retrieveParticipants(query: 'e-invoice', countryCode: 'BE');

// Validate a document before you send it
$validation = $client->documents->validate($document->id);
var_dump($validation->isValid, $validation->issues);

// Get a notification when a document arrives
$webhook = $client->webhooks->create(
  events: ['document.received'],
  url: 'https://example.com/webhooks/e-invoice',
);

// Read the details of your account
$me = $client->me->retrieve();
var_dump($me->name, $me->peppolIDs);

File uploads

The methods that upload a file accept the contents of the file as a string, or a EInvoiceAPI\Core\FileParam. Use FileParam to set the file name and the content type:

<?php

use EInvoiceAPI\Core\FileParam;

// From a string
$result = $client->documents->createFromPdf(
  file: FileParam::fromString(
    file_get_contents('invoice.pdf'),
    filename: 'invoice.pdf',
    contentType: 'application/pdf',
  ),
);

// From a resource. The file name comes from the path of the resource.
$document = $client->documents->ubl->createFromUbl(
  file: FileParam::fromResource(fopen('invoice.xml', 'r'), contentType: 'application/xml'),
);

The library reads a resource from its current position and does not close it.

Pagination

The list methods of inbox, outbox, drafts and mailbox are paginated. They return a EInvoiceAPI\DocumentsNumberPage, which can get the subsequent pages for you:

<?php

use EInvoiceAPI\Client;

$client = new Client(apiKey: getenv('E_INVOICE_API_KEY'));

$page = $client->inbox->list(pageSize: 50);

// The items of the current page
foreach ($page->getItems() as $item) {
  var_dump($item->id);
}

// The items of the current page and all subsequent pages.
// The library makes the necessary requests.
foreach ($page->pagingEachItem() as $item) {
  var_dump($item->id);
}

// Or get the pages one at a time
if ($page->hasNextPage()) {
  $page = $page->getNextPage();
}

A page also has the properties page, pageSize, pages and total.

Handling errors

When the library cannot connect to the API, or when the API returns a status code that is not a success (4xx or 5xx), the library throws a subclass of EInvoiceAPI\Core\Exceptions\APIException:

<?php

use EInvoiceAPI\Core\Exceptions\APIConnectionException;
use EInvoiceAPI\Core\Exceptions\APIStatusException;
use EInvoiceAPI\Core\Exceptions\APITimeoutException;
use EInvoiceAPI\Core\Exceptions\RateLimitException;

try {
  $document = $client->documents->retrieve('doc_123');
} catch (APITimeoutException $e) {
  echo "The request timed out", PHP_EOL;
} catch (APIConnectionException $e) {
  echo "The server could not be reached", PHP_EOL;
  var_dump($e->getPrevious());
} catch (RateLimitException $e) {
  echo "A 429 status code was received; we should back off a bit.", PHP_EOL;
} catch (APIStatusException $e) {
  echo "Another non-200-range status code was received", PHP_EOL;
  echo $e->getMessage();
}

The error types are as follows:

Cause Error Type
HTTP 400 BadRequestException
HTTP 401 AuthenticationException
HTTP 403 PermissionDeniedException
HTTP 404 NotFoundException
HTTP 409 ConflictException
HTTP 422 UnprocessableEntityException
HTTP 429 RateLimitException
HTTP >= 500 InternalServerException
Other HTTP error APIStatusException
Timeout APITimeoutException
Network error APIConnectionException

APITimeoutException is a subclass of APIConnectionException, so catch it first.

Retries

The library automatically retries some errors 2 times by default. If the response has a Retry-After header with a number of seconds, the library waits that time before the retry.

The library retries these status codes: 408 Request Timeout, 409 Conflict, 429 Rate Limit and all codes >= 500. It does not retry connection errors and timeouts.

Use the maxRetries option to change or disable the retries for a request:

<?php

use EInvoiceAPI\RequestOptions;

$document = $client->documents->retrieve(
  'doc_123',
  requestOptions: RequestOptions::with(maxRetries: 5),
);

Advanced concepts

Raw responses

Each resource has a raw property with the same methods. These methods return the PSR-7 response, thus you can read the status code and the headers. Call parse() to get the typed result:

<?php

$response = $client->documents->raw->retrieve('doc_123');

var_dump($response->getStatusCode());
var_dump($response->getHeaderLine('Content-Type'));

$document = $response->parse();

Using a different HTTP client

By default the library uses the PSR-18 client and the PSR-17 factories that php-http/discovery finds. To use a different PSR-18 client for a request, for example one with your timeout or proxy configuration, give it as the transporter option:

<?php

use EInvoiceAPI\RequestOptions;
use Symfony\Component\HttpClient\Psr18Client;

$me = $client->me->retrieve(
  requestOptions: RequestOptions::with(transporter: new Psr18Client),
);

Making custom or undocumented requests

Undocumented parameters

You can send undocumented query parameters and headers to any endpoint with the extraQueryParams and extraHeaders request options:

<?php

use EInvoiceAPI\RequestOptions;

$document = $client->documents->create(
  requestOptions: RequestOptions::with(
    extraQueryParams: ['my_query_parameter' => 'value'],
    extraHeaders: ['my-header' => 'value'],
  ),
);

Note: an extra header overrides the header of the same name that the library sends.

Undocumented endpoints

To make a request to an undocumented endpoint, use $client->request. The request keeps the authentication, the retries and the error handling of the library:

<?php

$response = $client->request(
  method: 'post',
  path: '/undocumented/endpoint',
  query: ['dog' => 'woof'],
  headers: ['useful-header' => 'interesting-value'],
  body: ['hello' => 'world'],
);

var_dump($response->getStatusCode());

Versioning

This package follows SemVer conventions. While the major version is 0, a breaking change increases the minor version. See the changelog for the changes in each version.

This package considers improvements to the (non-runtime) PHPDoc type definitions to be non-breaking changes.

Requirements

PHP 8.1 or higher, and an implementation of PSR-18 (HTTP client) and PSR-17 (HTTP factories).

Contributing

See the contributing documentation. To report a security problem, see the security policy.