Dokumentacja API

Dokumentacja API Notaly

Wszystko, czego potrzebujesz, aby wystawiać e-faktury w KSeF z kodu: uwierzytelnianie, obiekt faktury, statusy, pobieranie plików, podpisane webhooki i błędy. JSON przez HTTPS, jeden klucz API.

Aktualizacja 8 min czytania

Adres bazowy i uwierzytelnianie

Wszystkie żądania trafiają do https://api.getnotaly.com przez HTTPS, z treścią w JSON. Uwierzytelniasz się kluczem API w jednym z nagłówków:

headers
Authorization: Bearer $NOTALY_API_KEY
# lub
X-API-Key: $NOTALY_API_KEY

Klucz dostajesz przy rejestracji; w panelu tworzysz, rotujesz i unieważniasz klucze. Pełny klucz jest pokazywany raz, a przechowujemy tylko jego skrót. Limit to 60 żądań na minutę na klucz; powyżej API odpowiada 429 z Retry-After: 60.

Utwórz fakturę

POST/invoices

Waliduje dane, generuje XML FA(3) i wysyła go do KSeF. Zwraca 201 z obiektem faktury. Wyślij nagłówek Idempotency-Key, aby ponowienia były bezpieczne: ten sam klucz zwraca pierwotną fakturę zamiast tworzyć nową.

PoleTypUwagi
numberstringTwój numer faktury, np. FV/2026/10/001
issueDatestringYYYY-MM-DD
currencystringISO 4217, np. PLN
seller, buyerobjectnip (10 cyfr, poprawna suma kontrolna), name, address.line1, opcjonalnie address.line2, address.country (ISO 3166 alpha-2)
items[]array1–1000 pozycji: name, opcjonalnie unit, quantity, unitNetPrice, vatRate (23, 8, 5 lub 0)
POST/invoices201 Created
{
  "id": "inv_8f2c41d0a9b34e7c9d1e2f3a4b5c6d7e",
  "status": "submitted",
  "number": "FV/2026/10/001",
  "ksefReferenceNumber": "20261012-EE-2A41B3C7D9-1F",
  "ksefNumber": null,
  "upo": null,
  "pdfUrl": "/invoices/inv_8f2c…/pdf",
  "error": null,
  "history": [ { "status": "draft", "at": "…", "source": "api" },
               { "status": "submitted", "at": "…", "source": "ksef" } ],
  "createdAt": "2026-10-12T07:23:07.000Z",
  "updatedAt": "2026-10-12T07:23:08.000Z"
}

Gdy KSeF jest niedostępny, faktura zostaje zapisana jako draft z "error": "ksef_unavailable" i żadne dane nie giną.

Pobierz fakturę

GET/invoices/{id}

Zwraca ten sam obiekt faktury. Gdy faktura ma status submitted, wywołanie sprawdza też KSeF i zapisuje decyzję, więc odpytywanie jest poprawną alternatywą dla webhooków.

StatusZnaczenie
draftXML wygenerowany, jeszcze nie wysłany do KSeF
submittedWysłana do KSeF; pozostaje tu, dopóki KSeF ją przetwarza
acceptedPrzyjęta: ksefNumber i upo są ustawione
rejectedOdrzucona: error zawiera stały kod, np. ksef_rejected:<code>

Pobieranie XML i PDF

GET/invoices/{id}/xml

Dokładny XML FA(3) wysłany do KSeF (application/xml). Jego strukturę opisuje poradnik XML FA(3).

GET/invoices/{id}/pdf

Wizualizacja PDF (application/pdf) z kodem QR do weryfikacji w KSeF (KOD I). Fakturą w sensie prawnym jest XML; PDF jest dla ludzi.

Webhooki

Dodaj endpoint HTTPS w panelu, w sekcji Webhooki. Sekret do podpisu jest pokazywany raz. Notaly wysyła POST, gdy faktura zmienia stan:

ZdarzenieKiedy
invoice.acceptedKSeF przyjął fakturę; zawiera ksefNumber, upo i bezwzględny pdfUrl
invoice.rejectedKSeF odrzucił fakturę; error zawiera kod
invoice.errorNie udało się przetworzyć faktury
POSTTwój endpointinvoice.accepted
{
  "id": "evt_inv_8f2c…_accepted",
  "type": "invoice.accepted",
  "createdAt": "2026-10-12T07:24:02.000Z",
  "data": { "invoiceId": "inv_8f2c…", "status": "accepted",
            "ksefReferenceNumber": "20261012-EE-…", "ksefNumber": "2745013933-20261012-…",
            "upo": "…", "error": null, "pdfUrl": "https://api.getnotaly.com/invoices/inv_8f2c…/pdf" }
}

Weryfikacja podpisu

Każda dostawa zawiera X-Webhook-Id, X-Webhook-Timestamp i X-Webhook-Signature: t=<unix>,v1=<hex>, gdzie v1 to HMAC-SHA256 z "<t>.<surowe body>" z sekretem endpointu jako kluczem.

verify.ts
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret: string, rawBody: string, header: string): boolean {
  const p = Object.fromEntries(header.split(",").map((x) => x.split("=", 2)));
  const t = Number(p.t);
  if (!p.v1 || Math.abs(Date.now() / 1000 - t) > 300) return false; // okno 5 minut
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const got = Buffer.from(p.v1, "hex");
  return got.length === expected.length && timingSafeEqual(got, expected);
}

Ponowienia i dostarczanie

  • Potwierdź dowolnym kodem 2xx. Każdy inny status, przekierowanie lub timeout powoduje ponowienie.
  • Do 8 prób z wykładniczym odstępem (30 s, 60 s, 2 min… maksymalnie 1 godzina, z losowym rozrzutem). Treść i id zdarzenia są identyczne przy każdej próbie.
  • Dostarczanie „co najmniej raz”: deduplikuj po X-Webhook-Id.
  • Endpointy muszą być publicznymi adresami HTTPS; adresy prywatne i wewnętrzne są odrzucane.

Błędy

Błędy to JSON ze stałym kodem error. Surowe komunikaty KSeF nigdy nie są zwracane.

HTTPerrorCo zrobić
401unauthorizedBrak klucza API lub klucz nieprawidłowy
402quota_exceededSandbox wykorzystał 3 faktury testowe; przejdź na Starter lub Scale
404not_foundNieznane id lub faktura utworzona innym kluczem API
422validation_failedPopraw pola wymienione w details
422seller_nip_not_authorizedW sandboxie seller.nip musi mieć wartość 2745013933
429rate_limitedOdczekaj Retry-After sekund

Kody na poziomie faktury w polu error: ksef_unavailable (KSeF nie odpowiedział; faktura pozostaje draft), ksef_rejected:<code> (odrzucona przez KSeF) i ksef_error.

Sandbox i plany

PlanCenaŚrodowisko KSeFFaktury
SandboxDarmowyTEST, NIP sprzedawcy 27450139333 łącznie
Starter49 €/mcTwoje połączenie z KSeFDo 200 miesięcznie
Scale199 €/mcTwoje połączenie z KSeFDo 1 000 miesięcznie

Najczęstsze pytania

Jak się uwierzytelnić?

Wyślij klucz API jako Authorization: Bearer <klucz> (lub X-API-Key: <klucz>). Klucze tworzysz przy rejestracji i w panelu; pełny klucz jest pokazywany raz.

Czy mogę bezpiecznie ponowić POST /invoices?

Tak. Wyślij nagłówek Idempotency-Key; powtórzenie żądania z tym samym kluczem zwróci pierwotną fakturę zamiast tworzyć nową.

Jakie są statusy faktury?

draft → submitted → accepted | rejected. Gdy KSeF przetwarza wysyłkę, faktura pozostaje w statusie submitted.

Jak zweryfikować podpis webhooka?

Oblicz HMAC-SHA256 z "<t>.<surowe body>" sekretem endpointu, porównaj w stałym czasie z v1 z nagłówka X-Webhook-Signature i odrzuć znaczniki czasu starsze niż 5 minut.

Jakie są limity sandboxa?

3 darmowe faktury testowe w KSeF TEST na konto, z NIP sprzedawcy 2745013933. Więcej w planie Starter (49 €/mc) lub Scale (199 €/mc).

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.