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 | rejectedFaktura 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