Backend split: kulanz-online ↔ kulanz-backend
Ownership
| Concern | Owner |
|---|---|
| Drizzle schema + migrations | kulanz-online (src/server/db/schema/) |
| SQLAlchemy mirrors | kulanz-backend (src/kulanz_backend/db/models/) — update in the same PR as Drizzle changes |
| Dashboard UI | kulanz-online — calls backend with apiClient + Bearer JWT |
| Agents + Python REST | kulanz-backend only |
| Browser auth | Supabase session in the browser → Authorization: Bearer on api.kulanz.audla.ai |
| iOS native app | kulanz-app → https://api.kulanz.audla.ai /api/v1/... |
Client API pattern (web)
import { createOrgApi } from '@/lib/org-api'
const orgApi = createOrgApi(organizationSlug)
await orgApi.get('/journal-entries?limit=50')
window.__KULANZ_CONFIG__.backendUrl is set from NEXT_PUBLIC_KULANZ_BACKEND_URL in the root layout.
The dashboard calls kulanz-backend for org domain APIs, auth profile/password/bootstrap, members, uploads, and expense journal export. Remaining kulanz-online /api/* route handlers are webhook proxies only (prefer backend URLs in production).
iOS (kulanz-app)
- Production:
https://api.kulanz.audla.ai - Debug:
http://127.0.0.1:8000 - Contract:
docs/api-contracts/ios-v1.md
Local development
cd kulanz-backend && uv run kulanz-api(port 8000)- In
kulanz-online/.env.local:KULANZ_BACKEND_URL=http://127.0.0.1:8000(server/scripts if needed)NEXT_PUBLIC_KULANZ_BACKEND_URL=http://127.0.0.1:8000NEXT_PUBLIC_SUPABASE_URL/NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYKULANZ_BACKEND_SERVICE_TOKEN(service-to-service only)
Framework: Next.js App Router (app/). UI code lives in app/**, src/components, src/lib, src/views.
Production
| Setting | Value |
|---|---|
| Web/iOS API base | https://api.kulanz.audla.ai |
| Accounting webhooks | https://api.kulanz.audla.ai/api/v1/webhooks/accounting/transactions |
| Stripe webhooks | https://api.kulanz.audla.ai/api/v1/webhooks/stripe (preferred); online proxy may still exist) |
| Sales webhooks | Prefer backend; online /api/webhooks/sales/:source until fully migrated |
| Contact form | POST https://api.kulanz.audla.ai/api/v1/contact |
Agents
Web UI calls POST /api/v1/organizations/{slug}/agents/{name}/run (and COA sessions under /api/v1/.../agents/coa_assistant/sessions) directly on the Python API.
Import scripts
Historical import and maintenance scripts under kulanz-online/scripts/ may still use Drizzle/Postgres locally; new posting logic belongs in kulanz-backend.