CRM API — Dokumentacja endpointów

Arc OS — The Orchestration System for AI Teams

Informacje ogólne

Parametr Wartość
Base URL https://arc-os.co/api/crm
Autoryzacja Authorization: Bearer <JWT> lub ?token=<JWT> (dla SSE/WebSocket)
Content-Type application/json
Algorytm JWT HMAC-SHA256
TTL JWT 24 godziny

Uwierzytelnianie

Wszystkie endpointy (poza /docs/*) wymagają tokenu JWT w nagłówku Authorization: Bearer <token>.

Dla połączeń SSE i WebSocket token przekazywany jest przez parametr query ?token=<JWT>.

Błędy autoryzacji

Kod Opis
401 Brak lub nieprawidłowy token
403 Brak dostępu do projektu (multi-tenancy)

Endpointy według kategorii

Konto i ustawienia

Metoda Ścieżka Opis
GET /account/settings Pobierz ustawienia konta
PUT /account/settings Zaktualizuj ustawienia konta

Onboarding + Trial Credits (Phase 50.1)

Metoda Ścieżka Opis
POST /onboarding/setup Utwórz pierwszy projekt. Body multipart: config (JSON) + files. Pole anthropicKey jest teraz opcjonalne — jeśli puste + użytkownik ma zweryfikowany email + nie otrzymał wcześniej wersji próbnej, projekt tworzony jest w trial_mode=1 ze 100K free tokens. Response: { ok, project, trial_activated }. Phase 51: zwraca 402 z {error:"plan_limit_reached", reason, current, limit, plan} gdy użytkownik przekroczył limit projektów dla planu.
GET /account/trial-status Status wersji próbnej dla bannera UI. Response: { email, email_verified, trial_granted, has_trial_active, total_remaining, total_granted, projects: [...] }
GET /account/usage Historia zużycia tokenów dla autoryzowanego użytkownika (Phase 63, #148). Response: { rows: [ { project_name, worker_id, input_tokens, output_tokens, cache_tokens, total_tokens, created_at } × do 200 ], totals: { total, input, output } }. Odczytuje token_usage_log po owner_id. Wyświetlane w UserDropdown (UsageCard) i BillingPage (sekcja Token Usage).
GET /account/billing-summary Zbiorcze podsumowanie billingu (#309). Response: { arc: { plan, status, tokens_this_month, tokens_input, tokens_output, renewal_date }, anthropic: { connected, key_prefix, credit_balance_usd, spend_month_usd, tokens_this_week } }. Sekcja Anthropic wypełniana server-side przez Anthropic API z account_settings.anthropic_key użytkownika (fallback → PLATFORM_ANTHROPIC_KEY). Wyświetlane w UserDropdown UsageCard.

Onboarding Checklist (Phase 54.1, issue #56)

Post-wizard 5-krokowa lista kontrolna zaangażowania. Każdy krok (workers, cli, skill, bot, issue) przyjmuje status completed lub skipped. Mutacje są idempotentne: ponowny identyczny POST zwraca ten sam stan, nie zapisuje duplikatu w activity_log. Replay nie resetuje stanu, jedynie usuwa dismissed_at — UI ponownie wyświetla panel z tym samym postępem.

Metoda Ścieżka Opis
GET /onboarding/progress Aktualny stan dla zalogowanego użytkownika. Response: { steps:["workers","cli","skill","bot","issue"], state:{<step>:<status>}, completed_count, total_steps:5, completed_at, dismissed_at, source, started_at, updated_at }. Nienaruszony użytkownik → zera/null bez tworzenia wiersza.
POST /onboarding/event Zapisz przejście kroku. Body: { step: "workers"|"cli"|"skill"|"bot"|"issue", status: "completed"|"skipped", source?: "web"|"cli" }. Walidacja allowlisty → 400 na nieznany krok/status. Response: ten sam kształt co GET. Emituje onboarding_step_completed/onboarding_step_skipped w activity_log tylko przy changed; przy przejściu do 5/5 dodatkowo emituje onboarding_completed z duration_ms.
POST /onboarding/dismiss Zamknij panel (dismissed_at = now). Idempotentnie. Emituje onboarding_dismissed przy pierwszym wywołaniu z payload {completed_count}.
POST /onboarding/replay Ponownie otwórz zamknięty panel (dismissed_at = NULL). Stan kroków nie jest dotykany. Emituje onboarding_replayed przy clear-event.
POST /projects/:name/active-issue Issue #115. Powiąż bieżącą sesję webową z issue. Body: { issue_id: number, title?: string }. Zapisuje zdarzenie session_active_issue w activity_log (source=web).
GET /projects/:name/active-issue Issue #115. Ostatnio powiązane issue dla tego właściciela w ciągu 7 dni. Response: { active_issue_id, title, ts }.
GET /onboarding/cli-status Phase 54.3 (issue #58). Czy użytkownik logował się przez arc login w ciągu ostatnich 30 dni? Response: { installed: boolean, last_cli_at: string|null }. SSOT — wiersze w activity_log z event_type='cli_invocation' i actor=chatId. Frontend onboarding-checklist odpytuje ten endpoint co 10s dopóki krok CLI jest pending; gdy installed=true — automatycznie oznacza krok cli jako completed.
GET /analytics/onboarding-funnel Phase 54.6 (issue #61). Zagregowane statystyki lejka w kroczącym oknie czasowym. Query: hours=168 (1-720, domyślnie 7d). Response: { hours, total_steps:5, started_users, completed_users, completion_rate, per_step: [{step, completed, skipped}…], duration_p50_ms, duration_p90_ms, ttfc_p50_ms, ttfc_sample_size }. SSOT — zdarzenia onboarding_step_* + onboarding_completed + cli_invocation w activity_log. TTFC = czas do pierwszego arc (delta julianday od pierwszego kroku onboardingu do pierwszego cli_invocation per aktor).

SSOT dla metryk lejka (Phase 54.6 / issue #61) — zdarzenia w activity_log (event_type LIKE 'onboarding_%'). Tabela onboarding_progress — derived cache: UI renderuje się jednym zapytaniem zamiast agregacji po zdarzeniach.

Beta Feedback (Phase 53.3)

Metoda Ścieżka Opis
POST /feedback Wyślij beta feedback. Body: {type: "bug"|"feature"|"other", title, description, project?, browser?}. Zapisuje do activity_log (event_type=feedback_report) i pinguje CEO na Telegram.
GET /admin/feedback Lista ostatnich zgłoszeń (tylko admin). Query: limit=50 (max 500). Response: {items: [...], count}.

POST /feedback — walidacja body: type ∈ {bug,feature,other}, title ≤200 znaków, description ≤5000 znaków. Sukces → {ok: true, type, title}. Ping Telegram formatowany jako 🐞/💡/📝 New <type> feedback ... From: <user> Title: <title> + pierwsze 400 znaków opisu.

Pływający widget w FeedbackWidget.jsx (dashboard CRM) automatycznie przekazuje browser (UA + viewport + locale) i project (technical_name aktywnego projektu).

Arc Help AI Chat (Phase 61, #147)

Metoda Ścieżka Opis
POST /help/chat In-app AI Q&A. Body: {message, history: [{role,text}]}. Response: {reply, sources: string[], remaining, limit}. Rate limit: 30/dzień/użytkownik.
GET /help/usage Aktualny limit. Response: {remaining, limit, used}.

POST /help/chat — pipeline: (1) sprawdzenie rate-limitu (429 przy przekroczeniu), (2) RAG przez shared/rag.ts (Cohere + sqlite-vec, Phase 71) z merge trafień projektu + skilli _global_ → fallback wyszukiwanie słów kluczowych w docs/public/, (3) Claude Haiku z system promptem + kontekstem dokumentacji + historią. message ≤2000 znaków. Odpowiada w języku zapytania.

Beta Invites (Phase 52.1, tylko admin)

Metoda Ścieżka Opis
GET /admin/dashboard System Dashboard (Phase 60.9, #145). Tylko admin. Zwraca: CPU/RAM/Disk z /proc, użytkowników wg planu, flotę kontenerów, ostatnie 50 zdarzeń aktywności, statystyki waitlisty + projektów + zgłoszeń.
GET /admin/wipe-metrics Dashboard telemetrii WIP-E (#308). Tylko admin. Zwraca: {render: {count, avg_ms, p50_ms, p95_ms, max_ms}, interaction: {count, avg_per_session, p95_per_session, max_per_session, sessions_zero}, by_worker: [{worker_id, render_count, avg_render_ms, session_count, avg_interactions}], daily: [{date, renders, interactions, avg_render_ms}], recent: [...]}.
GET /admin/waitlist Lista wszystkich zgłoszeń waitlisty. Tylko admin. Response: {entries: [{id, email, message, status, created_at}]}.
POST /admin/waitlist/:id/approve Zatwierdź zgłoszenie — generuje invite code (arc-XXXX-XXXX), wysyła email z kodem, aktualizuje status→approved. Response: {ok, invite_code, email_sent}.
POST /admin/waitlist/:id/reject Odrzuć zgłoszenie. Response: {ok}.
GET /admin/invites Lista wszystkich kodów zaproszenia + liczniki (total_active, total_used). Tylko admin.
POST /admin/invites Wygeneruj N kodów. Body: {count: N, note?: string}. Tylko admin. Response: {ok, codes, count}.
DELETE /admin/invites/:code Unieważnij nieużyty kod zaproszenia.
/admin/notebooklm/* Usunięte w Phase 71.8 wraz z NotebookLM Bridge. Wyszukiwanie semantyczne działa teraz przez self-hosted RAG (rag-architecture.md).

Aktualizacja flow auth: POST /api/auth/register wymaga teraz pola invite_code (zamknięta beta Phase 52.1). Bez kodu → 403 {error: "invite_required"}. Nieprawidłowy/użyty kod → 403 {error: "invalid_invite"}.

Standard Cloud — WebSocket Terminal + SSE Logs (Phase 60 #139)

Protokół Ścieżka Opis
WS /ws/cloud/:userId/terminal?token=<JWT> Proxy do docker exec -i <containerId> /bin/bash. IDOR: userId musi zgadzać się z chatId z JWT. Wstrzymany (paused) kontener jest automatycznie wznawiany. Przychodzące ramki WS → stdin kontenera; stdout+stderr → ramki WS.
SSE /api/sse/cloud/:userId/logs docker logs -f --tail 50 dla kontenera użytkownika. Auth: Bearer JWT. IDOR: userId === chatId. Zdarzenia: data: {"line": "..."} per linia, data: {"closed": true} przy zakończeniu.

Standard Cloud (Phase 60)

Metoda Ścieżka Opis
POST /cloud/claude-verify Sprawdza claude --version w kontenerze (transport-safe shell-quoted przez SSH w trybie remote-host, #329). Ustawia claude_authed=true. Response: { ok, output }
POST /cloud/ssh-keygen Generuje klucz ed25519 w kontenerze (idempotentnie). Response: { public_key }
POST /cloud/ssh-verify ssh -T [email protected] w kontenerze. Ustawia github_authed=true przy sukcesie. Response: { ok, output }
POST /cloud/provision Provisioning kontenera Docker dla użytkownika. Wymaga planu cloud, inaczej 402. Idempotentnie: jeśli kontener już istnieje — zwraca bieżący stan. Response: { container_id, status, server_ip, port, claude_authed, github_authed }
GET /cloud/status Stan kontenera + live reconciliation przez docker inspect. Response: { container_id, status, server_ip, internal_port, claude_authed, github_authed, docker_running, last_active, created_at } lub { status: "none" }
POST /cloud/deprovision Zatrzymaj + usuń kontener (docker stop + docker rm -f + docker network rm arc-net-{id}). Aktualizuje status=deleted w DB. Response: { ok: true, container_id }

Statusy kontenera: provisioningreadypausedsuspended / deleted.

Bezpieczeństwo (SEC-60 #152, #154, #155, #156): każdy kontener jest izolowany we własnej sieci arc-net-{id} (lateral movement prevention). Połączenie SSH Contabo→Hetzner przez dedykowanego użytkownika arcapi (grupa docker, bez root) z wrapperem docker-only — polecenia nie-dockerowe są blokowane na poziomie authorized_keys. ARC_TOKEN wstrzykiwany przez docker exec po starcie (niewidoczny w docker inspect). git clone ograniczony przez timeout 60. WebSocket idle timeout: 120s. SSE docker logs ograniczone do --since 1h. Zapobieganie IDOR: wszystkie endpointy weryfikują container.user_id === req.userId. Flagi bezpieczeństwa przy docker run: --cap-drop=ALL --security-opt=no-new-privileges --cpus=1.5 --memory=2g --pids-limit=200. Wolumeny: arc-{id}-workspace:/workspace, arc-{id}-claude:/home/arcuser/.claude, arc-{id}-ssh:/home/arcuser/.ssh. Lifecycle (#141): GET /cloud/status zawsze aktualizuje last_active. Idle 30 min → docker pause (cron co 5 min, scripts/cloud-lifecycle-cron.ts). Wake: wiadomość CRM, wiadomość TG, WS upgrade → automatyczne docker unpause.

Waitlist (#134):

Metoda Ścieżka Opis
POST /cloud/waitlist Dołącz do kolejki. Idempotentnie. Response: { position, status, joined_at, message }. 409 jeśli już na planie cloud lub kontener już istnieje.
GET /cloud/waitlist/status Własny status w kolejce. Response: { position, status, joined_at, invited_at } lub { status: "not_joined" }.
GET /cloud/waitlist Tylko admin. Pełna lista + statystyki. Response: { stats: { total, waiting, invited, activated }, list: [...] }.
POST /cloud/waitlist/invite Tylko admin. Zaproś użytkownika. Body: { user_id }. Ustawia status=invited + automatycznie podnosi plan do cloud. Response: { ok, user_id, position }.

Billing (Phase 51 → #202 Plata by mono)

Phase #202: Stripe zastąpiono przez Plata by mono (internet acquiring monobanku). Subskrypcje recurring przez tokenizację (karta zapisywana przy pierwszej płatności).

Metoda Ścieżka Opis
GET /billing/status Aktualny plan, limity, użycie, funkcje. Response: { plan, status, current_period_end, next_billing_date, plata_masked_pan, limits, usage, features, pricing, can_upgrade, plata_ready }
POST /billing/checkout-session Tworzy fakturę Plata z tokenizacją. Body: { plan: "min"|"cloud", success_url?, cancel_url? }. Response: { url, invoice_id, plan, amount_uah }. 503 jeśli PLATA_MERCHANT_TOKEN nie jest w vault.
POST /billing/webhook Callback Plata (BEZ auth CRM — weryfikacja przez nagłówek X-Token). Statusy: success (aktywuje plan + zapisuje cardToken), failure/expired (inkrementuje billing_failures, 3+ → downgrade do free). Idempotentnie przez tabelę plata_events.
POST /billing/cancel Anuluj subskrypcję (downgrade do free). Pauzuje kontener Docker dla planu cloud. Response: { ok, plan: "free" }.

#205 (2026-05-26): Trasa legacy /billing/portal-session została usunięta razem z martwym kodem Stripe. Do anulowania subskrypcji służy /billing/cancel.

Limity planu (semantyka OR):

Odpowiedź 402 na POST /onboarding/setup lub POST /projects/:name/workers gdy limit przekroczony: { error: "plan_limit_reached", reason: "projects_limit"|"workers_limit", current, limit, plan, message }

Użytkownicy admin (role=admin) całkowicie pomijają sprawdzanie limitu planu — są operatorami, nie płatnymi tenantami.

Testerzy beta (subscriptions.plan='beta', Phase 52 F&F) również pomijają — nieograniczona liczba projektów/workerów plus wszystkie funkcje Max. Przypisywane ręcznie: UPDATE subscriptions SET plan='beta' WHERE user_id=?.

Bugfix (issue #25): POST /projects/create (Quick Start, Phase 50.2) wcześniej rzucał błąd ownerChatId is not defined przez literówkę — naprawione, aktor audytu jest teraz poprawnie zapisywany.

Bugfix (issue #26): allocatePort() dla nowych projektów sprawdza teraz rzeczywiste powiązania TCP (ss -tln), a nie tylko registry. Wcześniej mógł zwrócić port zajęty przez serwis spoza registry (NotebookLM bridge :19213, internal bridges) → workspace bot padał na EADDRINUSE.

Flow auth (Phase 50.1): /api/auth/register i /api/auth/login zwracają teraz JWT nawet dla niezweryfikowanego emaila + flaga needs_verification: true. Wrażliwe operacje (przyznanie wersji próbnej, billing, kody zaproszenia) sprawdzają email_verified osobno. Rate limit na rejestrację: 3 / IP / 24h.


Projekty (9 endpointów)

Metoda Ścieżka Opis
GET /projects Lista projektów użytkownika
POST /projects/create Utwórz projekt
POST /projects/create-with-team Atomowe utworzenie projektu + workerów + (opc.) bota TG w jednym żądaniu — body: {project, workers[], telegram?}; rollback przy błędzie
GET /projects/suggest-preset Podpowiedź presetu wg niszy — query: niche=<text>; zwraca {preset_id} na podstawie mapy słów kluczowych
GET /projects/:name Szczegóły projektu
GET /projects/:name/config Konfiguracja projektu
PUT /projects/:name/config Zaktualizuj konfigurację
GET /projects/:name/protocol Protokół projektu
PUT /projects/:name/protocol Zaktualizuj protokół
GET /projects/:name/logs Logi projektu
GET /projects/:name/metrics Metryki projektu

POST /projects/create — body:

{
  "technical_name": "string",
  "displayName": "string",
  "description": "string",
  "icon": "string",
  "color": "string"
}

GET /projects/:name/logs — query: category, lines

GET /projects/:name/metrics — query: since, until


Workery (11 endpointów)

Metoda Ścieżka Opis
GET /workers #304 Phase A — wszystkie workery wszystkich projektów bieżącego użytkownika. Response: { workers: [{ id, label, icon, type, model, tools, context_assets, project_name }] }. Filtrowanie po owner_id (multi-tenancy). CEO widzi wszystkie projekty.
GET /workers/presets #228 — globalna biblioteka presetów (niezależna od projektu). Zwraca 13 workerów z kanonicznego config/workers_registry.json: { presets: [{ id, label, icon, type, model, max_turns, tools, system_prompt, context_assets, focus_dirs, prompt_style }] }. Używane przez WorkerCreationWizard w Step 1.
GET /workers/templates #304 Phase I — szablony bieżącego użytkownika. Response: { templates: [{ id, name, description, config, is_public, created_at }] }.
POST /workers/templates #304 Phase I — zapisz/zaktualizuj szablon. Body: { name, description?, config }. Response: { ok, id }.
DELETE /workers/templates/:id #304 Phase I — usuń szablon (tylko właściciel). Response: { ok }.
GET /projects/:name/workers Lista workerów
POST /projects/:name/workers Utwórz workera
POST /projects/:name/workers/reorder Phase 53.8 — zmień kolejność workerów. Body: {order: [id1, id2, ...]}. Atomowo nadpisuje workers_registry.json. Workery nieobecne w order dodawane są na końcu (zabezpieczenie przed utratą). Response: {ok, count, order}.
PUT /projects/:name/workers/:id Zaktualizuj workera
DELETE /projects/:name/workers/:id Usuń workera
POST /projects/:name/workers/generate-prompt Wygeneruj systemowy prompt
GET /projects/:name/workers/:id/telegram-token Pobierz token Telegram
POST /projects/:name/workers/:id/telegram-token Phase 53.4 — waliduje token przez Telegram getMe, zapisuje bot_username w vault, odmawia jeśli ten sam bot jest już powiązany z innym workerem (409). Response: {ok, started, bot_username}.
DELETE /projects/:name/workers/:id/telegram-token Usuń token Telegram
POST /projects/:name/workers/:id/avatar #304 Phase D — prześlij awatar (multipart file, JPEG/PNG/WebP, max 2 MB). Weryfikacja magic-byte. Zapisuje do data/worker-avatars/, rejestruje w worker_avatars (migracja 043). Response: { ok, url }.
GET /projects/:name/workers/:id/avatar #304 Phase D — pobierz awatar binarnie (Content-Type zgodny z MIME). 404 jeśli awatar nie został przesłany.
DELETE /projects/:name/workers/:id/avatar #304 Phase D — usuń awatar, zresetuj avatar_pack='role' w JSON workera.
GET /projects/:name/workers/:id/activity #306 — feed aktywności workera (ostatnie 50 zdarzeń). Scalone: activity_log (actor=workerId) + project_issues.activity (author=workerId) + token_usage_log (dzienne snapshoty). Response: { events: [{ type, title, detail, when }] }. Typy: git_commit, skill_loaded, skill_unloaded, issue_pick, issue_close, issue_log, token_budget, session_start.
GET /projects/:name/workers/:id/runtime #306 — stan runtime workera. Response: { status: 'working'|'idle', status_started_at, tokens_today, tokens_pct, tokens_cap, current_skill }. Czyta najpierw z workers_runtime_state (migracja 045); fallback staleness: status='working' + tmux martwy + updated_at > 10 min → idle (detekcja crasha). Dzienny limit wg planu przez lookup subscriptions.plan: free=100K, starter=400K, starter_cloud=2M, beta=unmetered (zwraca tokens_cap: null, tokens_pct: 0). Interwał odpytywania 15s.
POST /projects/:name/workers/:id/notify Phase 53.2 — wyślij ping zdarzenia TG ({event?, text, buttons?}). Silent no-op jeśli token nie jest powiązany lub CRM_DISABLE_TG_NOTIFY=1.
POST /projects/:name/workers/:id/suggest-bot-username 53.11.1 (issue #48) — zwraca 5 kandydatów TG username dla wizarda tworzenia bota w formacie <project>_<worker>_bot + ponumerowane fallbacki. Slugify usuwa myślniki, obcięcie do 32 znaków (część worker obcinana pierwsza). Response: {candidates: string[]}.
POST /metrics/wizard 53.11.1 (issue #48) — sink telemetrii dla wizarda tworzenia bota. Body: {action, duration_ms?, attempts?, success?, project?, worker_id?, locale?} (#124: zdarzenia locale_active/locale_switch). Zapisuje do activity_log (event_type=wizard_metric), best-effort.
GET /analytics/wizard-metrics?hours=168 53.11.1 (issue #48) — podsumowanie lejka: {starts, completions, abandons, success_rate, avg_duration_ms_completed, avg_attempts_completed, by_action}. Domyślnie 7 dni, clamp 1-720h.
POST /projects/:name/restart Uruchom ponownie workera
GET /projects/:name/active-role Aktualnie aktywna rola
POST /projects/:name/active-role Zmień aktywną rolę

POST /projects/:name/workers — body:

{
  "label": "string",
  "icon": "string",
  "type": "terminal | telegram",
  "model": "string",
  "max_turns": 20,
  "tools": ["Read", "Write", "Bash"],
  "system_prompt": "string",
  "focus_dirs": ["src/", "docs/"]
}

max_turns domyślnie 20 (wcześniej było 5, powodowało błąd "Reached max turns" w wieloetapowych dialogach z wywołaniami narzędzi).

POST /projects/:name/restart — query: worker_id


Pliki i przechowywanie (8 endpointów)

Metoda Ścieżka Opis
GET /projects/:name/files Drzewo plików
POST /projects/:name/files/upload Prześlij plik (multipart, max 100MB)
POST /projects/:name/files/mkdir Utwórz katalog
POST /projects/:name/files/create Utwórz plik
GET /projects/:name/files/read Odczytaj plik
PUT /projects/:name/files/save Zapisz plik
DELETE /projects/:name/files/delete Usuń plik
POST /projects/:name/files/clone Git clone repozytorium

GET /projects/:name/files — query: path

GET /projects/:name/files/read — query: path, raw


Skille (18 endpointów)

Skille projektu

Metoda Ścieżka Opis
GET /projects/:name/skills Lista skilli projektu
POST /projects/:name/skills Utwórz skill
PUT /projects/:name/skills/:id Zaktualizuj skill
DELETE /projects/:name/skills/:id Usuń skill

#210 (2026-05-26): DB (skills_global) jest teraz writerem SSOT. Zapisy z UI trafiają najpierw do DB; .claude/skills/<name>/SKILL.md jest zapisywany jako artefakt, dzięki czemu Claude Code CLI automatycznie odkrywa skille. Zapisy do legacy skills/<name>.md zostały usunięte — istniejące pliki nie są już czytane ani utrzymywane. Helper migracji: scripts/migrate-skills-to-db.ts.

Globalny marketplace

Metoda Ścieżka Opis
GET /skills Lista globalnych skilli
POST /skills Opublikuj skill
GET /skills/:id Szczegóły skilla
PUT /skills/:id Zaktualizuj skill
DELETE /skills/:id Usuń skill

Ewolucja i aktualizacje

Metoda Ścieżka Opis
GET /skills/:id/evolution Historia ewolucji skilla
GET /skill-updates Lista dostępnych aktualizacji
POST /skill-updates/:id/approve Zatwierdź aktualizację
POST /skill-updates/:id/reject Odrzuć aktualizację

Forki skilli

Metoda Ścieżka Opis
GET /projects/:name/skill-forks Lista forków
POST /projects/:name/skill-forks Utwórz fork
PUT /projects/:name/skill-forks/:id Zaktualizuj fork
DELETE /projects/:name/skill-forks/:id Usuń fork

Chat i wiadomości

Metoda Ścieżka Opis
POST /projects/:name/chat Wyślij wiadomość na chat
GET /projects/:name/chat/history Historia chatu
POST /projects/:name/message Wyślij wiadomość do workera (Phase 48.6: automatycznie budzi uśpionego workera, ~2-4s cold start; Phase 48.6.1: budzenie działa teraz również w projektach single-mode, nie tylko parallel)
GET /projects/:name/pins Lista notatek (pins)
POST /projects/:name/pins Utwórz notatkę
DELETE /projects/:name/pins/:id Usuń notatkę

Wiki (4 endpointy)

Metoda Ścieżka Opis
GET /projects/:name/wiki/tree Drzewo stron wiki
GET /projects/:name/wiki/file Odczytaj stronę wiki
PUT /projects/:name/wiki/save Zapisz stronę wiki. Phase 71.5: wyzwala syncWiki → re-embed Cohere (fire-and-forget; błędy są logowane, zapis nie pada).
GET /projects/:name/wiki/download Pobierz wiki jako archiwum ZIP

Analityka (4 endpointy)

Metoda Ścieżka Opis
GET /analytics/activity Feed aktywności
GET /analytics/sidebar Dane dla panelu bocznego
GET /analytics/phases Lista faz projektu
POST /analytics/phases Zaktualizuj fazy projektu

Marketplace i Sage (8 endpointów)

Metoda Ścieżka Opis
GET /sage/scout/categories Kategorie marketplace
POST /sage/scout Wyszukaj skille
POST /sage/scout/quick-scan Szybkie skanowanie
POST /sage/scout/analyze Dogłębna analiza skilla
POST /sage/scout/install Zainstaluj skill
POST /sage/analyze Analiza Sage
GET /sage/status Status serwisu Sage
POST /sage/benchmark Uruchom benchmark

Pamięć i wiedza

Metoda Ścieżka Opis
GET /projects/:name/rag/search?q=...&k=6&include_global=true&doc_types=wiki,issue,skill,transcript Phase 71.7 (#364): wyszukiwanie semantyczne nad embeddings + embeddings_vec (Cohere + sqlite-vec). Parametry: q (tekst zapytania), k (1-25, domyślnie 6), include_global (domyślnie true — merge z namespace skilli _global_), doc_types (podzbiór po przecinku; Phase 73.6 dodatkowy typ: transcript). Response: { query, project, hits: [{rank, doc_type, doc_id, chunk_ix, distance, scope: 'project'|'global', text}] }. Zasila arc kb search + narzędzie czatu ask_notebooklm. Architektura: rag-architecture.md.
POST /projects/:name/memory/refresh Phase 71.8 (#365): re-embed MANIFEST + ROADMAP + kluczowych plików do RAG store (wcześniej — sync do NotebookLM). Ten sam endpoint, nowa semantyka.
POST /projects/:name/memory/fetch-artifact Usunięto w Phase 71.8 (audio overview nie ma odpowiednika w RAG) — zwraca 410 Gone.
GET /projects/:name/learnings Lista learnings
POST /projects/:name/learnings Dodaj learning
GET /projects/:name/knowledge-graph Graf wiedzy projektu

Dokumentacja (globalna, bez auth)

Metoda Ścieżka Opis
GET /docs/tree?lang=<lang> Drzewo dokumentacji; lang opcjonalny (en/uk), domyślnie en
GET /docs/file?path=<p>&lang=<lang> Odczytaj plik dokumentacji z language fallback

GET /docs/tree — query: lang (opcjonalny)

GET /docs/file — query: path (wymagany), lang (opcjonalny)


System

Metoda Ścieżka Opis
GET /system/configs Pobierz konfiguracje systemowe
PUT /system/configs Zaktualizuj konfiguracje systemowe

Kody błędów

Kod Znaczenie
200 Sukces
201 Utworzono
400 Nieprawidłowe żądanie
401 Nieautoryzowany
403 Zabronione (multi-tenancy)
404 Nie znaleziono
409 Konflikt (duplikat)
429 Zbyt wiele żądań
500 Błąd serwera

GitHub Integration (Phase 49.3)

Endpoint Method Opis
/api/crm/projects/:name/github GET Lista repos GitHub powiązanych z projektem
/api/crm/projects/:name/github POST Powiąż repo (body: {owner, repo}) — zwraca webhook URL + secret + instrukcje konfiguracji
/api/crm/projects/:name/github/:id DELETE Odepnij repo
/api/crm/projects/:name/github/events GET Lista ostatnich zdarzeń GitHub (Phase 49.3.1, query: ?limit=50)
/api/webhooks/github POST Publiczny odbiornik webhook (walidowany HMAC-SHA256, rate-limit 100/min)

Obsługiwane zdarzenia: push, pull_request, workflow_run, issues. Powiadomienia kierowane do Telegram właściciela projektu.

Account Security (Phase 45.4)

Endpoint Method Opis
/api/crm/account/recovery GET Lista aktywnych kluczy odzyskiwania
/api/crm/account/recovery POST Utwórz klucz odzyskiwania (body: encryptedKey, keyHint)
/api/crm/account/recovery DELETE Unieważnij klucz(-e) odzyskiwania (body: { id } lub {} dla wszystkich)
/api/crm/account/recovery/restore GET Pobierz zaszyfrowany klucz główny do odtworzenia

Bezpieczeństwo


Phase 53.13 — type-safety baseline (2026-05-10)

Brak zmiany zachowania endpointów — tylko typy wewnętrzne. tsc --noEmit blokuje teraz push/CI:


Sentinel Pentest Remediation (2026-06-10, #433–#444)

Sprint pentestu white-box — zmiany zachowania endpointów po naprawie 3×P1 + 4×P2 + 3×P3:


Phase 53.15 — Sentinel Sprint 1 (2026-05-10)

Zmiany zachowania endpointów auth + admin (Sentinel audit P0 fixes):


Phase 53.21 — Sentinel P2 batch 2 (2026-05-12)

Phase 53.18 — tmux secret-leak fix (2026-05-11)

Brak zmiany zachowania endpointów — tylko refactor wewnętrznych ścieżek spawn.

Phase 53.16 — Sentinel Sprint 2 (2026-05-10)

Zmiany zachowania endpointów po hardeningu 13 × P1:

Phase 55 — Cosmic Editorial login (2026-05-13)

Nowe endpointy dla logowania magic-link:

Union EphemeralTokenType rozszerzony: zawiera teraz "magic_link" obok istniejących oauth_state / password_reset / email_verification / tfa_challenge.

Frontend (CosmicCard.jsx) obsługuje stan magic (60-sekundowy countdown do ponownego wysłania) oraz parametr URL ?magic_token= (auto-consume → login → animacja sukcesu).

Phase 56 — AI Interop / Project Context Export (2026-05-13)

Eksport dostępny tylko dla właściciela — sanitizowany snapshot projektu jako .md do przekazania zewnętrznemu AI (Gemini / ChatGPT / Perplexity / Claude.ai).

Alert: gdy właściciel przekroczy 3 eksporty w ciągu 24h AND prefs.notify_on_export = true (domyślnie OFF) — logActivity("export_alert", ...) przechodzi przez istniejący pipeline powiadomień TG Phase 53.10 (alertFired: true w treści odpowiedzi).

Multi-tier scanner (shared/secret-scanner.ts) — Tier 1 regex (PATTERN_REGISTRY z sanitizerem PII), Tier 2 entropia Shannona ≥4,5 bitów/znak na ciągach ≥20 znaków, Tier 3 heurystyki kontekstowe (key=/token:/secret=/password=). Allowlist: UUID / git SHA / SHA-256 / powtarzające się znaki / krótki hex / base58 o niskiej entropii. Poziomy ważności (critical/high/medium/low). Wydajność: <500 ms / 1 MB.

Migracja DB 024 — tabele export_audit_log + export_preferences.


Phase 57 — Platform Settings (Sentinel #103 follow-up, 2026-05-15)

Zarządzanie sekretami super-admina przez UI CRM zamiast ssh/edycji-.env/wklejania-na-chacie. Backend MVP (Stage 1 z 4 stages). Wszystkie endpointy bramkowane przez requireAdmin (Phase 53.15) — zwracają 403 Forbidden — admin only dla nie-admina, 401 Unauthorized bez JWT.

Lista twardych wykluczeń NEVER_EXPOSE: CRM_SECRET (podpisywanie JWT) + SECRET_ENCRYPTION_KEY (meta-klucz vault) — nawet żądanie admina z prawidłowym tokenem zwraca 400 "not managed". Log audytu tylko do dołączania (brak handlera UPDATE/DELETE), każda akcja (wliczając nieudane) zapisuje wiersz z IP + UA + email.

Migracja DB 026 — tabela platform_audit_log. Stage 2 (frontend PlatformSettings.jsx) — dostarczony 2026-05-15 (cbc8bac): siatka kart tylko dla adminów + modal rotacji (<input type="password"> + potwierdzenie przez powtórzenie) + szuflada audytu; wpis w sidebarze filtrowany przez userRole === "admin" pobierany z /api/auth/me.

Polish (2026-05-15, commit 56191b0) — Restrukturyzacja UI Platform Settings. Elementy odpowiedzi GET /api/crm/platform/settings zyskują 5 nowych pól: category (anthropic|oauth|telegram|email), usedIn (string[] — pliki/przepływy korzystające z klucza), getFromUrl (skąd pobrać nową wartość), effectAfterRotate, riskIfLeaked. Używane przez frontend do renderowania 4 pogrupowanych sekcji kart + zwijany panel pomocy per karta ze strukturalnym kontekstem (Used in / Get from / Effect / Risk). Brak zmian behawioralnych endpointów mutujących (PUT/POST/restart/test).

Refactor (2026-05-16) — wewnętrzne porządki shared/routes/platform.ts. Usunięto 39 linii (dodano 16), brak zmian w publicznym API. Sygnatury i odpowiedzi endpointów PUT/POST/restart/test/audit niezmienione. Udokumentowane tutaj tylko dlatego, że pre-push gate doc-coverage wyzwala na każdym diffie shared/routes/*.ts.

Backdated activity (#117, 2026-05-16)POST /api/mcp/issues/:project/:id/log akceptuje teraz opcjonalne pole ts (ciąg ISO-8601). Używane przez arc retro do rekonstrukcji historycznych wpisów z ich oryginalnymi znacznikami czasu. Wartości z przyszłości są po cichu obcinane do teraz wewnątrz addActivity() (ochrona przed grubymi palcami). Nieprawidłowy ISO → 400.

Stage 3 (2026-05-15) — hot-reload sekretów OAuth + Resend bez restartu. shared/auth.ts loadOAuthConfig() teraz czyta getSecret("GITHUB_CLIENT_ID/SECRET" | "GOOGLE_CLIENT_ID/SECRET") per wywołanie zamiast process.env. Callsites w master-bot/routes/auth.ts już wywoływały getOAuthConfig() per żądanie → 0 zmian callsites. RESEND_API_KEY już hot-reload przez shared/email.ts:47. Zmiana behawioralna: PUT /api/crm/platform/settings/{GITHUB_CLIENT_ID|GITHUB_CLIENT_SECRET|GOOGLE_CLIENT_ID|GOOGLE_CLIENT_SECRET|RESEND_API_KEY} wchodzi teraz w życie od następnego żądania, nie wymaga restartu. restartTargets dla tych 5 kluczy jest pusty → przycisk Restart w UI jest ukryty. Edge case: flow OAuth z state-tokenem wydanym przed rotacją może otrzymać 400 na callbacku przy wymianie kodu — ponowna próba użytkownika rozwiązuje problem. ANTHROPIC_API_KEY, PLATFORM_ANTHROPIC_KEY, MASTER_BOT_TOKEN, CITADEL_BOT_TOKEN pozostają wymagające restartu (czytane przy spawn child-bota / inicjalizacji TG long-poll).

Phase 57.3.5 cleanup (2026-05-16) — allowlist MANAGED_KEYS skrócony z 9 do 6. Usunięto: ANTHROPIC_API_KEY (operatorzy używają teraz jednego PLATFORM_ANTHROPIC_KEY zarówno dla trial-credits jak i platform inference; fallback .env nadal działa dla starszych ścieżek kodu dopóki Sage/Karpathy nie zmigrują), CITADEL_BOT_TOKEN (bot per-projekt należy do wpisów vault child:<name>:token, zarządzanych przez flow onboardingu workera — nie Platform Settings). MASTER_BOT_TOKEN zmienił przeznaczenie: label → "Telegram — System Monitor Bot", description → "Server health alerts + on-demand status probes (admin-only, not a chat bot)". Phase 58 doda pętlę monitorowania (alerty push dla crashu workera / dysku / RAM / brute-force SSH / bypass CF + polecenia /status, /health, /errors, /restart). Finalny zestaw: PLATFORM_ANTHROPIC_KEY + GITHUB×2 + GOOGLE×2 + MASTER_BOT_TOKEN + RESEND_API_KEY (refs #103).

Phase 63 — Konsolidacja UI/UX + Śledzenie zużycia tokenów (2026-05-21, #148)

Nowy endpoint:

Zmiany w claude-runner.ts:

Zmiany UI (nie API):

Arc Help (Phase 61 / #147)

Historia (Phase 61 / #153):

GDPR / Compliance (Sprint 1+2, #161–#174, 2026-05-22)

Right to Erasure — DELETE /api/auth/account (#162)

Trwale usuwa uwierzytelnionego użytkownika i wszystkie jego dane (RODO art. 17).

Password Version / Token Invalidation (#174)

Migracja 035 dodaje password_version INTEGER NOT NULL DEFAULT 0 do users. Przy zmianie hasła password_version jest inkrementowane. Payload JWT zawiera pole pv. crmAuthMiddleware waliduje pv względem DB przy każdym żądaniu, odrzucając tokeny wydane przed ostatnią zmianą hasła (401 "Token invalidated — please log in again"). Fail-open, gdy DB jest niedostępna.

Data Retention Cron (#168)

Master bot uruchamia codzienne czyszczenie przy starcie + co 24h. Limity retencji: chat_messages 180 dni (po timestamp), activity_log 365 dni (po created_at), auth_events 90 dni (po ts), token_usage_log 730 dni (po created_at unixepoch), export_audit_log 365 dni (po exported_at). Non-fatal — czyszczenie nie blokuje startu.

Email Compliance (#167)

Wszystkie wychodzące emaile transakcyjne (reset hasła, weryfikacja, magic-link) zawierają teraz:

Security — HIBP Breached Password Check (#171)

Przy POST /api/auth/register i POST /api/auth/reset-password przesłane hasło jest sprawdzane względem API k-anonimowości HaveIBeenPwned przed zapisaniem. Do HIBP wysyłane jest tylko pierwsze 5 znaków hex hasha SHA-1 — pełne hasło nigdy nie opuszcza serwera. Jeśli hasło występuje w jakiejkolwiek bazie wycieków z count > 0, żądanie jest odrzucane z HTTP 400: "This password was found in a known data breach. Please choose a different password." Fail-open przy timeout/błędzie HIBP (timeout 4s) — leżący HIBP nie blokuje rejestracji.

Data Portability — GET /api/auth/export (#163)

RODO art. 20 — prawo do przenoszenia danych. Zwraca ustrukturyzowany plik JSON ze wszystkimi danymi osobowymi, jakie Arc OS przechowuje o uwierzytelnionym użytkowniku.

Arc Help — Hardened System Prompt + Anti-Injection (#151)

Zmiany zachowania POST /api/crm/help/chat (bez zmiany powierzchni API):

Worker Discipline Hardening (#187, #188, #189, 2026-05-23)

Issue Status Expansion (#187)

PUT /api/mcp/issues/:project/:id akceptuje teraz rozszerzone wartości statusu:

Status Znaczenie
open Jeszcze nie rozpoczęte
in_progress Aktywnie w pracy (ustawiane przez arc issue take)
blocked Czeka na zewnętrzną zależność
deferred Odłożone (wcześniej przechowywane tylko jako tekst)
closed Zrobione

Nowe pole assignee: issues mają teraz assignee: string | null. Ustawiane przez arc issue take <id> lub --assignee <worker_id> w arc issue update.

Migracja 036: ALTER TABLE project_issues ADD COLUMN assignee TEXT (nullable, auto-aplikowana przy starcie serwera).

arc issue take <id> CLI Command (#187)

Skrót do przejęcia issue: ustawia assignee = current_worker_id, status = in_progress, loguje aktywność, zapisuje stan sesji. Równoważne z:

arc issue update <id> --status in_progress --assignee developer
arc issue log <id> "Taken by developer — status set to in_progress"

commit-msg Hook Validation (#187)

.githooks/commit-msg waliduje teraz odwołania do issues #N względem lokalnego issues/issues.json:

PROJECT_MANIFEST.md Bridge Injection (#188)

handleCliInit (shared/cli-routes.ts) czyta teraz PROJECT_MANIFEST.md z katalogu głównego projektu i wstrzykuje go do bloku CITADEL pod ## Project Context. Limit: 8000 znaków. Daje to workerom bridge (działającym na maszynach klientów przez arc) dostęp do kompaktowej architektury, wzorców bezpieczeństwa, struktury plików i kluczowych learnings z pełnego CLAUDE.md.

Umiejscowienie: po PROJECT_RULES.md, przed listą skilli.

context_assets Worker Config Field (#189)

Konfiguracja workera w workers_registry.json obsługuje opcjonalne context_assets: string[] — listę nazw skilli, które są automatycznie wstrzykiwane do każdej sesji bridge tego workera (bez konieczności arc skill <name>):

{
  "id": "developer",
  "context_assets": ["crm-api-reference", "archivist_system"]
}

Treść każdego skilla jest wstrzykiwana pod ### Auto-Loaded Skills → #### Skill: <name>, ucinana do 3000 znaków każda.

Phase 62 — Voice Input (#373, 2026-06-05)

Transkrypcja głosu w czasie rzeczywistym proxowana przez self-hosted serwer whisper.cpp (arc-whisper.service, port 19214, model ggml-base wstępnie załadowany).

POST /api/crm/voice/transcribe (#373, Phase 62.4)

Transkrybuje krótkie klipy głosowe (dyktowanie na chacie). Proxuje audio do lokalnego whisper-server i zwraca tekst.

Auth: token Bearer (lub query ?token=).

Body: multipart/form-data

Pole Typ Uwagi
audio Blob webm / ogg / wav. Max 25 MB.
locale string BCP-47, np. uk-UA, en-US. Przekazywany do whisper jako parametr language.

Response 200:

{ "transcript": "Що ти зробив вчора?" }

Kody błędów:

Kod Znaczenie
400 Brak pola audio lub locale
413 Audio powyżej 25 MB
429 Wyczerpany dzienny limit (60 min/użytkownik/dzień) LUB serwer zajęty (max 2 równoległe transkrypcje)
502 whisper-server zwrócił status inny niż 200
500 Nieoczekiwany błąd

Rate limit: voice_usage_log (migracja 051) śledzi przybliżone sekundy per (użytkownik, dzień), używając rozmiaru uploadu w bajtach jako proxy (zakłada kodek głosowy ~32 kbps, dokładność ±30%). Twardy limit: 3600 s / dzień. Żądania, które przekroczyłyby limit, zwracają 429 zanim trafią do whisper.

Uwaga architektoniczna: whisper działa wyłącznie na Contabo (nie w per-user kontenerach Hetzner). Bajty audio nigdy nie opuszczają Contabo; routing cloud-chat z Phase 70 widzi tylko wynikowy tekst. arc-whisper.service trzyma model ggml-base w pamięci, więc koszt per wywołanie to czysta inferencja (~3.4 s warm dla 11 s audio, 3.1× realtime na obecnej maszynie 6-vCPU EPYC).


Phase 73 — Transkrypcja + analiza spotkań (#377-#384, 2026-06-05)

Prześlij audio/wideo spotkania do projektu, otrzymaj transkrypcję whisper + podsumowanie Claude, opcjonalnie osadzone w RAG. Wszystkie trasy bramkowane przez canAccessProject (właściciel lub admin).

POST /api/crm/projects/:name/transcripts/upload (#377, Phase 73.1)

Upload multipart, zwraca 202 z transcript_id + job_id + status:'queued'. Job podejmowany przez kolejkę in-process (max 1 równolegle).

Pola body:

Limity: max 1 GB uploadu, allow-lista MIME (mp3/wav/m4a/aac/ogg/opus/flac + mp4/mov/webm/mkv).

Błędy: 400 (brak pola / zły MIME), 401, 413 (powyżej limitu), 500 (zapis na dysk).

GET /api/crm/projects/:name/transcripts (#379, Phase 73.3)

Lista transkrypcji projektu, paginacja kursorem. Query: ?limit=20&cursor=<id>. Zwraca {items: TranscriptSummary[], next_cursor: number|null}.

GET /api/crm/projects/:name/transcripts/:id (#379)

Pełny wiersz, w tym transcript_text, summary_json (sparsowany do obiektu) oraz frames_json (parsowany od Phase 73.4).

GET /api/crm/projects/:name/transcripts/job/:jobId/progress (#379)

Strumień SSE postępu joba. Wysyła event: progress z {status, progress_pct, step_label, error} przy każdej zmianie dowolnego pola, plus heartbeaty komentarzem : keep-alive co 1s, aby 10-sekundowy idleTimeout Buna nie zabijał długich przebiegów whisper. Zamyka się przez event: end, gdy status jest terminalny.

Auth: przeglądarkowy EventSource dokleja ?token=<bearer> (nie może ustawić nagłówka Authorization).

Statusy terminalne: done (po Phase 73.6 embed do RAG + sprzątanie plików), failed. Uwaga: summarized to krok przejściowy — SSE pozostaje otwarte przez embeddingdone. Przycisk wysyłki we frontendzie odblokowuje się przy summarized (nie czeka na RAG).

Maszyna stanów (Phases 73.1-73.6)

queued
  → extracting_audio    (ffmpeg → 16 kHz mono WAV)
  → transcribing        (whisper-cli -t 4)
  → (video) extracting_frames → frames_extracted   (ffmpeg scene-change)
            → vision_analyzing → vision_analyzed   (Phase 73.4 Claude vision per frame)
  → (audio) transcribed
  → summarizing         (Claude Sonnet → summary_json, receives vision frames as context)
  → summarized
  → embedding           (Phase 73.6 Cohere upsert via shared/rag.ts, skipped if embed_to_rag=0)
  → done                (source file + frames dir deleted — CEO decision D4)

Kształt JSON klatek vision (Phase 73.4, #380)

Przechowywany jako string JSON w transcripts.frames_json (parsowany z powrotem do obiektu przez GET /transcripts/:id).

[
  { "ts_ms": 3000, "description": "Slide titled 'Q3 Revenue' with bar chart showing 30% growth." },
  { "ts_ms": 6000, "description": "Architecture diagram with three boxes labeled API/Worker/DB." }
]

Twardy limit MAX_FRAMES=50 na transkrypcję (~$0.15 w najgorszym przypadku przy typowych cenach Sonnet vision). Klatki ponad limit są po cichu odrzucane, ostatni zachowany opis dostaje sufiks [+N more frames dropped]. Błędy per klatka stają się stringami [vision failed: <msg>] — nie przerywają przebiegu. Klatki opisane jako "No informational content" to obraz tylko z kamerki lub dekoracyjny.

Kształt JSON podsumowania (Phase 73.5, #381)

Przechowywany jako string JSON w transcripts.summary_json. Parsowany z powrotem do obiektu przez GET /transcripts/:id.

{
  "tldr": "1-2 sentence executive summary",
  "key_points": ["..."],
  "action_items": [{"task": "...", "owner": "name or null"}],
  "decisions": ["..."],
  "topics": ["..."],
  "model": "claude-sonnet-4-5",
  "generated_at": "2026-06-05T20:04:15.573Z"
}

Rozwiązywanie klucza Anthropic odzwierciedla shared/worker-spawn.ts: BYOK account_settings.anthropic_key (odszyfrowany, jeśli zaszyfrowany), fallback PLATFORM_ANTHROPIC_KEY dla właścicieli w trial-mode. Błędy podsumowania nie są fatalne — transcript_text pozostaje nietknięty, status cofa się do transcribed/frames_extracted, aby użytkownik mógł ponowić po naprawieniu klucza.

Phase 78 — Notes: Knowledge Collections (#394–#404, 2026-06-08)

Notatki per projekt w stylu NotebookLM. Każda notatka to kolekcja źródeł (wideo, audio, YouTube, web, PDF, DOCX, TXT, obraz) ze wspólnym indeksem RAG i chatem.

GET /api/crm/projects/:name/notes

Zwraca wszystkie notatki projektu. Wymagana autoryzacja + canAccessProject.

Response 200:

[{ "id": 1, "title": "Sprint planning", "description": null, "created_at": "...", "source_count": 3 }]

POST /api/crm/projects/:name/notes

Utwórz nową notatkę.

Body: { "title": "string", "description": "string?" }
Response 201: { "id": 1, "title": "Sprint planning" }

GET /api/crm/projects/:name/notes/:id

Szczegóły notatki ze źródłami, powiązaniami ze zgłoszeniami i historią chatu.

Response 200:

{
  "id": 1, "title": "Sprint planning",
  "sources": [{ "id": 1, "source_type": "youtube", "title": "My video", "url": "...", "status": "done", "duration_seconds": 3600 }],
  "issue_links": [{ "issue_id": 42, "title": "Issue title" }],
  "chats": [{ "role": "user", "content": "Summarize", "created_at": "..." }]
}

DELETE /api/crm/projects/:name/notes/:id

Usuń notatkę i wszystkie źródła/chaty. Kaskaduje na note_sources, note_chats, note_issue_links.

POST /api/crm/projects/:name/notes/:id/sources

Dodaj źródło (upload pliku lub URL).

Content-Type: multipart/form-data LUB application/json

Response 201: { "source_id": 5, "status": "queued" }

Przetwarzanie jest asynchroniczne. Odpytuj GET /notes/:id, aż source.status === "done".

PATCH /api/crm/projects/:name/notes/:id/sources/:sourceId

Zmień nazwę źródła (edycja tytułu inline).

Body: { "title": "New name" }
Response 200: {}
Przekaż pusty string lub null, aby zresetować do domyślnej nazwy pliku/URL.

DELETE /api/crm/projects/:name/notes/:id/sources/:sourceId

Usuń źródło i jego treść.

GET /api/crm/projects/:name/notes/:id/sources/:sourceId/progress

Strumień SSE postępu przetwarzania źródła.

Zdarzenia: progress { "status": "processing"|"done"|"error", "message": "..." }

POST /api/crm/projects/:name/notes/:id/chat

Wyślij wiadomość do chatu notatki. Odpowiedź jako strumień SSE.

Body:

{
  "message": "Summarize all sources",
  "selectedSourceIds": [1, 3]
}

selectedSourceIds jest opcjonalne — pomiń, aby uwzględnić wszystkie źródła.

Zdarzenia SSE:

Strategia RAG: wyszukiwanie sqlite-vec po embeddingach note_source → fallback: bezpośrednie wstrzyknięcie content_text (max 80 K znaków), gdy wyszukiwanie wektorowe jest niedostępne lub bez wyników. Anty-halucynacyjny guard w system promptcie wstrzykiwany, gdy uwzględnione są nieprzetworzone źródła.

Tool use — create_issue: Claude może tworzyć zgłoszenia projektu z chatu. Multi-turn: tura 1 streamuje do wywołania narzędzia, backend wykonuje (issueQueries.nextId + issueQueries.insert), tura 2 wznawia streaming z wstrzykniętym wynikiem narzędzia.

Maszyna stanów statusu źródła

queued → processing → done
                    ↘ error

Wartości pola status źródła:

Strategia transkrypcji YouTube (Phase 78.3)

  1. npm youtube-transcript: kaskada języków ["en", "en-US", "en-GB"] → fallback dowolny
  2. API Supadata.ai: GET https://api.supadata.ai/v1/youtube/transcript?url=...&text=true&lang=en → fallback bez parametru lang
  3. yt-dlp + Whisper: ostateczny fallback dla wideo bez napisów

Priorytet: preferuj angielskie napisy, aby uniknąć auto-tłumaczonych transkrypcji arabskich/innych języków.