Documentation for 4.0 (pre-release). Using 3.x? See the README on main.
Skip to content

Clients (Kunden) ​

API wrapper for the Billomat "clients" resource, covering the endpoints under /clients.

Access ​

php
$billomat->clients
$billomat->clients->tags            // tags on a client, see client-tags.md
$billomat->clients->properties      // custom property definitions, see properties.md
$billomat->clients->propertyValues  // custom property values on a client

Justpilot\Billomat\Api\ClientsApi, created lazily by BillomatClient.

Endpoint overview ​

MethodHTTPPath
list($filters?)GET/clients
listPage($filters?)GET/clients (one page with metadata)
iterateAll($filters?, $pageSize?)GET/clients (all pages)
get($id) / getOrFail($id)GET/clients/{id}
getMyself()GET/clients/myself
create($options)POST/clients
update($id, $options)PUT/clients/{id}
delete($id)DELETE/clients/{id}
avatar($id, $size?)GET/clients/{id}/avatar

Methods ​

list(ClientFilter|array $filters = []): list<Client> ​

Lists clients, optionally filtered.

php
public function list(ClientFilter|array $filters = []): array

Use the typed filter ClientFilter (every field is an optional named argument, plus a with*() wither per field). A plain array of Billomat parameters still works, e.g. for parameters the filter doesn't cover; array values are serialized as key[]=….

php
use Justpilot\Billomat\Api\ClientFilter;
use Justpilot\Billomat\Api\OrderBy;

$clients = $billomat->clients->list(new ClientFilter(
    orderBy: OrderBy::asc('name'),
    perPage: 50,
));

foreach ($clients as $client) {
    echo $client->id . ': ' . $client->name . PHP_EOL;
}
Billomat filterClientFilter field
namename
client_numberclientNumber
emailemail
first_name / last_namefirstName / lastName
country_codecountryCode
notenote
invoice_idinvoiceId (list, sent comma-separated)
tagstags (list, sent comma-separated)
order_byorderBy (OrderBy::asc('name')->thenDesc('id'))
page / per_pagepage / perPage

Other query parameters (e.g. filters on custom properties) go into extra: [...]; they override fields with the same name.

Exceptions: AuthenticationException, HttpException.

listPage(ClientFilter|array $filters = []): Page<Client> ​

Same as list(), but also returns the pagination metadata (@page, @per_page, @total) from the response envelope. See Pagination.

iterateAll(ClientFilter|array $filters = [], int $pageSize = 100): Generator<int, Client> ​

Lazily walks through all pages and yields one client at a time. page and per_page in the filter are overwritten.

php
foreach ($billomat->clients->iterateAll(new ClientFilter(countryCode: 'DE')) as $client) {
    // …
}

get(int $id): ?Client ​

Fetches a client by ID. Returns null when Billomat responds with 404.

php
$client = $billomat->clients->get(12345);

if ($client === null) {
    throw new RuntimeException('Client 12345 does not exist.');
}

Exceptions: AuthenticationException, HttpException (no NotFoundException).

getOrFail(int $id): Client ​

Like get(), but throws NotFoundException instead of returning null.

create(ClientCreateOptions $options): Client ​

Creates a client. The payload is sent wrapped as { "client": { … } }.

php
use Justpilot\Billomat\Api\ClientCreateOptions;

$opts = new ClientCreateOptions(
    name: 'Acme GmbH',
    firstName: 'Max',
    lastName: 'Mustermann',
    email: 'max@acme.example',
    countryCode: 'DE',
);

$client = $billomat->clients->create($opts);
echo $client->id;

Exceptions: ValidationException (required field missing, invalid value), AuthenticationException, HttpException.

update(int $id, ClientUpdateOptions $options): Client ​

Partial update of an existing client. Only fields set in ClientUpdateOptions are sent to Billomat; all other fields stay unchanged.

Billomat restriction: an archived client can't be updated. Set archived: false first if necessary.

php
use Justpilot\Billomat\Api\ClientUpdateOptions;

$opts = new ClientUpdateOptions(name: 'Acme GmbH & Co. KG', email: 'invoices@acme.example');

$updated = $billomat->clients->update(12345, $opts);

Exceptions: ValidationException, AuthenticationException, NotFoundException, HttpException.

delete(int $id): void ​

Deletes a client. Clients that already have documents (invoices, offers, …) can't be deleted, only archived; Billomat then responds with 400/422 and the SDK throws a ValidationException.

getMyself(): Client ​

Returns your own account profile as a Client. Useful for health checks ("are my credentials valid?") and for reading your own master data.

php
$me = $billomat->clients->getMyself();
echo 'Logged in as: ' . $me->name;

Exceptions: AuthenticationException, HttpException.

avatar(int $id, ?int $size = null): string ​

Downloads the client's avatar. Billomat returns the image itself (usually PNG), not JSON; the method returns the raw body. $size is the optional edge length in pixels.

php
file_put_contents('/tmp/avatar.png', $billomat->clients->avatar(12345, size: 128));

Write model: ClientCreateOptions ​

Every field is a constructor parameter with a default and is passed as a named argument. The object is immutable (final readonly); each field has a with*() method that returns a modified copy (e.g. $opts->withName('New')). toArray() drops null values so Billomat can fill in defaults from the account settings.

Master data ​

PropertyBillomat fieldTypeDescription
namename?stringCompany name (required for companies according to the Billomat docs).
archivedarchived?booltrue = archived, false = active.
numberPrenumber_pre?stringClient number prefix (default from settings).
numbernumber?intSequential client number (default: next free number).
numberLengthnumber_length?intMinimum length of the client number.
clientNumberclient_number?stringFree-form client number.

Address ​

PropertyBillomat fieldType
streetstreet?string
zipzip?string
citycity?string
statestate?string
countryCodecountry_code?string (ISO 3166-1 alpha-2, e.g. DE, AT, CH)

Contact person and contact details ​

PropertyBillomat fieldType
firstNamefirst_name?string
lastNamelast_name?string
salutationsalutation?string
emailemail?string
phonephone?string
faxfax?string
mobilemobile?string
wwwwww?string (URL without scheme)
notenote?string
localelocale?string (e.g. de_DE)

Tax and price basis ​

PropertyBillomat fieldType
taxNumbertax_number?string
vatNumbervat_number?string
taxRuletax_rule?TaxRule (TAX, NO_TAX, COUNTRY)
netGrossnet_gross?NetGross (NET, GROSS, SETTINGS)
currencyCodecurrency_code?string (ISO currency code)

Debtor account and price group ​

PropertyBillomat fieldType
debitorAccountNumberdebitor_account_number?int
priceGroupprice_group?int

Bank details ​

PropertyBillomat fieldType
bankAccountNumberbank_account_number?string
bankAccountOwnerbank_account_owner?string
bankNumberbank_number?string
bankNamebank_name?string
bankSwiftbank_swift?string
bankIbanbank_iban?string

SEPA ​

PropertyBillomat fieldType
sepaMandatesepa_mandate?string
sepaMandateDatesepa_mandate_date?string (YYYY-MM-DD)

Payment terms ​

PropertyBillomat fieldType
defaultPaymentTypesdefault_payment_typeslist<PaymentType> (sent comma-separated, e.g. CASH,BANK_TRANSFER)
reductionreduction?float (discount in %)
discountRateTypediscount_rate_type?ValueType (SETTINGS, ABSOLUTE, RELATIVE)
discountRatediscount_rate?float
discountDaysTypediscount_days_type?ValueType
discountDaysdiscount_days?float
dueDaysTypedue_days_type?ValueType
dueDaysdue_days?int
reminderDueDaysTypereminder_due_days_type?ValueType
reminderDueDaysreminder_due_days?int
offerValidityDaysTypeoffer_validity_days_type?ValueType
offerValidityDaysoffer_validity_days?int
dunningRundunning_run?bool (only sent when set; otherwise Billomat's default applies)
php
use Justpilot\Billomat\Model\Enum\PaymentType;
use Justpilot\Billomat\Model\Enum\TaxRule;
use Justpilot\Billomat\Model\Enum\ValueType;

$opts = new ClientCreateOptions(
    name: 'Acme GmbH',
    taxRule: TaxRule::COUNTRY,
    dueDaysType: ValueType::ABSOLUTE,
    dueDays: 14,
    defaultPaymentTypes: [PaymentType::BANK_TRANSFER, PaymentType::PAYPAL],
);

Write model: ClientUpdateOptions ​

Same fields as ClientCreateOptions, all optional. Only fields that are not null go into the PUT request. Clear::Field (Justpilot\Billomat\Api\Clear) sends a field as JSON null so Billomat empties it, e.g. new ClientUpdateOptions(note: Clear::Field). The boolean archived is serialized as 1/0.

PropertyBillomat fieldType
all fields of ClientCreateOptionssamesame type, additionally |Clear
defaultPaymentTypesdefault_payment_typeslist<PaymentType>|Clear (empty list = don't send)

Read model: Client ​

final readonly class Client. Properties:

PropertyTypeNotes
idintBillomat's internal ID. Always set; a response without it throws UnexpectedResponseException.
namestringRequired, not nullable.
created, updated?\DateTimeImmutable
clientNumber?string
number, numberLength?int
numberPre?string
street, zip, city, state, countryCode?string
address?stringFull address assembled by Billomat (read-only)
firstName, lastName, salutation?string
email, phone, fax, mobile, www?string
note, locale?string
taxNumber, vatNumber?string
taxRule?TaxRule
netGross?NetGross
currencyCode?string
debitorAccountNumber, priceGroup?int
archived, dunningRun, enableCustomerportal, digExclude?bool
reduction, discountRate, discountDays?float
dueDays, reminderDueDays, offerValidityDays?int
discountRateType, discountDaysType, dueDaysType, reminderDueDaysType, offerValidityDaysType?ValueType
bankAccountOwner, bankNumber, bankName, bankAccountNumber, bankSwift, bankIban?string
sepaMandate?string
sepaMandateDate?\DateTimeImmutable
defaultPaymentTypeslist<PaymentType>
revenueGross, revenueNet?MoneyRevenue with this client (read-only), see Money
facturxIdentifier, sellerIdentifier?stringE-invoicing (ZUGFeRD/Factur-X), see Pitfalls
customfield?stringFree-form extra field, see Custom meta attributes
extraarray<string, mixed>Fields Billomat returns that the SDK doesn't map (yet), see Models

Client::fromArray(array $data): self hydrates a model from the client node of the API response. toArray() renders the model back with Billomat field names (snake_case). It's meant for debugging and logging, not for update calls.

Client property values ​

Stores the values for the client properties defined in properties.md ($billomat->clients->properties). A property definition (ClientProperty) is the schema (name + PropertyType); a ClientPropertyValue is the actual value on one client.

MethodHTTPPath
list($clientId?, $filters?)GET/client-property-values
listPage($clientId?, $filters?)GET/client-property-values
iterateAll($clientId?, $filters?, $pageSize?)GET/client-property-values (all pages)
get($id) / getOrFail($id)GET/client-property-values/{id}
create($clientId, $options)POST/client-property-values
php
use Justpilot\Billomat\Api\ClientPropertyValueCreateOptions;
use Justpilot\Billomat\Api\ClientPropertyValueFilter;
use Justpilot\Billomat\Api\OrderBy;

// List the values of one client
$values = $billomat->clients->propertyValues->list($client->id);

// Only the values of property 12, typed filter with sorting
$values = $billomat->clients->propertyValues->list($client->id, new ClientPropertyValueFilter(
    clientPropertyId: 12,
    orderBy: OrderBy::desc('updated'),
));

// Set the value of the property "Industry" (id = 12)
$value = $billomat->clients->propertyValues->create(
    $client->id,
    new ClientPropertyValueCreateOptions(
        clientPropertyId: 12,
        value: 'Logistics',
    ),
);

Calling create() again with the same clientPropertyId overwrites the existing value. There is no update() or delete().

Write model: ClientPropertyValueCreateOptions ​

The client ID isn't part of the options: pass it as the first argument of create(), which adds client_id to the payload.

PropertyBillomat fieldTypeNotes
clientPropertyIdclient_property_idint (required)ID of the property definition
valuevaluemixed (required)CHECKBOX expects 0/1

Read model: ClientPropertyValue ​

PropertyType
idint
clientIdint
clientPropertyIdint
type?PropertyType (TEXTFIELD, TEXTAREA, CHECKBOX)
name?string (name from the property definition)
valuemixed
created, updated?\DateTimeImmutable
customfield?string (free-form extra field, see Custom meta attributes)
extraarray<string, mixed>

Enums used ​

  • TaxRule: TAX, NO_TAX, COUNTRY (taxRule)
  • NetGross: NET, GROSS, SETTINGS (netGross)
  • ValueType: SETTINGS, ABSOLUTE, RELATIVE (the *Type fields)
  • PaymentType: INVOICE_CORRECTION, CREDIT_NOTE, BANK_CARD, BANK_TRANSFER, DEBIT, CASH, CHECK, PAYPAL, CREDIT_CARD, COUPON, MISC (defaultPaymentTypes)
  • PropertyType: TEXTFIELD, TEXTAREA, CHECKBOX (ClientPropertyValue::$type)

Pitfalls ​

  • E-invoicing (undocumented). Billomat returns facturx_identifier and seller_identifier in API responses but doesn't describe them in the API docs. The SDK only reads them (types derived from observed values); writing isn't supported for lack of a specification.
  • Single-item list quirk. When a list contains only one client, Billomat sometimes sends it as an object instead of a list. ClientsApi::list() normalizes this, so you always get a list<Client>.
  • Archived clients can't be updated. Send archived: false in a first ClientUpdateOptions request, then the remaining changes in a second one.
  • number_length shorter than the actual length. If number already has more digits, Billomat's validation fails and you get a ValidationException.
  • Clients with documents can't be deleted. delete() throws a ValidationException; archive them via update() instead.

End-to-end example ​

php
<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Justpilot\Billomat\Api\ClientCreateOptions;
use Justpilot\Billomat\Api\ClientFilter;
use Justpilot\Billomat\Api\ClientUpdateOptions;
use Justpilot\Billomat\Api\OrderBy;
use Justpilot\Billomat\BillomatClient;
use Justpilot\Billomat\Exception\ValidationException;

$billomat = BillomatClient::create(
    billomatId: 'mycompany',
    apiKey: getenv('BILLOMAT_API_KEY'),
);

// Create
$create = new ClientCreateOptions(name: 'Example GmbH', countryCode: 'DE', email: 'invoices@example.de');

try {
    $client = $billomat->clients->create($create);
} catch (ValidationException $e) {
    fwrite(STDERR, 'Creation rejected: ' . $e->getResponseBody() . PHP_EOL);
    exit(1);
}

printf("Created: #%d %s\n", $client->id, $client->name);

// Read
$reloaded = $billomat->clients->getOrFail($client->id);
var_dump($reloaded->email);

// Update
$update = new ClientUpdateOptions(note: 'First contact: ' . date('Y-m-d'));
$billomat->clients->update($client->id, $update);

// List (e.g. the most recently created)
$recent = $billomat->clients->list(new ClientFilter(
    orderBy: OrderBy::desc('id'),
    perPage: 5,
));
foreach ($recent as $c) {
    printf("- #%d %s\n", $c->id, $c->name);
}

Unofficial SDK, not affiliated with Billomat GmbH. Released under the Apache License 2.0.