Kontrakt API
Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.
Sancho rozpoznaje dokumenty przez wymienny backend OCR. Domyślnie jest to Parseur, ale wdrożenie self-hosted może wskazać własną usługę — wystarczy, że wystawi dwa endpointy HTTP opisane poniżej.
Jeśli dopiero zaczynasz implementację, zacznij od przewodnika Jak zbudować backend OCR, a tutaj wracaj po szczegóły.
Konfiguracja
Section titled “Konfiguracja”Zmienne środowiskowe API:
| Zmienna | Opis |
|---|---|
OCR_PROVIDER |
parseur (domyślnie) albo custom. |
OCR_CUSTOM_BASE_URL |
Adres bazowy usługi, np. https://ocr.firma.local. Wymagany dla custom. |
OCR_CUSTOM_SUBMIT_PATH |
Ścieżka endpointu przyjmującego dokument. Domyślnie /ocr/documents. |
OCR_CUSTOM_STATUS_PATH |
Ścieżka endpointu statusu; identyfikator zlecenia doklejany jest na końcu. Domyślnie /ocr/documents. |
OCR_CUSTOM_API_KEY |
Token wysyłany w nagłówku Authorization. Wymagany przy bearer i raw, nieużywany przy none i jwt_hs256. |
OCR_CUSTOM_AUTH_SCHEME |
none, bearer (domyślnie), raw albo jwt_hs256. Patrz Uwierzytelnianie. |
OCR_CUSTOM_JWT_SECRET |
Sekret współdzielony z usługą OCR, minimum 24 znaki. Wymagany dla jwt_hs256. |
OCR_CUSTOM_JWT_ISSUER |
Wartość iss tokenu. Domyślnie adres API tego wdrożenia. |
OCR_CUSTOM_JWT_AUDIENCE |
Wartość aud tokenu. Domyślnie host z OCR_CUSTOM_BASE_URL. |
OCR_CUSTOM_JWT_TTL_S |
Czas życia tokenu w sekundach. Domyślnie 300, zakres 180–3600 — token musi przeżyć całą wysyłkę, którą uwierzytelnia. |
OCR_POLL_INTERVAL_MS |
Odstęp między odpytaniami. Domyślnie 3000. |
OCR_POLL_MAX_ATTEMPTS |
Maksymalna liczba odpytań. Domyślnie 200 (czyli 10 minut przy domyślnym odstępie). |
Adres jest sklejany dosłownie: OCR_CUSTOM_BASE_URL + ścieżka. Baza może więc zawierać własny prefiks (https://ocr.firma.local/api) i nie zostanie on utracony.
Niepoprawna konfiguracja zatrzymuje start API: custom bez OCR_CUSTOM_BASE_URL, parseur bez PARSEUR_API_KEY, bearer albo raw bez OCR_CUSTOM_API_KEY, jwt_hs256 bez sekretu, a także budżet odpytywania (OCR_POLL_INTERVAL_MS × OCR_POLL_MAX_ATTEMPTS) dłuższy niż 10 minut.
Uwierzytelnianie
Section titled “Uwierzytelnianie”OCR_CUSTOM_AUTH_SCHEME wybiera jeden z czterech trybów:
| Tryb | Nagłówek Authorization |
|---|---|
none |
brak nagłówka — dla usługi, która niczego nie sprawdza |
bearer (domyślny) |
Bearer <OCR_CUSTOM_API_KEY> |
raw |
<OCR_CUSTOM_API_KEY> — dla bram dopasowujących nagłówek dosłownie, jak Header Auth w n8n |
jwt_hs256 |
Bearer <token> — token HS256 podpisywany od nowa przy każdym żądaniu |
Wybór jest walidowany przy starcie: bearer i raw wymagają OCR_CUSTOM_API_KEY, a jwt_hs256 — sekretu (wystawca i odbiorca mają wartości domyślne). Usługę bez uwierzytelniania trzeba zadeklarować jawnie przez none; brak tokenu nie jest traktowany jak jego wyłączenie.
Nagłówek trafia do obu endpointów: przyjęcia dokumentu i statusu.
Podpisywany token (jwt_hs256)
Section titled “Podpisywany token (jwt_hs256)”Sancho podpisuje token sekretem współdzielonym z usługą OCR (HMAC-SHA256), a usługa weryfikuje go tym samym sekretem. Ładunek:
{ "iss": "https://app.firma.local", "aud": "ocr.firma.local", "sub": "sancho-ocr-client", "iat": 1774694400, "exp": 1774694700, "jti": "b8f3c1de-9d4e-4a51-8b0c-1f2e3d4a5b6c"}iss i aud są wypełniane automatycznie — adresem API tego wdrożenia i hostem usługi OCR — dopóki nie ustawisz ich jawnie. Nagłówek JOSE to {"alg":"HS256","typ":"JWT"}. Token żyje OCR_CUSTOM_JWT_TTL_S sekund (domyślnie 300) i nie jest cache’owany — każde żądanie, łącznie z każdym odpytaniem o status, dostaje świeży.
Sekret jest symetryczny: obie strony mogą wystawić ważny token. Krótki czas życia chroni przed powtórzeniem przechwyconego żądania, ale nie przed wyciekiem samego sekretu — traktuj go jak hasło. Minimum 24 znaki — krótszy zatrzymuje start API. Zalecane 32 (openssl rand -hex 16). Sama długość niczego nie gwarantuje: sekret musi być losowy, bo zgadywalny daje się złamać offline z jednego przechwyconego tokenu.
Konfiguracja w n8n
Section titled “Konfiguracja w n8n”W węźle Webhook ustaw Authentication: JWT Auth i utwórz poświadczenie:
- Key Type —
Passphrase - Secret — ten sam sekret co
OCR_CUSTOM_JWT_SECRET - Algorithm —
HS256
Poświadczenie podepnij do obu węzłów Webhook — n8n weryfikuje uwierzytelnienie osobno w każdym z nich.
n8n sprawdza wyłącznie podpis oraz exp. Pola iss, aud, sub i jti są wysyłane, ale nie są weryfikowane; jeśli mają obowiązywać, dołóż jawne sprawdzenie w samym przepływie.
Tryb raw jest niezgodny z JWT Auth: n8n czyta token jako drugi człon nagłówka (authHeader.split(" ")[1]), więc token bez prefiksu Bearer kończy się błędem 401.
Kody błędów i rotacja klucza
Section titled “Kody błędów i rotacja klucza”Błąd uwierzytelnienia zwracaj jako 401 (brak tokenu) albo 403 (token niepoprawny) — dokładnie tak zachowuje się wbudowana weryfikacja n8n. Nie zwracaj 5xx: Sancho ponawia odpytania po 5xx, więc dokument zużyłby cały budżet, zanim zgłosi błąd.
Poświadczenie n8n przechowuje dokładnie jeden sekret, więc nie da się przyjmować starego i nowego równocześnie. Wymiana jest jednoczesna: w oknie serwisowym podmień sekret w n8n i OCR_CUSTOM_JWT_SECRET w Sancho. Dokumenty wysłane w trakcie wymiany skończą się błędem i trzeba je powtórzyć.
Endpoint 1 — przyjęcie dokumentu
Section titled “Endpoint 1 — przyjęcie dokumentu”POST {OCR_CUSTOM_BASE_URL}{OCR_CUSTOM_SUBMIT_PATH}
Żądanie multipart/form-data:
| Pole | Opis |
|---|---|
file |
Zawartość dokumentu (PDF lub obraz); nazwa pliku i typ MIME są zachowane. |
documentType |
purchase_order albo goods_received_source. |
Odpowiedź 202 (dowolny kod 2xx jest akceptowany):
{ "id": "3f1c8b12-9d4e-4a51-8b0c-1f2e3d4a5b6c", "status": "pending" }id to dowolny identyfikator unikalny w obrębie usługi — najlepiej UUID. Musi być nieprzezroczysty i bezpieczny w URL: trafia do ścieżki endpointu statusu, więc znaki takie jak / czy spacja zostaną zakodowane procentowo (%2F, %20), a wiele serwerów odrzuca %2F w segmencie ścieżki. Nie używaj numerów dokumentów.
status przyjmuje wartości pending, processing, queued, ready lub error; pominięcie pola oznacza pending. Zwrócenie ready już tutaj jest dozwolone — Sancho i tak wykona jedno odpytanie, bo odpowiedź na przyjęcie nie zawiera pola data.
Sancho nigdy nie ponawia tego żądania automatycznie — powtórka oznaczałaby drugie zlecenie dla tego samego dokumentu.
Endpoint 2 — odpytanie o wynik
Section titled “Endpoint 2 — odpytanie o wynik”GET {OCR_CUSTOM_BASE_URL}{OCR_CUSTOM_STATUS_PATH}/{id}
W trakcie przetwarzania:
{ "id": "3f1c8b12-9d4e-4a51-8b0c-1f2e3d4a5b6c", "status": "processing" }Po zakończeniu:
{ "id": "3f1c8b12-9d4e-4a51-8b0c-1f2e3d4a5b6c", "status": "ready", "data": { "documentNumber": "ZAM/1/2026", "items": [] }}Przy błędzie:
{ "id": "3f1c8b12-9d4e-4a51-8b0c-1f2e3d4a5b6c", "status": "error", "message": "Nie udało się odczytać dokumentu" }Odpowiedź spoza zakresu 2xx (w tym 404 dla nieznanego id) jest traktowana jak awaria połączenia: Sancho ponawia odpytanie w ramach pozostałego budżetu, a gdy ten się wyczerpie — oznacza dokument jako błędny.
Format wyniku
Section titled “Format wyniku”Pole data musi być zgodne ze schematem JSON odpowiadającym przesłanemu documentType:
purchase-order.schema.json—documentType: purchase_ordergoods-received-source.schema.json—documentType: goods_received_source— dowolny dokument źródłowy przyjęcia: WZ, faktura zakupu, dowód dostawy. Oprócz nagłówka i pozycji schemat zawiera klasyfikacjękind(invoice/goodsIssuedNote/other), ceny pozycji (priceNet,valueNet,vatRate) orazdeliveryCostNet— koszt transportu, zwracany zamiast pozycji usługowej witems
Zasady wspólne dla obu schematów:
- Wszystkie pola są opcjonalne — pomiń to, czego parser nie odczytał.
- Pominięta wartość skalarna jest traktowana jak
null, pominięteitemsjak[]. - Nieznane klucze są ignorowane.
- Daty to napisy w formacie
YYYY-MM-DD. - Liczby to liczby JSON, nie napisy.
- NIP-y najlepiej podawać jako same cyfry — Sancho i tak usuwa spacje oraz myślniki.
Dokument, którego wynik nie przejdzie walidacji, otrzymuje status błędu (SchemaValidationError) i nie trafia do dalszego przetwarzania.