Overview
apps/finance and the Finance experience inside apps/web are paired Finance
hosts. They must stay 1:1 for product features, data behavior, mutations,
permissions, route behavior, and user-facing workflow updates.
Canonical workspace routes include:
/{wsId}/{wsId}/transactions/{wsId}/wallets/{wsId}/invoices/{wsId}/categories/{wsId}/tags/{wsId}/recurring/{wsId}/budgets/{wsId}/analytics/{wsId}/debts
apps/web /{wsId}/finance/* routes render the same shared Finance product
surface inside the normal platform dashboard shell. apps/finance keeps the
shorter standalone route shape without the /finance segment.
Use route prefixes instead of forks:
apps/web:financePrefix="/finance"andFinanceRouteProvider prefix="/finance".apps/finance:financePrefix=""andFinanceRouteProvider prefix="".
/{wsId}/finance/transactions/categories, should redirect inside apps/web to
/{wsId}/finance/categories while preserving query strings.
API Ownership
Protected Finance APIs stay centralized inapps/web.
apps/financeowns the standalone workspace shell, Finance UI route wrappers, local/verify-tokenhandoff, and local app-session logout.apps/webowns platform-shell Finance route wrappers under/{wsId}/finance/*.apps/financeshould keep only host-local auth/session routes under its localapitree.- Product CRUD, reporting, invoice, wallet, transaction, tag, promotion,
inventory product, settings, and invoice helper APIs should be added in
apps/weband consumed through forwarded/api/*requests or@tuturuuu/internal-api. - Finance app-session cookies are accepted by central Finance API routes and
invoice-adjacent helper routes, then route-level permissions are enforced by
apps/web. - Finance transaction attachment reads must authorize through the same
authenticated transaction visibility path as normal transaction reads. Do not
grant storage list, metadata, or signed-read URL access from coarse
view_transactionsorupdate_transactionspermissions alone; reuseget_wallet_transactions_with_permissionswithp_transaction_idsso wallet whitelists, viewing windows, and granular income/expense permissions stay aligned. - Finance transaction attachment uploads have server-side limits: 10 files per transaction and 50 MB per file. The signed-upload route must reject over-limit declared sizes before issuing a URL, and finalize must inspect the actual stored object size/count and delete over-limit uploads before any follow-up processing.
- Finance transaction type filters must not classify confidential amount signs
for callers without
view_confidential_amount. RPCs that acceptp_transaction_typeshould apply income/expense predicates only when the row is non-confidential or the caller can view confidential amounts; otherwise typed filters should omit those redacted rows instead of using the raw amount sign. - Finance invoice customer IDs must be validated against
workspace_userswith the route workspace before insert, and admin-backed invoice reads should resolve customer display fields with an explicitws_idfilter instead of a nested service-role join oncustomer_id.
apps/web, expose a helper in packages/internal-api when shared UI
needs it, and consume it from both hosts through TanStack Query or shared server
components.