Przewodnik integracji

Integracja KSeF API w Node.js

Jak wysłać fakturę KSeF FA(3) z Node.js przez API Notaly: POST /invoices, idempotencja, obsługa błędów i śledzenie statusu.

Aktualizacja

Wprowadzenie

W Node.js najszybciej wyślesz fakturę do KSeF bez ręcznego budowania pliku FA(3). Notaly przyjmuje zwykły JSON, sam generuje XML zgodny ze schemą, weryfikuje go i wysyła do KSeF. Poniżej znajdziesz gotowy, działający przykład oparty o wbudowane fetch, dostępne w Node.js od wersji 18 — niczego nie musisz instalować.

Co będzie potrzebne

Załóż darmowe konto sandbox i skopiuj klucz API z panelu. W środowisku testowym sprzedawca ma przypisany NIP 2745013933 — to testowy NIP powiązany z certyfikatem Notaly. Podanie innego NIP sprzedawcy kończy się błędem 422 seller_nip_not_authorized jeszcze zanim faktura trafi do KSeF, więc nie zużywa limitu trzech darmowych faktur testowych.

Nagłówek Idempotency-Key to dodatkowe zabezpieczenie przy ponawianiu żądań: gdy powtórzysz wysyłkę z tym samym kluczem, dostaniesz tę samą fakturę, a nie duplikat.

Wysyłka faktury

const NOTALY_API_KEY = process.env.NOTALY_API_KEY;

const faktura = {
  number: "FV/2026/10/001",
  issueDate: "2026-10-12",
  currency: "PLN",
  seller: { nip: "2745013933", name: "Example Sp. z o.o. (FICTIONAL)", address: { line1: "ul. Testowa 1, 00-001 Warszawa", country: "PL" } },
  buyer: { nip: "1234563218", name: "Test Customer S.A. (FICTIONAL)", address: { line1: "ul. Probna 2, 30-001 Kraków", country: "PL" } },
  items: [
    { name: "SaaS subscription - October", unit: "pcs.", quantity: 1, unitNetPrice: 1000, vatRate: 23 },
    { name: "Support", unit: "hrs.", quantity: 2, unitNetPrice: 150.5, vatRate: 8 }
  ]
};

const response = await fetch("https://api.getnotaly.com/invoices", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${NOTALY_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID()
  },
  body: JSON.stringify(faktura)
});

if (!response.ok) {
  const body = await response.json();
  console.error(`${response.status}:`, body.error, body.details ?? body.message ?? "");
  process.exit(1);
}

const utworzona = await response.json();
console.log(`Faktura ${utworzona.id} ma status ${utworzona.status}`);

Co zwraca API

Poprawna wysyłka kończy się kodem 201 i obiektem faktury. Pole status przyjmuje wartość submitted, gdy KSeF przyjął zgłoszenie, albo draft, gdy system był chwilowo niedostępny i faktura została zapisana do późniejszego ponowienia. W odpowiedzi znajdziesz też id (służy do sprawdzania statusu), ksefReferenceNumber, a po akceptacji — upo i pdfUrl. Notaly weryfikuje i wysyła fakturę; stawki VAT i klasyfikacja podatkowa pozostają po Twojej stronie.

Błędy i co z nimi zrobić

Notaly zwraca ustrukturyzowane błędy, więc nie parsuj tekstów, tylko kody. 422 validation_failed zawiera tablicę details z opisem, które pole jest błędne. 402 quota_exceeded oznacza wykorzystanie trzech darmowych faktur sandbox. 401 to brakujący lub błędny klucz, 404 to poprawny klucz, ale cudzy zasób. 503 ksef_unavailable jest przejściowy — warto ponowić z wycofaniem.

Śledzenie statusu

const res = await fetch(`https://api.getnotaly.com/invoices/${utworzona.id}`, {
  headers: { Authorization: `Bearer ${NOTALY_API_KEY}` }
});
const faktura = await res.json();
console.log(faktura.status); // draft | submitted | accepted | rejected

Faktura przechodzi przez etapy draft → submitted → accepted lub rejected. Możesz odpytywać GET /invoices/:id albo skonfigurować webhooki, by Notaly sam powiadamiał o każdej zmianie statusu.

Źródła

Źródło: getnotaly.com

Najczęstsze pytania

Której wersji Node.js potrzebuję?

Node.js 18 lub nowszej — przykład korzysta z wbudowanego fetch. Starsze wersje mogą użyć node-fetch albo axios.

Czy mogę wysłać kilka pozycji na jednej fakturze?

Tak. Tablica items przyjmuje do 1000 pozycji, każda z nazwą, ilością, ceną netto i stawką VAT.

Wystaw pierwszą fakturę testową w KSeF jeszcze dziś

Załóż konto sandbox: klucz API w kilka sekund, 3 darmowe faktury testowe w KSeF TEST, bez karty płatniczej.