AI Budget Assistant i eKsięgowy AI (Accounting Agent AI nie tylko odpowiada — planuje kolejne kroki i sam korzysta z narzędzi: pyta systemy, czyta dokumenty i dopiero potem wykonuje zadanie. Pełna definicja → z eksiegowyai.pl) to dwa osobne produkty. Pierwszy prowadzi finanse osobiste, rodzinne i firmowe: wydatek dyktuje się głosem, robi zdjęcie paragonu albo przechwytuje z powiadomienia banku. Drugi to agent księgowy dla polskich firm, pracujący na danych z wFirma i KSeF to obowiązkowy w Polsce Krajowy System e-Faktur do wystawiania i odbierania faktur bezpośrednio z organami skarbowymi. Pełna definicja →. Użytkownicy, którzy mają oba, prosili o rzecz pozornie prostą: niech firmowe wydatki zapisane w AI Budget trafiają do księgowości — i niech prywatne zostaną prywatne.

W tym artykule opisujemy, jak to zbudowaliśmy: własny serwer OAuth w AI Budget, jedno połączenie obejmujące kilka kont, stronę księgową z uzgadnianiem wydatków z dokumentami kosztowymi i — najobszerniej — to, co znalazły przeglądy kodu i audyty bezpieczeństwa, zanim cokolwiek trafiło do produkcji. Wszystkie opisane poprawki są już wdrożone.

Dlaczego publiczne API, a nie integracja na skróty

Najkrótsza droga byłaby taka: dwa nasze serwery, wspólny sekret, prywatny endpoint. Wybraliśmy dłuższą. AI Budget dostało otwarte, darmowe i udokumentowane API, z którego może skorzystać każdy programista: aplikację rejestruje się samodzielnie, bez ręcznej weryfikacji. eKsięgowy AI jest po prostu jego pierwszym klientem — traktowanym jako aplikacja własna (znaczek weryfikacji, wyższe limity), ale idącym tą samą drogą co wszyscy inni.

Ta decyzja ustawiła kilka kolejnych. Użytkownik łączy konta przyciskiem «Połącz AI Budget» w standardowym przepływie OAuth. Pierwsza faza obejmuje tylko wydatki — przychody, budżety i cele przyjdą później. Zamiast webhooków klient odpytuje kanał zmian. Połączyć konto może wyłącznie jego właściciel lub edytor. Dokumentacja jest po angielsku, polsku i rosyjsku.

Najważniejsze wymaganie było jednak produktowe, nie techniczne: księgowość musi widzieć wydatki firmy ściśle oddzielone od prywatnych. Cała reszta architektury z niego wynika.

Własny serwer OAuth w AI Budget

Serwer autoryzacji działa wewnątrz API to interfejs, przez który dwa systemy wymieniają dane automatycznie — bez eksportów do Excela i przeklejania ręką. Pełna definicja → AI Budget (NestJS), bez zewnętrznego dostawcy tożsamości. Używamy przepływu authorization code, a PKCE z metodą S256 jest obowiązkowe dla każdego klienta — zarówno publicznego, jak i poufnego, który ma sekret. Ekran zgody mieszka w aplikacji, pod app.ai-budget.pl/authorize, i ma układ zarówno na telefon, jak i na szeroki ekran przeglądarki.

Tokeny są nieprzezroczyste i przechowywane wyłącznie jako skróty SHA-256 — wyciek bazy nie daje działających Token to fragment tekstu, zwykle kawałek słowa, w którym modele AI mierzą i długość zapytania, i koszt — rachunek wystawiany jest za tokeny, nie za pytania. Pełna definicja →. Mają rozpoznawalne prefiksy: aba_at_ dla tokenu dostępu, aba_rt_ dla odświeżania, aba_cs_ dla sekretu klienta i aba_ci_ dla identyfikatora. Prefiks pozwala skanerom sekretów i ludziom od razu rozpoznać, co wyciekło. Token dostępu żyje godzinę, token odświeżania 90 dni i jest rotowany przy każdym użyciu. Jeśli ktoś przedstawi już zużyty token odświeżania, traktujemy to jako sygnał kradzieży i unieważniamy całe połączenie.

Kody autoryzacyjne leżą w Redisie i są konsumowane operacją GETDEL w trybie fail-closed: kod da się wymienić dokładnie raz, nawet gdy dwa żądania ścigają się o niego w tej samej milisekundzie.

Zakresy działają w trybie deny-by-default: expenses:read, expenses:write, receipts:read i categories:read. Trasa, która nie deklaruje wymaganego zakresu, jest odrzucana — zapomniany dekorator oznacza odmowę, a nie otwarte drzwi. Do tego rola osoby, która udzieliła zgody, jest sprawdzana przy każdym żądaniu: jeśli właściciel zdegraduje ją do obserwatora, następne wywołanie dostaje 403 grant_inactive.

Całość stoi za jedną flagą, PUBLIC_API_ENABLED, domyślnie wyłączoną — wtedy każda trasa odpowiada 404. Włączyliśmy ją 2 listopada 2026, po zmianie w nginx: osobna strefa limitów dla /public/ i /oauth/ oraz nagłówki frame-ancestors 'none' i X-Frame-Options DENY na hoście aplikacji, żeby ekranu zgody nie dało się osadzić w cudzej ramce i wyłudzić kliknięcia (clickjacking). Limity liczymy osobno na połączenie i na aplikację: domyślnie 120 żądań na minutę na połączenie i 1200 na aplikację, dla aplikacji zweryfikowanych 600 i 10 000.

Kanał zmian zamiast webhooków

Synchronizacja działa przez /public/v1/expenses/changes: paginację po kluczu (updated_at, id), znaczniki usunięcia (tombstones) dla skasowanych wydatków i 10-sekundowe opóźnienie bezpieczeństwa liczone zegarem bazy danych. Bez niego wolno zatwierdzana transakcja mogłaby zostać pominięta: dostaje znacznik czasu w chwili zapisu, ale staje się widoczna później — gdy kursor klienta zdążył już przejść dalej. Opóźnienie sprawia, że kanał oddaje tylko wiersze na tyle stare, że wszystko, co je poprzedza, jest już zatwierdzone. Liczenie go zegarem bazy, a nie serwera aplikacji, usuwa z równania rozjazd zegarów.

Drugi szczegół: edycja wiersza podrzędnego — tagu, projektu, podziału na kategorie, pozycji paragonu — podbija updatedAt wydatku-rodzica, żeby kanał dostarczył go ponownie. Inaczej klient nigdy by się nie dowiedział, że wydatek zmienił kategorię w podziale.

Tworzenie wydatku przyjmuje nagłówek Idempotency-Key, którego zakres to klient (a od drugiej fazy także konto), więc ponowiona po błędzie sieci prośba nie tworzy duplikatu.

Konta szyfrowane, dokumentacja i ekrany

AI Budget ma poziomy szyfrowania end-to-end tekstów. Konta na poziomie 2 i wyższym w ogóle nie dają się połączyć. Konta z poziomem 1 są dostępne tylko do odczytu, a każde pole szyfrowane na tym poziomie wraca jako null. Lista tych pól jest przypięta testem do rejestru pól szyfrowania aplikacji — jeśli ktoś doda nowe pole szyfrowane, test się wywróci, zanim API zacznie je zwracać jako szyfrogram.

Dokumentacja OpenAPI (Swagger) jest pod /public/docs/en, /public/docs/pl i /public/docs/ru, obok przewodnik w trzech językach; testy pilnują, żeby wszystkie wersje miały te same klucze. W ustawieniach aplikacji są ekrany «Deweloperzy» i «Połączone aplikacje», użytkownik dostaje e-mail o połączeniu aplikacji (najwyżej raz dziennie dla pary aplikacja i konto), a administrator ma stronę aplikacji API: weryfikację, limity i blokadę.

Jedno połączenie, kilka kont

Pierwsza faza zakładała jedno połączenie na jedno konto. Księgowość potrzebuje więcej: konta firmowego i prywatnych jednocześnie, wybranych raz, na ekranie zgody, polami wyboru.

W danych to tabela łącząca oauth_grant_accounts (zgoda ↔ konto) oraz jedna aktywna zgoda na parę (klient, użytkownik), wymuszona częściowym indeksem unikalnym. Migracja przepisała istniejące wiersze i scaliła zdublowane zgody. Tu przegląd bezpieczeństwa złapał subtelny błąd pierwszej wersji: sumowanie zakresów scalanych zgód po cichu rozszerzyłoby zgodę tylko do odczytu o zapis. Ocalała zgoda zachowuje więc własne zakresy.

Konto wskazuje się nagłówkiem X-Account-Id, a reguła liczy konta objęte zgodą, nie konta aktualnie używalne:

Dlaczego liczymy konta przyznane, a nie aktywne? Gdyby jedno z dwóch kont stało się chwilowo niedostępne, reguła «aktywnych» uznałaby, że zostało jedno, i zaczęła po cichu kierować żądania bez nagłówka do niego. Żądanie nigdy nie może wylądować w koncie, którego integrator nie wskazał.

GET /public/v1/accounts zwraca dla każdego konta: id, nazwę, typ, walutę bazową, dostęp (read lub read_write), flagę aktywności i powód nieaktywności. To typ pozwala księgowości automatycznie odróżnić firmę od prywatnych. Nazwa, typ i waluta znikają, gdy użytkownik przestaje być członkiem konta. Odpowiedź tokenowa zawsze niesie account_ids, a account_id tylko przy połączeniu jednokontowym. Ponowna zgoda zaznacza już połączone konta i zastępuje cały zestaw.

Osobny przypadek to współwłaściciele. Współwłaściciel konta wspólnego widzi połączenie wyłącznie ze swoimi kontami — nigdy z prywatnymi kontami osoby, która go utworzyła. Jego «Odłącz» usuwa tylko jego konta; całe połączenie unieważnić może tylko autor zgody. Przepływ dwóch kont pokrywa test end-to-end na prawdziwym Postgresie i Redisie.

Przepływ połączenia: eKsięgowy AI wystawia adres autoryzacji ze state i PKCE, użytkownik zaznacza konta w AI Budget, a kod jest wymieniany dopiero po sprawdzeniu, że state należy do zalogowanego użytkownika.

Strona księgowa: trzy etykiety

Po stronie eKsięgowego AI każde konto dostaje jedną z trzech etykiet: firma, inna działalność albo prywatne. Etykieta ustawia się automatycznie z typu konta w AI Budget — jedno konto firmowe staje się «firmą», przy kilku użytkownik wybiera, reszta jest prywatna — i da się ją zmienić ręcznie. Dokładnie jedną «firmę» na połączenie wymusza częściowy indeks unikalny w bazie, nie kod aplikacji. «Inna działalność» jest widoczna w czacie, ale nigdy w panelu firmy ani w uzgadnianiu.

Prywatność: kopia tylko tego, co i tak trafi do ksiąg

Właściciel zadał pytanie, które ustawiło tę część: «czy kopia to nie utrata prywatności?». Odpowiedź brzmi: kopia tak, ale tylko tam, gdzie jest uzasadniona. Do bazy księgowej trafia wyłącznie konto firmowe — i tak skończy w księgach. Dane prywatne i «innej działalności» są czytane na żywo z AI Budget i trzymane w Redisie, zaszyfrowane AES-256-GCM, najwyżej 300 sekund. Nigdy nie są zapisywane w bazie, logowane ani zapamiętywane w pamięci długoterminowej agenta.

Lustro firmy synchronizuje się przez kanał zmian co 15 minut, na żądanie i przy otwarciu panelu: kursor per konto, znaczniki usunięcia i cotygodniowa pełna resynchronizacja, bo AI Budget nie zgłasza wydatku przeniesionego do innego konta. Zmiana etykiety z «firmy» na inną atomowo kasuje lustro. Tokeny są szyfrowane w spoczynku, odświeżane tylko pod blokadą per użytkownik w Redisie, a nowa para jest zapisywana przed czymkolwiek innym.

Uzgadnianie: «wydatki bez dokumentu»

Najbardziej użyteczna funkcja dla księgowości porównuje wydatki firmy od początku poprzedniego miesiąca do dziś z dokumentami kosztowymi z wFirma, pobieranymi na żywo. Dopasowanie oznacza tę samą walutę, kwotę brutto równą z dokładnością do 1 grosza i datę dokumentu w oknie ±7 dni. Dopasowanie jest jeden do jednego — każdy dokument pasuje najwyżej do jednego wydatku. Wydatki z mniejszą liczbą kandydatów dopasowujemy najpierw, żeby «łatwy» wydatek nie zabrał jedynego dokumentu «trudnemu». Remisy rozstrzyga podobieństwo nazw po normalizacji, z odrzuceniem form prawnych w rodzaju «sp. z o.o.» czy «S.A.».

Sformułowania są świadome: «nie znaleziono pasującego dokumentu», nigdy «brak faktury» — algorytm nie wie, czy faktura istnieje, wie tylko, że jej nie dopasował. Pozycję można odrzucić jako «dokument niepotrzebny». Długi i należności z podziału rachunku nie są wydatkami i są wykluczone wszędzie, a sumy kategorii idą za podziałem na kategorie.

Panel ma blok firmy na stronie głównej (ten miesiąc wobec poprzedniego, kategorie, karta uzgadniania) i osobną stronę «Prywatne — poza księgami firmy». Każdy nieznany stan jest nazwany wprost: niepołączone, wymaga ponownego połączenia, synchronizacja trwa, synchronizacja nieudana, brak wFirma. Nigdy zera i nigdy «wszystko dopasowane».

Czat: siedem narzędzi

Agent dostał 7 narzędzi: lista kont, pobranie wydatków, podsumowanie wydatków, utworzenie, edycja i usunięcie wydatku oraz wydatki bez dokumentu. Każda liczba w odpowiedzi jest podpisana kontem. Zapis wymaga jawnie wskazanego konta i potwierdzenia, konta szyfrowane (tylko do odczytu) odmawiają zapisu z wyjaśnieniem, a każde utworzenie niesie własny Idempotency-Key. W rozmowie udostępnionej organizacji narzędzia widzą tylko konto firmy; tak samo w grupowych czatach Telegrama.

Co znalazły przeglądy i jak to zamknęliśmy

Każde zadanie sprawdzał świeży recenzent, który nie widział, jak powstawało. Potem cała gałąź przeszła jeszcze jeden przegląd, a każde repozytorium — osobny audyt bezpieczeństwa. Poniżej wszystkie poważne znaleziska, każde w układzie: problem, dlaczego to ważne, poprawka.

ZnaleziskoCzym groziłoPoprawka
IDOR w /sync AI Budget (istniał wcześniej)Członek jednego konta mógł zmieniać powiązania i widzieć pozycję paragonu w cudzym koncieKażdy zapis przez konto rodzica; obce wiersze — «nie znaleziono»
Phishing zgody: state powiązany z tym, kto zacząłTokeny ofiary zapisane pod cudzym kontemWymiana kodu tylko na uwierzytelnionej stronie; state musi należeć do zalogowanego
Obrazki markdown w czacie, brak CSPPrzeglądarka sama wysłałaby prywatne sumy na obcy serwerBez zdalnych obrazków; czyszczenie każdego ciągu z AI Budget; wynik narzędzia to dane
Dane prywatne w śladach LangSmithPrywatne liczby w zewnętrznym magazyniePrywatność śladu ustalana przed przebiegiem głównym; wejścia i wyjścia wymazane
Zużyty token odświeżania po nieudanym zapisieAI Budget unieważniłby całe połączeniePonowienie pod blokadą, needs_reconnect, 60-sekundowy znacznik, compare-and-swap
Odłączenie w trakcie synchronizacjiNieaktualne wydatki na stałe w liczbach firmyNajpierw lokalne unieważnienie; SELECT … FOR UPDATE przy każdym zapisie lustra
Ciche przełączenie firmySkasowane lustro prawdziwej firmyKonto nieaktywne, ale widoczne, zachowuje etykietę
«Wszystko dopasowane» zamiast «nie wiadomo»Fałszywe «każdy wydatek ma dokument»Rozróżnione stany; lustro kompletne dopiero po pełnym przebiegu

1. IDOR w istniejącym /sync AI Budget

Problem. Przy okazji dodawania API przegląd znalazł błąd, który istniał wcześniej: w synchronizacji aplikacji wiersze relacji (wydatek ↔ tag, projekt, podział na kategorie) i pozycje paragonu były wyszukiwane po identyfikatorze bez sprawdzenia konta wywołującego.

Dlaczego to ważne. Członek dowolnego konta, znający identyfikatory z cudzego konta, mógł tam usuwać lub dopinać powiązania, a konflikt wersji zwracał mu pozycję paragonu z cudzego konta. Publiczne API otwierało dostęp do danych szerzej, więc to musiało być zamknięte przed startem.

Poprawka. Osobne zadanie (ABA-708): każdy zapis rozwiązuje wiersz przez konto rodzica, a wiersze obce odpowiadają «nie znaleziono» — tak samo jak nieistniejące.

2. Phishing zgody i wstrzyknięcie kodu autoryzacyjnego

Problem. State w OAuth był powiązany z użytkownikiem, który rozpoczął łączenie, a nie z przeglądarką, która je kończy. Ktoś mógł rozpocząć łączenie na własnym koncie w eKsięgowym AI i podesłać link autoryzacji ofierze.

Dlaczego to ważne. Gdyby ofiara zatwierdziła zgodę, jej tokeny AI Budget zostałyby zapisane pod kontem tej osoby — z dostępem do cudzych finansów i bez śladu po stronie ofiary poza jednym kliknięciem «Zezwól».

Poprawka. Callback API już nie wymienia kodu. Przekazuje code i state do strony w aplikacji webowej, która wysyła je POST-em na uwierzytelniony endpoint. Serwer pobiera state przez GETDEL i wymaga, żeby należał do zalogowanego użytkownika — w przeciwnym razie state_mismatch i nic nie jest zapisywane. Strona od razu usuwa code i state z adresu (history.replaceState), ma politykę no-referrer i jest wyłączona z analityki, żeby kod nie wyciekł do historii, nagłówka Referer ani narzędzi statystycznych.

3. Wyciek przez wstrzyknięty markdown

Problem. Nazwy sprzedawców, opisy oraz nazwy kont i kategorii pochodzą z AI Budget, a może je napisać współczłonek konta, paragon albo powiadomienie banku. Czat renderował obrazki markdown, a strona nie miała CSP.

Dlaczego to ważne. Tekst, który przekonałby model do wstawienia do odpowiedzi obrazka z adresem zawierającym prywatne sumy, sprawiłby, że przeglądarka sama wyśle te liczby na obcy serwer — bez żadnego kliknięcia.

Poprawka. Trzy warstwy. Czat nigdy nie ładuje zdalnych obrazków. Każdy ciąg z AI Budget jest oczyszczany przed trafieniem do modelu: znikają linki i obrazki markdown, adresy URL i HTML, a długość jest ograniczona. Reguła w prompcie mówi, że wynik narzędzia to dane strony trzeciej, nigdy instrukcje. Ponowny przegląd znalazł jeszcze jedno nieoczyszczone miejsce — nazwy kategorii w komunikacie błędu narzędzia zapisu — i to też zostało poprawione.

4. Dane prywatne w śladach LangSmith

Problem. Śledzenie (zewnętrzny magazyn) dostawało wyniki narzędzi, a w kolejnych iteracjach także odpowiedzi modelu powtarzające prywatne liczby, tekst dla syntezy mowy i odtwarzaną historię.

Dlaczego to ważne. Redakcja po nazwach narzędzi nie wystarczała: flaga «ten przebieg użył danych prywatnych» jest znana dopiero po wykonaniu narzędzia, a wtedy ślad główny jest już wysłany.

Poprawka. Decyzja zapada przed wysłaniem przebiegu głównego: ślad jest prywatny, gdy rozmowa była już oznaczona albo narzędzia AI Budget są podpięte z zakresem wszystkich kont. W prywatnym śladzie wejścia i wyjścia każdego przebiegu są wymazywane. Testy stały się hermetyczne — lokalny .env dewelopera z włączonym śledzeniem sprawił, że jeden test zadzwonił do LangSmith (odpowiedział 403) — a wykrywanie śledzenia obejmuje wszystkie cztery nazwy zmiennych środowiskowych.

5. Ponowne użycie tokenu odświeżania po nieudanym zapisie

Problem. Jeśli zapis do bazy padł po udanym odświeżeniu, następny proces przedstawiłby zużyty token odświeżania.

Dlaczego to ważne. AI Budget słusznie traktuje to jako kradzież i unieważnia całe połączenie. Nasza własna ochrona przed kradzieżą odcięłaby uczciwego klienta.

Poprawka. Zapis jest ponawiany pod blokadą, a jeśli i to zawiedzie, połączenie dostaje stan needs_reconnect. Odświeżenie o nieznanym wyniku (timeout, błąd sieci, 5xx) ustawia 60-sekundowy znacznik, żeby nikt nie próbował ponownie z możliwie zużytym tokenem. Odświeżamy tylko po 401 invalid_token, a zapis nowego tokenu to compare-and-swap na przechowywanym tokenie odświeżania.

6. Odłączenie w trakcie synchronizacji

Problem. Strona danych pobrana przed odłączeniem mogła zostać zapisana już po wyczyszczeniu lustra, ze starym numerem generacji synchronizacji, którego późniejsza pełna resynchronizacja nigdy nie zamiata.

Dlaczego to ważne. Nieaktualne wydatki zostałyby na stałe w liczbach firmy — w miejscu, które ma trafić do ksiąg.

Poprawka. Połączenie jest najpierw lokalnie oznaczane jako unieważnione. Każdy zapis do lustra przechodzi przez strażnika opartego na blokadzie wiersza (SELECT … FOR UPDATE), który wymaga «etykieta firma ORAZ aktywne połączenie». Czyszczenie lustra najpierw aktualizuje wiersz konta, więc czeka na stronę, która jest właśnie w locie.

7. Ciche przełączenie firmy

Problem. Gdy dostęp do konta firmowego był chwilowo ograniczony, automatyczne etykietowanie awansowało inne konto firmowe na «firmę» i kasowało lustro prawdziwej firmy.

Dlaczego to ważne. Przejściowy problem z uprawnieniami zamieniałby po cichu zawartość panelu firmy na dane innej działalności.

Poprawka. Konto tylko nieaktywne, ale wciąż widoczne, zachowuje etykietę firmy (i jest wyłączone z liczb). Etykietę traci dopiero konto, którego w ogóle już nie widać.

8. «Wszystko dopasowane» zamiast «nie wiadomo»

Problem. Uzgadnianie zwracało pustą listę, gdy wFirma nie była podłączona albo nie odpowiadała, a pierwsza synchronizacja pokazywała częściowe lustro jako gotowe.

Dlaczego to ważne. Narzędzie czatu odczytałoby pustą listę jako «każdy wydatek ma dokument» — fałszywe uspokojenie w sprawie, w której chodzi o koszty uzyskania przychodu.

Poprawka. Rozróżnione stany wszędzie, od API po interfejs i narzędzia agenta. Lustro uznajemy za kompletne dopiero po pełnym przebiegu.

Jak pracowaliśmy

Proces był ten sam we wszystkich trzech częściach: burza mózgów z właścicielem, spisana specyfikacja, niezależna «kontrola budowniczego», która czyta specyfikację oczami wykonawcy, plan krok po kroku, a potem wykonanie przez subagentów — świeży agent na każde zadanie, świeży recenzent po każdym zadaniu, rundy poprawek, na końcu przegląd całej gałęzi najmocniejszym modelem i osobny audyt bezpieczeństwa.

Faza 1 API to 17 zadań, wiele kont — 10, integracja księgowa — 18. Rund poprawek wywołanych przeglądami były dziesiątki; większość zadań miała co najmniej jedno znalezisko klasy «Important». Końcowe liczby testów: API AI Budget — 4970 testów jednostkowych, aplikacja mobilna — 2370, API eKsięgowego AI — 781, aplikacja webowa — 171, do tego 13 testów integracyjnych na prawdziwej bazie, 12 testów Playwright i 10 przypadków end-to-end publicznego API, w tym przepływ dwóch kont.

Wniosek, który z tego wynosimy: recenzent, który nie pisał kodu, znajduje rzeczy, których autor nie zobaczy — tu między innymi phishing zgody i rozszerzenie zakresów przy scalaniu zgód. Najdroższe znaleziska nie były błędami składni, tylko błędnymi założeniami o tym, kto kończy przepływ i co oznacza pusta lista.

Co dalej i co jest jeszcze otwarte

W API dojdą przychody, budżety i cele; webhooki być może później. Pilotaż działa na własnych kontach właściciela.

Trzy sprawy świadomie zgłosiliśmy zamiast naprawiać od razu: potwierdzenie destrukcyjnych narzędzi agenta wymuszane przez serwer (dziś działa na poziomie Prompt to polecenie, które wpisujesz modelowi AI — od jego precyzji zależy, czy odpowiedź będzie użyteczna, czy ogólnikowa. Pełna definicja → i dotyczy projektu całej aplikacji, nie tylko tej integracji), CSP dla całej strony oraz to, że wiadomości z grupowych czatów Telegrama wciąż korzystają z prywatnej historii rozmowy użytkownika.

Więcej o tym, jak zbudowane są oba produkty, piszemy w artykułach Jak AI Budget Assistant wykorzystuje AI i Jak działa Accounting AI Agent, a sam produkt jest na stronie AI Budget Assistant.

Najczęstsze pytania

Kto może korzystać z publicznego API AI Budget?
Każdy programista. API jest darmowe i udokumentowane, a aplikację rejestruje się samodzielnie, bez ręcznej weryfikacji. Połączyć konto może tylko jego właściciel lub edytor. Pierwsza faza obejmuje wydatki; przychody, budżety i cele dojdą później.
Czy eKsięgowy AI przechowuje moje prywatne wydatki?
Nie. W bazie księgowej jest kopia wyłącznie konta oznaczonego jako firma. Dane prywatne są czytane na żywo z AI Budget i trzymane w Redisie zaszyfrowane AES-256-GCM najwyżej 300 sekund — bez zapisu do bazy, logów i pamięci długoterminowej agenta.
Dlaczego kanał zmian, a nie webhooki?
Kanał zmian z paginacją po (updated_at, id) i znacznikami usunięcia pozwala klientowi samemu nadrobić przerwę od zapisanego kursora. 10-sekundowe opóźnienie liczone zegarem bazy sprawia, że wolno zatwierdzana transakcja nie zostanie pominięta. Webhooki być może dojdą później.
Jak API wybiera konto, gdy połączenie obejmuje kilka kont?
Nie wybiera. Przy jednym koncie w zgodzie nagłówek X-Account-Id jest opcjonalny; przy kilku jego brak daje 400 account_required, a identyfikator spoza zgody — 403 account_not_granted. Żądanie nigdy nie trafia do konta, którego integrator nie wskazał.