Przejdź do treści

Dokumentacja API

Połącz własny system z Zaksio: odczytuj i zapisuj produkty, kontrahentów i faktury, sprawdzaj stany magazynowe i odbieraj zdarzenia webhookiem. Wersja dokumentu: 1.0.0.

Szybki start

Publiczne API Zaksio ma adres bazowy https://api.zaksio.pl i wersję w ścieżce (/v1). Obecna wersja dokumentu: 1.0.0. Zmiany niekompatybilne wprowadzimy tylko jako nową wersję ścieżki; nowe pola w odpowiedziach i nowe adresy mogą pojawiać się w ramach /v1, więc ignoruj pola, których nie znasz.

  1. W aplikacji wejdź w Ustawienia, „API i integracje”, kliknij „Nowy klucz”, wybierz firmę i zakresy, potwierdź hasłem i skopiuj klucz (pokazujemy go tylko raz).
  2. Zapisz klucz w zmiennej środowiskowej, np. ZAKSIO_API_KEY, i sprawdź, że działa:
curl https://api.zaksio.pl/v1/whoami \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"

Odpowiedź pokazuje firmę, do której należy klucz, oraz jego zakresy. Dalej korzystaj z adresów opisanych w sekcji „Adresy API”.

Uwierzytelnianie

Każde zapytanie wymaga nagłówka Authorization: Bearer zk_live_.... Klucz działa w imieniu osoby, która go utworzyła, ale tylko w wybranych zakresach i nigdy nie ma więcej uprawnień niż ta osoba (sprawdzane przy każdym zapytaniu). Klucz jest przypisany do jednej firmy: wszystkie adresy działają na danych tej firmy. Tokeny sesji aplikacji nie działają w /v1, a klucze API nie działają poza /v1.

  • Brak lub zły klucz: 401 (kody missing_api_key, invalid_api_key).
  • Klucz odwołany lub wygasły: 401 (api_key_revoked, api_key_expired).
  • Adres IP spoza listy klucza: 403 (ip_not_allowed).
  • Brak zakresu: 403 (insufficient_scope).
  • Licencja w trybie tylko do odczytu: zapisy zwracają 402 (licence_read_only).

Zakresy

Zakres określa, co klucz może robić. Zakresy zamówień i przesyłek są zarezerwowane i jeszcze niedostępne.

Zakresy kluczy API
ZakresOpisStan
products:readLista i szczegoly produktow z kartoteki.dostępny
products:writeDodawanie i zmiana produktow w kartotece.dostępny
stock:readStany magazynowe produktow (suma i podzial na magazyny).dostępny
contractors:readLista i szczegoly kontrahentow.dostępny
contractors:writeDodawanie, zmiana i usuwanie kontrahentow.dostępny
invoices:readLista, szczegoly i PDF faktur sprzedazowych.dostępny
invoices:writeTworzenie i wystawianie faktur sprzedazowych (numeracja i ksiegowanie jak w aplikacji).dostępny
orders:readZamowienia ze wszystkich kanalow (w przygotowaniu).w przygotowaniu
orders:writeTworzenie i zmiana zamowien (w przygotowaniu).w przygotowaniu
shipments:readPrzesylki i statusy (w przygotowaniu).w przygotowaniu
shipments:writeTworzenie przesylek i etykiet (w przygotowaniu).w przygotowaniu

Limity i bezpieczeństwo klucza

  • Limit zapytań: domyślnie 120 na minutę na klucz (konfigurowany przy tworzeniu klucza, do 6000). Każda odpowiedź ma nagłówki RateLimit-Limit, RateLimit-Remaining i RateLimit-Reset (sekundy do końca okna). Po przekroczeniu: 429 (rate_limited) z nagłówkiem Retry-After. Powtórz zapytanie po tym czasie.
  • Błędne uwierzytelnienia: po 30 nieudanych próbach w minucie z jednego adresu IP kolejne dostają 429 (too_many_failed_auth).
  • Lista adresów IP, wygasanie, rotacja i odwołanie: ustawiasz je w aplikacji. Rotacja tworzy nowy klucz, a stary działa jeszcze przez wybrany okres przejściowy; odwołanie działa natychmiast.
  • Przechowywanie: nie umieszczaj klucza w kodzie aplikacji przeglądarkowej ani mobilnej; używaj go tylko po stronie serwera.

Konwencje: kwoty, daty, paginacja

  • Kwoty są liczbami całkowitymi w groszach (12300 = 123,00 zł).
  • Ilości to tekst dziesiętny z kropką, np. "2", "14.5", maksymalnie cztery miejsca po przecinku.
  • Daty to RRRR-MM-DD, a znaczniki czasu to ISO 8601 w UTC. Identyfikatory to UUID.
  • Paginacja kursorowa: listy przyjmują limit (1 do 200, domyślnie 50), cursor i order (asc od najstarszych, desc od najnowszych). Odpowiedź ma data i pagination z polami hasMore oraz nextCursor; kolejną stronę pobierasz, przekazując nextCursor jako cursor. Kursor jest nieprzezroczysty: nie parsuj go. Dopisanie rekordów w trakcie przeglądania nie przesuwa stron.
  • Zmiana częściowa (PATCH): wysyłasz tylko pola do zmiany. Pominięte pola zostają bez zmian.

Idempotencja zapisów

Zapisy (POST, PATCH, DELETE) przyjmują nagłówek Idempotency-Key (do 255 znaków ASCII, np. UUID). Pierwsze zapytanie z danym kluczem wykonuje się normalnie, a jego odpowiedź jest zapamiętywana na 24 godziny. Powtórzenie tego samego zapytania z tym samym kluczem zwraca zapamiętaną odpowiedź z nagłówkiem Idempotent-Replayed: true i nie wykonuje zapisu drugi raz. Dzięki temu możesz bezpiecznie ponowić zapytanie po błędzie sieci, np. nie wystawisz dwóch faktur.

  • Ten sam klucz z innymi danymi: 422 (idempotency_key_reuse).
  • Ten sam klucz, gdy pierwsze zapytanie jeszcze trwa: 409 (idempotency_in_progress), ponów po chwili.
  • Błędy 5xx i 429 nie są zapamiętywane, więc ponowienie zostanie wykonane od nowa.
  • Klucze idempotencji są niezależne dla każdego klucza API.

Błędy

Błędy mają format RFC 9457 (application/problem+json). Opieraj obsługę na polu code i kodzie HTTP, a nie na tekście detail. Pole requestId (również w nagłówku X-Request-Id) podaj w zgłoszeniu do pomocy.

{
  "type": "https://zaksio.pl/dokumentacja-api#blad-insufficient-scope",
  "title": "Brak dostepu",
  "status": 403,
  "detail": "Klucz nie ma zakresu products:write (albo tworca klucza utracil odpowiednie uprawnienie)",
  "instance": "/v1/products",
  "code": "insufficient_scope",
  "requestId": "req-1a2b3c"
}

Przy validation_error (400) pole errors zawiera listę nieprawidłowych pól ze wskaźnikiem pointer, np. /lines/0/quantity.

Kody błędów
Kod (code)HTTPZnaczenie
missing_api_key401Brak naglowka Authorization: Bearer zk_live_...
invalid_api_key401Klucz ma zly format albo nie istnieje
api_key_revoked401Klucz zostal odwolany
api_key_expired401Klucz wygasl (takze po okresie przejsciowym rotacji)
api_key_owner_removed401Osoba, ktora utworzyla klucz, nie nalezy juz do organizacji
ip_not_allowed403Adres IP zadania nie jest na liscie dozwolonych dla klucza
insufficient_scope403Klucz nie ma zakresu wymaganego przez operacje (albo tworca klucza utracil uprawnienie)
feature_disabled403Publiczne API jest wylaczone dla organizacji
module_disabled403Modul (katalog, magazyn) nie jest wlaczony w profilu firmy
tenant_deleted403Organizacja zostala usunieta
licence_read_only402Licencja wygasla lub platnosc zalegla: dozwolony tylko odczyt
document_limit_reached402Wyczerpano miesieczny limit dokumentow w planie
validation_error400Nieprawidlowe dane; lista pol w errors[]
invalid_cursor400Kursor paginacji jest nieprawidlowy
invalid_idempotency_key400Idempotency-Key ma niedozwolone znaki albo jest za dlugi
not_found404Nie ma takiego adresu w API v1
product_not_found404Nie znaleziono produktu
contractor_not_found404Nie znaleziono kontrahenta (przy tworzeniu faktury: 422)
invoice_not_found404Nie znaleziono faktury
sku_or_ean_taken409SKU lub kod EAN jest juz uzyty w tej firmie
product_has_stock_history409Nie zmienia sie sledzenia partii produktu z historia ruchow
contractor_in_use409Kontrahent wystepuje na dokumentach i nie mozna go usunac
invoice_not_draft409Wystawionej faktury nie mozna zmieniac ani wystawic ponownie
period_closed409Okres ksiegowy jest zamkniety
idempotency_in_progress409Zadanie z tym Idempotency-Key jest wlasnie wykonywane; ponow po chwili
idempotency_key_reuse422Ten Idempotency-Key byl juz uzyty z innymi danymi zadania
invalid_service422Usluga nie ma partii, terminu waznosci ani cech ADR
exemption_basis_required422Pozycje zwolnione z VAT (zw) wymagaja podstawy zwolnienia
non_positive_total422Kwota faktury musi byc dodatnia
too_many_failed_auth429Zbyt wiele blednych prob uwierzytelnienia z jednego adresu
rate_limited429Przekroczono limit zapytan na minute dla klucza
maintenance503Tryb serwisowy: zapisy chwilowo zablokowane
internal_error500Blad serwera; podaj requestId w zgloszeniu

Adresy API

Konto

Informacje o kluczu API uzytym w zadaniu.

Informacje o kluczu

GET/v1/whoami

Wymagany zakres: żaden (wystarczy dowolny ważny klucz)

Zwraca firme, zakresy i limit biezacego klucza. Przydatne do sprawdzenia, czy klucz dziala i do czego daje dostep. Nie wymaga zadnego zakresu.

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/whoami" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/whoami", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/whoami",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/whoami');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "key": {
    "id": "5e8d2b1c-9f3a-4b7d-8c6e-2a1b3c4d5e6f",
    "name": "Integracja sklepu",
    "prefix": "zk_live_AbCd1234",
    "scopes": [
      "products:read",
      "stock:read"
    ],
    "rateLimitPerMin": 120
  },
  "company": {
    "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
    "name": "Moja Firma Sp. z o.o.",
    "nip": "5252248481"
  }
}

Możliwe błędy: 401, 429, 500 (format opisany w sekcji „Błędy”).

Produkty

Kartoteka produktow i uslug.

Lista produktow

GET/v1/products

Wymagany zakres: products:read

Produkty z kartoteki firmy przypisanej do klucza. Paginacja kursorowa; domyslnie od najstarszych (`order=asc`), bez produktow nieaktywnych.

Parametry: Lista produktow
ParametrGdzieTypOpis
limitzapytanieinteger
cursorzapytaniestring
orderzapytanieasc | desc
qzapytaniestringSzukaj w SKU i nazwie albo dokladny kod EAN
skuzapytaniestringDokladny SKU
kindzapytaniegood | service
includeInactivezapytanietrue | false

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/products?limit=50&q=kawa" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/products?limit=50&q=kawa", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/products",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    params={
        "limit": "50",
        "q": "kawa",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/products?limit=50&q=kawa');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "data": [
    {
      "id": "3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
      "sku": "KAW-001",
      "name": "Kawa ziarnista 1 kg",
      "kind": "good",
      "unit": "kg",
      "ean": "5901234123457",
      "vatRate": "8",
      "defaultNetPrice": 4500,
      "tracking": "none",
      "hasExpiry": false,
      "weightG": 1000,
      "hazmat": false,
      "active": true,
      "createdAt": "2026-10-12T08:15:00.000Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "hasMore": false,
    "nextCursor": null
  }
}

Możliwe błędy: 400, 401, 403, 429, 500 (format opisany w sekcji „Błędy”).

Dodanie produktu

POST/v1/products

Wymagany zakres: products:write

Dodaje produkt do kartoteki. SKU i EAN musza byc unikalne w firmie (409 `sku_or_ean_taken`). Obsluguje `Idempotency-Key`.

Parametry: Dodanie produktu
ParametrGdzieTypOpis
Idempotency-KeynagłówekstringUnikalny klucz zapisu (np. UUID). Powtorzenie zadania z tym samym kluczem zwraca zapamietana odpowiedz zamiast wykonywac zapis drugi raz (24 h).
Pola zapytania: Dodanie produktu
PoleTypOpis
skuwymaganestringmaks. 64 znaków
namewymaganestringmaks. 300 znaków
kindgood | servicedomyślnie: "good"
unitstringdomyślnie: "szt.". maks. 20 znaków
eanstring
vatRate0 | 5 | 8 | 23 | zw | npdomyślnie: "23"
defaultNetPriceinteger
trackingnone | lot | serialdomyślnie: "none"
hasExpirybooleandomyślnie: false
weightGinteger
hazmatbooleandomyślnie: false

Przykład zapytania

curl
curl -X POST "https://api.zaksio.pl/v1/products" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b" \
  -d '{"sku":"KAW-001","name":"Kawa ziarnista 1 kg","unit":"kg","vatRate":"8","defaultNetPrice":4500,"ean":"5901234123457"}'
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/products", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
  },
  body: JSON.stringify({
    "sku": "KAW-001",
    "name": "Kawa ziarnista 1 kg",
    "unit": "kg",
    "vatRate": "8",
    "defaultNetPrice": 4500,
    "ean": "5901234123457"
  }),
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "POST",
    "https://api.zaksio.pl/v1/products",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
    },
    json={
        "sku": "KAW-001",
        "name": "Kawa ziarnista 1 kg",
        "unit": "kg",
        "vatRate": "8",
        "defaultNetPrice": 4500,
        "ean": "5901234123457",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/products');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'sku' => 'KAW-001',
        'name' => 'Kawa ziarnista 1 kg',
        'unit' => 'kg',
        'vatRate' => '8',
        'defaultNetPrice' => 4500,
        'ean' => '5901234123457',
    ]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 201)

{
  "id": "3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
  "sku": "KAW-001",
  "name": "Kawa ziarnista 1 kg",
  "kind": "good",
  "unit": "kg",
  "ean": "5901234123457",
  "vatRate": "8",
  "defaultNetPrice": 4500,
  "tracking": "none",
  "hasExpiry": false,
  "weightG": 1000,
  "hazmat": false,
  "active": true,
  "createdAt": "2026-10-12T08:15:00.000Z"
}

Możliwe błędy: 400, 401, 402, 403, 409, 422, 429, 500 (format opisany w sekcji „Błędy”).

Szczegoly produktu

GET/v1/products/{id}

Wymagany zakres: products:read

Parametry: Szczegoly produktu
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "id": "3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
  "sku": "KAW-001",
  "name": "Kawa ziarnista 1 kg",
  "kind": "good",
  "unit": "kg",
  "ean": "5901234123457",
  "vatRate": "8",
  "defaultNetPrice": 4500,
  "tracking": "none",
  "hasExpiry": false,
  "weightG": 1000,
  "hazmat": false,
  "active": true,
  "createdAt": "2026-10-12T08:15:00.000Z"
}

Możliwe błędy: 400, 401, 403, 404, 429, 500 (format opisany w sekcji „Błędy”).

Zmiana produktu

PATCH/v1/products/{id}

Wymagany zakres: products:write

Zmienia tylko przekazane pola. Rodzaju (`kind`) nie zmienia sie; sledzenie partii i terminow waznosci mozna zmienic tylko dla produktu bez historii ruchow magazynowych.

Parametry: Zmiana produktu
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)
Idempotency-KeynagłówekstringUnikalny klucz zapisu (np. UUID). Powtorzenie zadania z tym samym kluczem zwraca zapamietana odpowiedz zamiast wykonywac zapis drugi raz (24 h).
Pola zapytania: Zmiana produktu
PoleTypOpis
skustringmaks. 64 znaków
namestringmaks. 300 znaków
unitstringmaks. 20 znaków
eanstring
vatRate0 | 5 | 8 | 23 | zw | np
defaultNetPriceinteger
trackingnone | lot | serial
hasExpiryboolean
weightGinteger
hazmatboolean

Przykład zapytania

curl
curl -X PATCH "https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b" \
  -d '{"name":"Kawa ziarnista Arabica 1 kg","defaultNetPrice":4900}'
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
  },
  body: JSON.stringify({
    "name": "Kawa ziarnista Arabica 1 kg",
    "defaultNetPrice": 4900
  }),
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "PATCH",
    "https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
    },
    json={
        "name": "Kawa ziarnista Arabica 1 kg",
        "defaultNetPrice": 4900,
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/products/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'Kawa ziarnista Arabica 1 kg',
        'defaultNetPrice' => 4900,
    ]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "id": "3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
  "sku": "KAW-001",
  "name": "Kawa ziarnista Arabica 1 kg",
  "kind": "good",
  "unit": "kg",
  "ean": "5901234123457",
  "vatRate": "8",
  "defaultNetPrice": 4900,
  "tracking": "none",
  "hasExpiry": false,
  "weightG": 1000,
  "hazmat": false,
  "active": true,
  "createdAt": "2026-10-12T08:15:00.000Z"
}

Możliwe błędy: 400, 401, 402, 403, 404, 409, 422, 429, 500 (format opisany w sekcji „Błędy”).

Stany magazynowe

Aktualne stany magazynowe (odczyt).

Stany magazynowe

GET/v1/stock

Wymagany zakres: stock:read

Stan kazdego produktu, ktory ma ruchy magazynowe: suma oraz podzial na magazyny. Lista jest uporzadkowana po identyfikatorze produktu, a kursor wskazuje ostatni zwrocony produkt.

Parametry: Stany magazynowe
ParametrGdzieTypOpis
limitzapytanieinteger
cursorzapytaniestring
warehouseIdzapytaniestring (uuid)Ogranicz do jednego magazynu

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/stock?limit=50" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/stock?limit=50", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/stock",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    params={
        "limit": "50",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/stock?limit=50');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "data": [
    {
      "productId": "3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
      "sku": "KAW-001",
      "name": "Kawa ziarnista 1 kg",
      "unit": "kg",
      "quantity": "42.5",
      "warehouses": [
        {
          "warehouseId": "a1b2c3d4-0000-4000-8000-000000000001",
          "warehouseCode": "MAG",
          "quantity": "40"
        },
        {
          "warehouseId": "a1b2c3d4-0000-4000-8000-000000000002",
          "warehouseCode": "SKLEP",
          "quantity": "2.5"
        }
      ]
    }
  ],
  "pagination": {
    "limit": 50,
    "hasMore": false,
    "nextCursor": null
  }
}

Możliwe błędy: 400, 401, 403, 429, 500 (format opisany w sekcji „Błędy”).

Stan jednego produktu

GET/v1/stock/{id}

Wymagany zakres: stock:read

Stan produktu o podanym identyfikatorze. Produkt bez ruchow magazynowych ma stan 0.

Parametry: Stan jednego produktu
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/stock/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/stock/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/stock/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/stock/3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "productId": "3f9a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
  "sku": "KAW-001",
  "name": "Kawa ziarnista 1 kg",
  "unit": "kg",
  "quantity": "42.5",
  "warehouses": [
    {
      "warehouseId": "a1b2c3d4-0000-4000-8000-000000000001",
      "warehouseCode": "MAG",
      "quantity": "40"
    },
    {
      "warehouseId": "a1b2c3d4-0000-4000-8000-000000000002",
      "warehouseCode": "SKLEP",
      "quantity": "2.5"
    }
  ]
}

Możliwe błędy: 400, 401, 403, 404, 429, 500 (format opisany w sekcji „Błędy”).

Kontrahenci

Klienci i dostawcy.

Lista kontrahentow

GET/v1/contractors

Wymagany zakres: contractors:read

Kontrahenci firmy przypisanej do klucza. Paginacja kursorowa.

Parametry: Lista kontrahentow
ParametrGdzieTypOpis
limitzapytanieinteger
cursorzapytaniestring
orderzapytanieasc | desc
qzapytaniestringSzukaj w nazwie, e-mailu i miescie
nipzapytaniestringDokladny NIP (cyfry)

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/contractors?q=przyklad" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/contractors?q=przyklad", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/contractors",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    params={
        "q": "przyklad",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/contractors?q=przyklad');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "data": [
    {
      "id": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
      "name": "Przykladowa Sp. z o.o.",
      "nip": "5252248481",
      "country": "PL",
      "street": "Prosta 1",
      "postalCode": "00-001",
      "city": "Warszawa",
      "email": "ksiegowosc@przyklad.pl",
      "createdAt": "2026-10-12T08:20:00.000Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "hasMore": false,
    "nextCursor": null
  }
}

Możliwe błędy: 400, 401, 403, 429, 500 (format opisany w sekcji „Błędy”).

Dodanie kontrahenta

POST/v1/contractors

Wymagany zakres: contractors:write

Dodaje kontrahenta. Obsluguje `Idempotency-Key`.

Parametry: Dodanie kontrahenta
ParametrGdzieTypOpis
Idempotency-KeynagłówekstringUnikalny klucz zapisu (np. UUID). Powtorzenie zadania z tym samym kluczem zwraca zapamietana odpowiedz zamiast wykonywac zapis drugi raz (24 h).
Pola zapytania: Dodanie kontrahenta
PoleTypOpis
namewymaganestringmaks. 300 znaków
nipstring
countrystringdomyślnie: "PL". maks. 2 znaków
streetstringmaks. 200 znaków
postalCodestringmaks. 20 znaków
citystringmaks. 100 znaków
emailstring (email)
vatEuNumberstring
isConsumerboolean

Przykład zapytania

curl
curl -X POST "https://api.zaksio.pl/v1/contractors" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b" \
  -d '{"name":"Przykladowa Sp. z o.o.","nip":"5252248481","street":"Prosta 1","postalCode":"00-001","city":"Warszawa","email":"ksiegowosc@przyklad.pl"}'
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/contractors", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
  },
  body: JSON.stringify({
    "name": "Przykladowa Sp. z o.o.",
    "nip": "5252248481",
    "street": "Prosta 1",
    "postalCode": "00-001",
    "city": "Warszawa",
    "email": "ksiegowosc@przyklad.pl"
  }),
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "POST",
    "https://api.zaksio.pl/v1/contractors",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
    },
    json={
        "name": "Przykladowa Sp. z o.o.",
        "nip": "5252248481",
        "street": "Prosta 1",
        "postalCode": "00-001",
        "city": "Warszawa",
        "email": "ksiegowosc@przyklad.pl",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/contractors');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'Przykladowa Sp. z o.o.',
        'nip' => '5252248481',
        'street' => 'Prosta 1',
        'postalCode' => '00-001',
        'city' => 'Warszawa',
        'email' => 'ksiegowosc@przyklad.pl',
    ]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 201)

{
  "id": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
  "name": "Przykladowa Sp. z o.o.",
  "nip": "5252248481",
  "country": "PL",
  "street": "Prosta 1",
  "postalCode": "00-001",
  "city": "Warszawa",
  "email": "ksiegowosc@przyklad.pl",
  "createdAt": "2026-10-12T08:20:00.000Z"
}

Możliwe błędy: 400, 401, 402, 403, 409, 422, 429, 500 (format opisany w sekcji „Błędy”).

Szczegoly kontrahenta

GET/v1/contractors/{id}

Wymagany zakres: contractors:read

Parametry: Szczegoly kontrahenta
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "id": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
  "name": "Przykladowa Sp. z o.o.",
  "nip": "5252248481",
  "country": "PL",
  "street": "Prosta 1",
  "postalCode": "00-001",
  "city": "Warszawa",
  "email": "ksiegowosc@przyklad.pl",
  "createdAt": "2026-10-12T08:20:00.000Z"
}

Możliwe błędy: 400, 401, 403, 404, 429, 500 (format opisany w sekcji „Błędy”).

Zmiana kontrahenta

PATCH/v1/contractors/{id}

Wymagany zakres: contractors:write

Zmienia tylko przekazane pola; `null` czysci pole (poza nazwa i krajem). Faktury juz wystawione zachowuja dane z chwili wystawienia.

Parametry: Zmiana kontrahenta
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)
Idempotency-KeynagłówekstringUnikalny klucz zapisu (np. UUID). Powtorzenie zadania z tym samym kluczem zwraca zapamietana odpowiedz zamiast wykonywac zapis drugi raz (24 h).
Pola zapytania: Zmiana kontrahenta
PoleTypOpis
namestringmaks. 300 znaków
nipstring lub null
countrystringmaks. 2 znaków
streetstring lub nullmaks. 200 znaków
postalCodestring lub nullmaks. 20 znaków
citystring lub nullmaks. 100 znaków
emailstring (email) lub null
vatEuNumberstring lub null
isConsumerboolean

Przykład zapytania

curl
curl -X PATCH "https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b" \
  -d '{"city":"Krakow","email":null}'
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
  },
  body: JSON.stringify({
    "city": "Krakow",
    "email": null
  }),
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "PATCH",
    "https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
    },
    json={
        "city": "Krakow",
        "email": None,
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'city' => 'Krakow',
        'email' => null,
    ]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "id": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
  "name": "Przykladowa Sp. z o.o.",
  "nip": "5252248481",
  "country": "PL",
  "street": "Prosta 1",
  "postalCode": "00-001",
  "city": "Krakow",
  "email": null,
  "createdAt": "2026-10-12T08:20:00.000Z"
}

Możliwe błędy: 400, 401, 402, 403, 404, 409, 422, 429, 500 (format opisany w sekcji „Błędy”).

Usuniecie kontrahenta

DELETE/v1/contractors/{id}

Wymagany zakres: contractors:write

Usuwa kontrahenta, ktory nie wystepuje na zadnym dokumencie (409 `contractor_in_use` w przeciwnym razie).

Parametry: Usuniecie kontrahenta
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)
Idempotency-KeynagłówekstringUnikalny klucz zapisu (np. UUID). Powtorzenie zadania z tym samym kluczem zwraca zapamietana odpowiedz zamiast wykonywac zapis drugi raz (24 h).

Przykład zapytania

curl
curl -X DELETE "https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -H "Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
    "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
  },
});

if (!response.ok) {
  const problem = await response.json(); // application/problem+json
  throw new Error(`${problem.code}: ${problem.detail}`);
}
Python
import os
import requests

response = requests.request(
    "DELETE",
    "https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
        "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/contractors/7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
        'Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}

Odpowiedź: kod 200.

Możliwe błędy: 400, 401, 402, 403, 404, 409, 422, 429, 500 (format opisany w sekcji „Błędy”).

Faktury

Faktury sprzedazowe: odczyt, PDF i wystawianie.

Lista faktur sprzedazowych

GET/v1/invoices

Wymagany zakres: invoices:read

Faktury (i korekty) firmy przypisanej do klucza, bez pozycji (szczegoly: `GET /v1/invoices/{id}`). Paginacja kursorowa; do synchronizacji przyrostowej uzyj `order=asc` i zapamietaj ostatni kursor.

Parametry: Lista faktur sprzedazowych
ParametrGdzieTypOpis
limitzapytanieinteger
cursorzapytaniestring
orderzapytanieasc | desc
statuszapytaniedraft | issued
contractorIdzapytaniestring (uuid)
issueDateFromzapytaniestring (date)
issueDateTozapytaniestring (date)

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/invoices?status=issued&issueDateFrom=2026-10-01&limit=50" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/invoices?status=issued&issueDateFrom=2026-10-01&limit=50", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/invoices",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    params={
        "status": "issued",
        "issueDateFrom": "2026-10-01",
        "limit": "50",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/invoices?status=issued&issueDateFrom=2026-10-01&limit=50');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "data": [
    {
      "id": "0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
      "number": "FV/001/10/2026",
      "kind": "sales",
      "status": "issued",
      "contractorId": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
      "issueDate": "2026-10-12",
      "saleDate": "2026-10-12",
      "dueDate": "2026-10-26",
      "currency": "PLN",
      "paymentMethod": "przelew",
      "bankAccount": null,
      "notes": null,
      "vatExemptionBasis": null,
      "netTotal": 25000,
      "vatTotal": 5000,
      "grossTotal": 30000,
      "paidTotal": 0,
      "paidAt": null,
      "correctsInvoiceId": null,
      "ksefNumber": null,
      "ksefStatus": null,
      "issuedAt": "2026-10-12T09:30:00.000Z",
      "createdAt": "2026-10-12T09:30:00.000Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "hasMore": false,
    "nextCursor": null
  }
}

Możliwe błędy: 400, 401, 403, 429, 500 (format opisany w sekcji „Błędy”).

Wystawienie faktury

POST/v1/invoices

Wymagany zakres: invoices:write

Tworzy fakture sprzedazowa i (domyslnie) od razu ja wystawia: numeracja bez luk, ksiegowanie i podsumowanie VAT jak w aplikacji. Kwoty w groszach, ilosc jako tekst dziesietny. Wystawienie i zapis to jedna transakcja: przy bledzie nie zostaje szkic. **Zawsze podawaj `Idempotency-Key`**, zeby ponowienie po bledzie sieci nie wystawilo faktury drugi raz.

Parametry: Wystawienie faktury
ParametrGdzieTypOpis
Idempotency-KeynagłówekstringUnikalny klucz zapisu (np. UUID). Powtorzenie zadania z tym samym kluczem zwraca zapamietana odpowiedz zamiast wykonywac zapis drugi raz (24 h).
Pola zapytania: Wystawienie faktury
PoleTypOpis
contractorIdwymaganestring (uuid)
issueDatewymaganestring (date)
saleDatewymaganestring (date)
dueDatestring (date)
currencystring
taxPointDatestring (date)
vatScenariodomestic | domestic_reverse_charge | wdt | export_goods | services_eu_b2b | services_non_eu
vatOptionsobject
buyerVatIdstring
paymentMethodstringmaks. 50 znaków
bankAccountstringmaks. 40 znaków
notesstringmaks. 2000 znaków
vatExemptionBasisstringmaks. 240 znaków
lineswymaganeobject[]
issuebooleantrue (domyslnie): faktura jest od razu wystawiana (numer, ksiegowanie); false: zapisuje szkic do pozniejszego wystawienia. domyślnie: true

Przykład zapytania

curl
curl -X POST "https://api.zaksio.pl/v1/invoices" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b" \
  -d '{"contractorId":"7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f","issueDate":"2026-10-12","saleDate":"2026-10-12","dueDate":"2026-10-26","paymentMethod":"przelew","lines":[{"name":"Usluga A","quantity":"2","unitNetPrice":10000,"vatRate":"23"},{"name":"Usluga B","quantity":"1","unitNetPrice":5000,"vatRate":"8"}]}'
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/invoices", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
  },
  body: JSON.stringify({
    "contractorId": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
    "issueDate": "2026-10-12",
    "saleDate": "2026-10-12",
    "dueDate": "2026-10-26",
    "paymentMethod": "przelew",
    "lines": [
      {
        "name": "Usluga A",
        "quantity": "2",
        "unitNetPrice": 10000,
        "vatRate": "23"
      },
      {
        "name": "Usluga B",
        "quantity": "1",
        "unitNetPrice": 5000,
        "vatRate": "8"
      }
    ]
  }),
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "POST",
    "https://api.zaksio.pl/v1/invoices",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
    },
    json={
        "contractorId": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
        "issueDate": "2026-10-12",
        "saleDate": "2026-10-12",
        "dueDate": "2026-10-26",
        "paymentMethod": "przelew",
        "lines": [
            {
                "name": "Usluga A",
                "quantity": "2",
                "unitNetPrice": 10000,
                "vatRate": "23",
            },
            {
                "name": "Usluga B",
                "quantity": "1",
                "unitNetPrice": 5000,
                "vatRate": "8",
            },
        ],
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/invoices');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'contractorId' => '7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f',
        'issueDate' => '2026-10-12',
        'saleDate' => '2026-10-12',
        'dueDate' => '2026-10-26',
        'paymentMethod' => 'przelew',
        'lines' => [
            [
                'name' => 'Usluga A',
                'quantity' => '2',
                'unitNetPrice' => 10000,
                'vatRate' => '23',
            ],
            [
                'name' => 'Usluga B',
                'quantity' => '1',
                'unitNetPrice' => 5000,
                'vatRate' => '8',
            ],
        ],
    ]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 201)

{
  "id": "0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
  "number": "FV/001/10/2026",
  "kind": "sales",
  "status": "issued",
  "contractorId": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
  "issueDate": "2026-10-12",
  "saleDate": "2026-10-12",
  "dueDate": "2026-10-26",
  "currency": "PLN",
  "paymentMethod": "przelew",
  "bankAccount": null,
  "notes": null,
  "vatExemptionBasis": null,
  "netTotal": 25000,
  "vatTotal": 5000,
  "grossTotal": 30000,
  "paidTotal": 0,
  "paidAt": null,
  "correctsInvoiceId": null,
  "ksefNumber": null,
  "ksefStatus": null,
  "issuedAt": "2026-10-12T09:30:00.000Z",
  "createdAt": "2026-10-12T09:30:00.000Z",
  "lines": [
    {
      "position": 1,
      "name": "Usluga A",
      "unit": "szt.",
      "quantity": "2",
      "unitNetPrice": 10000,
      "vatRate": "23",
      "net": 20000,
      "productId": null
    },
    {
      "position": 2,
      "name": "Usluga B",
      "unit": "szt.",
      "quantity": "1",
      "unitNetPrice": 5000,
      "vatRate": "8",
      "net": 5000,
      "productId": null
    }
  ],
  "vatSummary": [
    {
      "vatRate": "23",
      "net": 20000,
      "vat": 4600,
      "gross": 24600
    },
    {
      "vatRate": "8",
      "net": 5000,
      "vat": 400,
      "gross": 5400
    }
  ]
}

Możliwe błędy: 400, 401, 402, 403, 409, 422, 429, 500 (format opisany w sekcji „Błędy”).

Szczegoly faktury

GET/v1/invoices/{id}

Wymagany zakres: invoices:read

Faktura z pozycjami i podsumowaniem VAT.

Parametry: Szczegoly faktury
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "id": "0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
  "number": "FV/001/10/2026",
  "kind": "sales",
  "status": "issued",
  "contractorId": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
  "issueDate": "2026-10-12",
  "saleDate": "2026-10-12",
  "dueDate": "2026-10-26",
  "currency": "PLN",
  "paymentMethod": "przelew",
  "bankAccount": null,
  "notes": null,
  "vatExemptionBasis": null,
  "netTotal": 25000,
  "vatTotal": 5000,
  "grossTotal": 30000,
  "paidTotal": 0,
  "paidAt": null,
  "correctsInvoiceId": null,
  "ksefNumber": null,
  "ksefStatus": null,
  "issuedAt": "2026-10-12T09:30:00.000Z",
  "createdAt": "2026-10-12T09:30:00.000Z",
  "lines": [
    {
      "position": 1,
      "name": "Usluga A",
      "unit": "szt.",
      "quantity": "2",
      "unitNetPrice": 10000,
      "vatRate": "23",
      "net": 20000,
      "productId": null
    },
    {
      "position": 2,
      "name": "Usluga B",
      "unit": "szt.",
      "quantity": "1",
      "unitNetPrice": 5000,
      "vatRate": "8",
      "net": 5000,
      "productId": null
    }
  ],
  "vatSummary": [
    {
      "vatRate": "23",
      "net": 20000,
      "vat": 4600,
      "gross": 24600
    },
    {
      "vatRate": "8",
      "net": 5000,
      "vat": 400,
      "gross": 5400
    }
  ]
}

Możliwe błędy: 400, 401, 403, 404, 429, 500 (format opisany w sekcji „Błędy”).

PDF faktury

GET/v1/invoices/{id}/pdf

Wymagany zakres: invoices:read

Plik PDF faktury w tym samym ksztalcie co w aplikacji (`Content-Type: application/pdf`). Pobieraj PDF wystawionej faktury: szkic nie ma jeszcze numeru.

Parametry: PDF faktury
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)

Przykład zapytania

curl
curl -X GET "https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/pdf" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -o faktura.pdf
JavaScript (Node.js)
import { writeFileSync } from "node:fs";

const response = await fetch("https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/pdf", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
  },
});

if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
writeFileSync("faktura.pdf", Buffer.from(await response.arrayBuffer()));
Python
import os
import requests

response = requests.request(
    "GET",
    "https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/pdf",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
open("faktura.pdf", "wb").write(response.content)
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/pdf');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) { throw new RuntimeException($body); }
file_put_contents('faktura.pdf', $body);

Odpowiedź: kod 200.

Możliwe błędy: 400, 401, 403, 404, 429, 500 (format opisany w sekcji „Błędy”).

Wystawienie szkicu faktury

POST/v1/invoices/{id}/issue

Wymagany zakres: invoices:write

Wystawia fakture zapisana wczesniej jako szkic (z API albo z aplikacji). Wystawionej faktury nie mozna wystawic ponownie (409 `invoice_not_draft`). Obsluguje `Idempotency-Key`.

Parametry: Wystawienie szkicu faktury
ParametrGdzieTypOpis
idwymaganyadresstring (uuid)
Idempotency-KeynagłówekstringUnikalny klucz zapisu (np. UUID). Powtorzenie zadania z tym samym kluczem zwraca zapamietana odpowiedz zamiast wykonywac zapis drugi raz (24 h).

Przykład zapytania

curl
curl -X POST "https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/issue" \
  -H "Authorization: Bearer $ZAKSIO_API_KEY" \
  -H "Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b"
JavaScript (Node.js)
const response = await fetch("https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/issue", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ZAKSIO_API_KEY}`,
    "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
  },
});

const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.detail}`); // application/problem+json
console.log(data);
Python
import os
import requests

response = requests.request(
    "POST",
    "https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/issue",
    headers={
        "Authorization": f"Bearer {os.environ['ZAKSIO_API_KEY']}",
        "Idempotency-Key": "6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b",
    },
    timeout=30,
)

if not response.ok:
    problem = response.json()  # application/problem+json
    raise RuntimeError(f"{problem['code']}: {problem['detail']}")
print(response.json())
PHP
<?php
$ch = curl_init('https://api.zaksio.pl/v1/invoices/0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b/issue');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ZAKSIO_API_KEY'),
        'Idempotency-Key: 6f1c8a3e-2b4d-4e5f-9a7b-3c2d1e0f9a8b',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status >= 400) {
    // application/problem+json
    throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}
print_r($data);

Przykładowa odpowiedź (kod 200)

{
  "id": "0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
  "number": "FV/001/10/2026",
  "kind": "sales",
  "status": "issued",
  "contractorId": "7d2c9e10-5a4b-4c3d-9e8f-1a2b3c4d5e6f",
  "issueDate": "2026-10-12",
  "saleDate": "2026-10-12",
  "dueDate": "2026-10-26",
  "currency": "PLN",
  "paymentMethod": "przelew",
  "bankAccount": null,
  "notes": null,
  "vatExemptionBasis": null,
  "netTotal": 25000,
  "vatTotal": 5000,
  "grossTotal": 30000,
  "paidTotal": 0,
  "paidAt": null,
  "correctsInvoiceId": null,
  "ksefNumber": null,
  "ksefStatus": null,
  "issuedAt": "2026-10-12T09:30:00.000Z",
  "createdAt": "2026-10-12T09:30:00.000Z",
  "lines": [
    {
      "position": 1,
      "name": "Usluga A",
      "unit": "szt.",
      "quantity": "2",
      "unitNetPrice": 10000,
      "vatRate": "23",
      "net": 20000,
      "productId": null
    },
    {
      "position": 2,
      "name": "Usluga B",
      "unit": "szt.",
      "quantity": "1",
      "unitNetPrice": 5000,
      "vatRate": "8",
      "net": 5000,
      "productId": null
    }
  ],
  "vatSummary": [
    {
      "vatRate": "23",
      "net": 20000,
      "vat": 4600,
      "gross": 24600
    },
    {
      "vatRate": "8",
      "net": 5000,
      "vat": 400,
      "gross": 5400
    }
  ]
}

Możliwe błędy: 400, 401, 402, 403, 404, 409, 422, 429, 500 (format opisany w sekcji „Błędy”).

Webhooki

Webhook to zapytanie POST, które Zaksio wysyła na Twój adres (https, publiczny), gdy wystąpi zdarzenie. Subskrypcje dodajesz w aplikacji (Ustawienia, „API i integracje”, zakładka „Webhooki”): podajesz adres, wybierasz zdarzenia, ewentualnie jedną firmę, i zapisujesz sekret podpisu (widoczny tylko raz).

Koperta i nagłówki

Treść to obiekt JSON z polami id (identyfikator zdarzenia, stały przy ponowieniach), type, apiVersion, createdAt, companyId i data (dane w tym samym kształcie co w odpowiedziach API). Zapytanie ma nagłówki Zaksio-Event, Zaksio-Event-Id, Zaksio-Delivery i Zaksio-Signature.

Zdarzenia webhooków
ZdarzenieKiedy
invoice.issuedFaktura wystawiona. Faktura sprzedazowa (lub korekta) zostala wystawiona i zaksiegowana.
invoice.paidFaktura oplacona. Na fakturze zarejestrowano pierwsza wplate (pole paidAt ma wartosc).
contractor.createdKontrahent dodany. Dodano kontrahenta (z aplikacji, API albo importu).
contractor.updatedKontrahent zmieniony. Zmieniono dane kontrahenta.
product.createdProdukt dodany. Dodano produkt do kartoteki.
product.updatedProdukt zmieniony. Zmieniono dane produktu w kartotece (takze dezaktywacja).
product.stock_changedStan produktu zmieniony. Zmienil sie stan magazynowy produktu. Kilka ruchow zapisanych w krotkim czasie (zanim zdarzenie zostanie rozeslane) zlewa sie w jedno zdarzenie z aktualnym stanem.
{
  "id": "b9f1c2d3-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
  "type": "invoice.issued",
  "apiVersion": "v1",
  "createdAt": "2026-10-12T09:30:00.000Z",
  "companyId": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
  "data": {
    "invoice": {
      "id": "0b6f7a3e-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
      "number": "FV/001/10/2026",
      "kind": "sales",
      "status": "issued",
      "grossTotal": 12300,
      "currency": "PLN"
    }
  }
}

Przykłady danych dla pozostałych zdarzeń znajdziesz w pliku OpenAPI (pole x-webhook-events). Przycisk „Test” wysyła zdarzenie webhook.test z przykładową treścią, żebyś mógł sprawdzić adres i weryfikację podpisu.

Weryfikacja podpisu

Nagłówek Zaksio-Signature ma postać t=<unix>,v1=<hex>. Podpis to HMAC-SHA256 z sekretu subskrypcji nad tekstem <t>.<surowa treść zapytania>. Sprawdź podpis na surowej treści (nie na ponownie zserializowanym JSON), porównaj go w stałym czasie i odrzuć zapytania ze znacznikiem t starszym niż 5 minut. Podczas wymiany sekretu nagłówek może mieć kilka wartości v1=: wystarczy, że jedna pasuje.

Sprawdzanie podpisu

JavaScript (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: surowa tresc zadania (Buffer lub string), NIE ponownie zserializowany JSON
export function verifyZaksioSignature(secret, header, rawBody, toleranceSeconds = 300) {
  const parts = header.split(",").map((p) => p.trim());
  const timestamp = Number(parts.find((p) => p.startsWith("t="))?.slice(2));
  if (!Number.isInteger(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
  const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest();
  return parts
    .filter((p) => p.startsWith("v1="))
    .some((p) => {
      const given = Buffer.from(p.slice(3), "hex");
      return given.length === expected.length && timingSafeEqual(given, expected);
    });
}
Python
import hashlib
import hmac
import time


def verify_zaksio_signature(secret: str, header: str, raw_body: bytes, tolerance: int = 300) -> bool:
    parts = [p.strip() for p in header.split(",")]
    try:
        timestamp = int(next(p[2:] for p in parts if p.startswith("t=")))
    except (StopIteration, ValueError):
        return False
    if abs(time.time() - timestamp) > tolerance:
        return False
    signed = f"{timestamp}.".encode() + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return any(
        hmac.compare_digest(p[3:], expected) for p in parts if p.startswith("v1=")
    )
PHP
<?php
function verifyZaksioSignature(string $secret, string $header, string $rawBody, int $tolerance = 300): bool
{
    $timestamp = null;
    $signatures = [];
    foreach (array_map('trim', explode(',', $header)) as $part) {
        if (str_starts_with($part, 't=')) {
            $timestamp = (int) substr($part, 2);
        } elseif (str_starts_with($part, 'v1=')) {
            $signatures[] = substr($part, 3);
        }
    }
    if ($timestamp === null || abs(time() - $timestamp) > $tolerance) {
        return false;
    }
    $expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
    foreach ($signatures as $signature) {
        if (hash_equals($expected, $signature)) {
            return true;
        }
    }
    return false;
}

// $rawBody = file_get_contents('php://input');
// $header = $_SERVER['HTTP_ZAKSIO_SIGNATURE'] ?? '';
Bash (openssl)
#!/usr/bin/env bash
# Sprawdzenie podpisu z wiersza polecen (do diagnozy): SECRET, HEADER i plik z surowa trescia zadania
SECRET="whsec_..."
HEADER="t=1760261400,v1=..."   # naglowek Zaksio-Signature
BODY_FILE="body.json"          # surowa tresc zadania, bajt w bajt

T=$(echo "$HEADER" | tr ',' '\n' | sed -n 's/^t=//p')
EXPECTED=$( (printf '%s.' "$T"; cat "$BODY_FILE") | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
if echo "$HEADER" | tr ',' '\n' | grep -qx "v1=$EXPECTED"; then echo "podpis poprawny"; else echo "podpis NIEPOPRAWNY"; fi
# pamietaj tez o sprawdzeniu, ze t= nie jest starsze niz 5 minut

Dostawa i ponowienia

  • Dostawa jest udana po odpowiedzi 2xx w ciągu 10 sekund. Przekierowania nie są obsługiwane (traktujemy je jako błąd).
  • Po błędzie ponawiamy po 1 minucie, 5 minutach, 30 minutach, 2, 6 i 12 godzinach (łącznie 7 prób). Potem dostawa ma stan „nie udało się”; możesz ją ponowić ręcznie w dzienniku dostaw.
  • Po 25 nieudanych próbach z rzędu subskrypcja jest wyłączana automatycznie.
  • Dostawa jest „co najmniej raz”: to samo zdarzenie może przyjść więcej niż raz, a kolejność nie jest gwarantowana. Rozpoznawaj zdarzenia po polu id i odpowiadaj szybko (2xx), a ciężką obróbkę rób asynchronicznie.
  • Zdarzenia o stanie produktu mogą zlewać się: kilka zmian zapisanych w krótkim czasie daje jedno zdarzenie z aktualnym stanem.
  • Adresy wewnętrzne, lokalne i prywatne są blokowane (ochrona przed SSRF), także po rozwiązaniu nazwy DNS.

Historia zmian

1.0.0: Pierwsza wersja API v1 (12 października 2026)

  • Uwierzytelnianie kluczem API (Authorization: Bearer zk_live_...), zakresy, lista adresów IP, wygasanie, rotacja z okresem przejściowym i odwołanie.
  • Produkty: lista, szczegóły, dodawanie i zmiana częściowa.
  • Stany magazynowe: lista i szczegóły produktu (suma oraz podział na magazyny).
  • Kontrahenci: lista, szczegóły, dodawanie, zmiana częściowa i usuwanie.
  • Faktury sprzedażowe: lista, szczegóły, PDF, wystawianie (od razu albo ze szkicu) z numeracją i księgowaniem jak w aplikacji.
  • Idempotency-Key na zapisach, paginacja kursorowa, błędy w formacie RFC 9457 oraz nagłówki limitów RateLimit-*.
  • Webhooki: faktura wystawiona i opłacona, kontrahent i produkt dodany lub zmieniony, zmiana stanu produktu; podpis HMAC-SHA256 ze znacznikiem czasu, ponowienia z narastającym odstępem, dziennik dostaw i test połączenia.
  • Zamówienia i przesyłki: zakresy są zarezerwowane, adresy pojawią się razem z modułem zamówień (nie są częścią tej wersji).