Clients
A client is the buyer or receiver on your documents. The API exposes full CRUD under /clients, and when creating documents you can reference a client by id instead of sending their details every time.
Required scopes: clients:browse, clients:read, clients:create, clients:update, clients:delete (one per operation).
Listing clients
curl -s "https://prod.api.fakturnia.pl/external/v1/clients?searchQuery=Kowalski¤tPage=1&itemsPerPage=20" \
-H "Authorization: Bearer $TOKEN"
searchQuery filters by name. The list is paginated: the response includes totalCount and totalPages.
Creating a client
A client is either a company (type: "company") or a person (type: "person"). A company requires name, a person requires firstName and lastName.
curl -s -X POST https://prod.api.fakturnia.pl/external/v1/clients \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "company",
"name": "Przyklad Sp. z o.o.",
"identifierType": "nip",
"nip": "5252248481",
"street": "ul. Prosta 1",
"postalCode": "00-001",
"city": "Warszawa",
"countryCode": "PL",
"email": "biuro@przyklad.pl"
}'
The identifierType field says which identifier identifies the contractor in KSeF; the rules are in Contractor identifiers.
Everything else is optional: regon, krs, bdo, pesel, idNumber, address fields (street, postalCode, city, country, countryCode), contact fields (phoneNumber, email, emailAddresses, www, fax) and description.
For Polish companies you can fetch registry data automatically with the NIP lookup instead of retyping it.
Contractor identifiers
A KSeF invoice identifies the buyer (and the receiver) with exactly one identifier. identifierType picks which one, and the API checks it and stores it in the form KSeF accepts. The same rules apply to contractors (POST and PUT /clients) and to a buyer and receiver given as data when creating documents (POST /documents, POST /documents/validate, PUT and PATCH /documents/{id}).
identifierType |
Contractor | Required fields | Example | In KSeF |
|---|---|---|---|---|
nip |
Polish company | nip |
"nip": "5261040828" |
NIP |
vatEu |
company from another EU country | vatUE with the country prefix |
a contractor from Germany: "identifierType": "vatEu", "vatUE": "DE123456789" |
KodUE + NrVatUE |
other |
company outside the EU, or an EU company without a VAT UE number | countryCode, otherTaxIdentifier |
"countryCode": "US", "otherTaxIdentifier": "12-3456789" |
KodKraju + NrID |
idWew |
internal unit, only as the receiver of a document | idWew |
"idWew": "5252248481-16006" |
IDWew |
none |
consumer, or no identifier | none | "type": "person" |
BrakID |
How the API stores identifiers:
- NIP is 10 digits: spaces, dashes and the
PLprefix are removed (PL 526-104-08-28is stored as5261040828), and the check digit must match. - The VAT UE number is stored in
vatUEwith its country prefix, in uppercase and without spaces (de 123 456 789becomesDE123456789). A number without a prefix gets the prefix fromcountryCode. Greece uses theELprefix in VAT numbers (aGRprefix is turned intoEL), whilecountryCodestaysGR. After the prefix, digits, uppercase letters,+and*are allowed, 1 to 12 characters. Thenipfield never holds a foreign number. - A Polish company with a VAT UE number (e.g. for intra-EU supply invoices) has
identifierType: "nip", the NIP innipandPLwith the same NIP invatUE. - Northern Ireland (
XI) is not an EU country in KSeF: send it asotherwithcountryCode: "XI". otherneeds a country (countryCode, an ISO code such asUS,CH,GB,UA) and an identifier of up to 50 characters; the identifier is stored as you send it.idWewhas the formNIP-NNNNNand belongs to a receiver only: a buyer withidWewgets an error.
When identifierType is missing
The API infers the type from the data and returns every inference in data.warnings, so the integration can start sending the type explicitly:
| Data | Inferred type | What the API stores |
|---|---|---|
nip with a Polish NIP (10 digits, also with PL, spaces or dashes) |
nip |
the NIP as 10 digits |
nip with a VAT UE number of an EU country, e.g. DE123456789 |
vatEu |
the number in vatUE, an empty nip, countryCode from the prefix when missing |
nip with a number without a prefix and the countryCode of an EU country other than Poland, e.g. 123456789 and DE |
vatEu |
vatUE = DE123456789 |
vatUE with the countryCode of an EU country other than Poland, or vatUE alone with an EU prefix |
vatEu |
the number in vatUE |
vatUE alone with the PL prefix |
nip |
the NIP from the VAT UE number |
nip or vatUE with a number of a non-EU country (e.g. GB…, XI…), or nip with a non-EU countryCode |
other |
the number in otherTaxIdentifier |
otherTaxIdentifier alone |
other |
unchanged |
idWew alone |
idWew |
unchanged |
| no identifier | none |
unchanged |
A Polish NIP with a wrong check digit is refused also when the type was inferred.
Warnings in the response
POST and PUT /clients return data.warnings next to data.client, and documents next to data.document (next to data.preview for validate). The list is empty when the identifiers were sent explicitly and correctly.
{
"success": true,
"data": {
"client": { "identifierType": "vatEu", "nip": null, "vatUE": "DE123456789", "countryCode": "DE" },
"warnings": [
{ "code": "IDENTIFIER_TYPE_INFERRED", "field": "identifierType", "message": "Nie podano identifierType: przyjęto vatEu (numer VAT UE). Podawaj identifierType jawnie." },
{ "code": "IDENTIFIER_MOVED", "field": "vatUE", "message": "Numer DE123456789 przeniesiono z pola nip do vatUE: pole nip jest tylko na polski NIP." },
{ "code": "COUNTRY_CODE_INFERRED", "field": "countryCode", "message": "Nie podano countryCode: przyjęto DE z prefiksu numeru VAT UE DE123456789." }
]
}
}
code |
Meaning |
|---|---|
IDENTIFIER_TYPE_INFERRED |
identifierType was missing and the API inferred it |
IDENTIFIER_MOVED |
a number given in the wrong field was moved to vatUE or otherTaxIdentifier |
IDENTIFIER_IGNORED |
a field that does not match the type was ignored (e.g. nip with vatEu) |
COUNTRY_CODE_INFERRED |
countryCode was taken from the prefix of the number |
VAT_UE_PREFIX_NORMALIZED |
the Greek prefix GR was turned into EL |
On documents field carries the party prefix, e.g. buyer.vatUE. Messages are in Polish; branch on code.
Contradictions
The API refuses contradictions and names the field in errors: contractors with 422 VALIDATION_ERROR, documents with 422 PARTY_IDENTIFIER_INVALID (the field prefixed with buyer. or receiver.). Examples: identifierType: "nip" with DE123456789, vatEu with a PL… or XI… number, a VAT UE number without a prefix and without an EU country, a NIP with a wrong length or check digit, different VAT UE numbers in nip and vatUE, other without a country, idWew for a buyer, an unknown identifierType value.
A contractor saved earlier with data the API would not accept today stays editable: PUT does not re-check identifiers equal to the stored ones. A buyer given as data with the same wrong NIP as a saved contractor is taken from the contractor list.
Fetching, updating and deleting
# Details
curl -s https://prod.api.fakturnia.pl/external/v1/clients/{id} \
-H "Authorization: Bearer $TOKEN"
# Update (PUT, full replacement)
curl -s -X PUT https://prod.api.fakturnia.pl/external/v1/clients/{id} \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "type": "company", "name": "Przyklad Sp. z o.o.", "email": "faktury@przyklad.pl" }'
# Delete
curl -s -X DELETE https://prod.api.fakturnia.pl/external/v1/clients/{id} \
-H "Authorization: Bearer $TOKEN"
PUT is a full update: send the complete set of fields the client should have afterwards. Deleting a client does not change previously issued documents, the buyer details are preserved on them.
Webhooks
Write operations emit webhook events: client.created, client.updated, client.deleted.