Przejdź do głównej zawartości

Kontrakt API

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.

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 Opcjonalny token. Gdy ustawiony, Sancho wysyła nagłówek Authorization: Bearer <token>.
OCR_POLL_INTERVAL_MS Odstęp między odpytaniami. Domyślnie 3000.
OCR_POLL_MAX_ATTEMPTS Maksymalna liczba odpytań. Domyślnie 60 (czyli 3 minuty 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, a także budżet odpytywania (OCR_POLL_INTERVAL_MS × OCR_POLL_MAX_ATTEMPTS) równy lub dłuższy niż 10 minut.

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.

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.

Pole data musi być zgodne ze schematem JSON odpowiadającym przesłanemu documentType:

  • purchase-order.schema.jsondocumentType: purchase_order
  • goods-received-source.schema.jsondocumentType: 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) oraz deliveryCostNet — koszt transportu, zwracany zamiast pozycji usługowej w items

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ęte items jak [].
  • 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.