Skip to content

Finance Portal

The Finance Portal (apps/finance-portal, oi-sys-finance) is a dedicated Next.js application for the Finance / Accounting team. It surfaces TigerBeetle double-entry ledger data, accounts receivable aging, statutory financials, reconciliation workflows, and a flexible report builder — all scoped behind the finance_analyst JWT role.

EnvironmentURL
Productionhttps://finance.openinsure.dev
Local devhttp://localhost:3006

Use the standard OpenInsure sign-in flow. The Finance Portal sets the oi_finance_token cookie, verified against FINANCE_JWT_SECRET.

GET https://auth-dev.openinsure.dev/api/auth/sign-in/microsoft
?callbackURL=https://finance.openinsure.dev/api/auth/callback
JWT roleSpiceDB org relationDescription
finance_analystfinanceStandard finance team access
superadminFull access
systemMachine-to-machine (M2M)

The finance_analyst role is enforced in apps/finance-portal/lib/auth.ts. The middleware rejects any JWT that does not carry one of these three roles and redirects unauthenticated requests to /login.

SectionPageRoute
OverviewDashboard/
AnalyticsEntities/analytics/entities
AnalyticsPipeline/analytics/pipeline
AnalyticsCaptive/analytics/captive
ReportsAR Aging/reports/ar-aging
ReportsFinancials/reports/financials
ReportsReconciliation/reports/reconciliation
ReportsStatutory/reports/statutory
ReportsChart of Accounts/reports/chart-of-accounts
ReportsReport Builder/reports/builder

Three-pane layout (flex h-full overflow-hidden) with period toggle and live non-pay queue:

Left pane (w-64) — KPI sidebar:

  • Period toggle — YTD / MTD / QTD links (?period=ytd URL param), default ytd
  • KPICard for GWP (Gross Written Premium) — neutral status
  • KPICard for AR Outstandingstatus='warn' if balance > 0
  • KPICard for Loss Ratiostatus='warn' if > 65%, status='critical' if > 85%
  • Navigation links to A/R Aging, Pipeline, and Financials

Center pane (flex-1) — AR Aging visualization:

  • Fixed header with total outstanding balance
  • CSS horizontal bar chart — 5 aging buckets (Current / 31–60 / 61–90 / 91–120 / 120+), bars sized relative to the largest bucket, no chart library
  • Top 5 overdue accounts mini-table — Invoice #, Insured, Outstanding (cents), Days overdue
    • > 90d → red, > 30d → amber, else muted
    • “View all N invoices →” link to full AR Aging report

Right pane (w-72)NonPayQueue client component:

  • TanStack Query polling (refetchInterval: 60_000) with initialData from server render
  • Groups entries by dunning stage: Stage 3 Critical (red) first, Stage 2 Warning (amber) next
  • Each card shows policy number, insured name, amount due, and days remaining on grace period
  • Clicking a card opens a shadcn Dialog with amount due, grace period end, dunning stage, and optional note input
  • If queue empty: green checkmark (“Queue is clear”)
  • “Full A/R Aging Report” link at bottom

API calls (parallel via Promise.all):

  • GET /v1/analytics/mga?period=${period} — GWP + loss ratio
  • GET /v1/reports/ar-aging — bucket bars + top 5 overdue rows (amounts in cents)
  • GET /v1/billing/non-pay-queue — non-pay entries for right pane (amountDue in dollars)

Source: apps/finance-portal/app/(dashboard)/non-pay-queue.tsx (client island)

Accounts receivable aging report grouped into 30/60/90/90+ day buckets. Fetches from GET /v1/reports/ar-aging. Exportable as CSV via the ExportToolbar component.

Income statement and balance sheet views sourced from the TigerBeetle ledger (GET /v1/reports/financials). Supports period selection (MTD, QTD, YTD, custom range). Income statement and balance sheet views are backed by getTrialBalance() from the TigerBeetle ledger client, which queries all 9 account types concurrently and returns debit/credit totals with a balanced boolean.

The 9 account types queried are: Carrier Payable (1), MGA Fiduciary (2), MGA Revenue (3), Producer Payable (4), Tax Authority Payable (5), Loss Fund (6), Claims Paid (7), Reserves (8), and Reinsurer Payable (9). Each row in the trial balance reports total debits, total credits, and net balance in integer cents.

Displays unreconciled ledger entries with status badges. Individual entries can be marked reconciled via PATCH /v1/reports/reconciliation/:id. Changes optimistically update the UI.

State statutory financial exhibits for regulatory filing. Data from GET /v1/reports/statutory. Read-only; the actual filing workflow lives in the Compliance Portal.

Chart of Accounts (/reports/chart-of-accounts)

Section titled “Chart of Accounts (/reports/chart-of-accounts)”

Full account hierarchy from TigerBeetle. Sortable and searchable.

The TigerBeetle ledger client provides two audit-grade reporting methods:

  • Trial Balance (getTrialBalance()) — Queries all 9 account types concurrently via Promise.all. Each row contains the account ID, account type code (1-9), human-readable name, total credits, total debits, and net balance — all in integer cents. Returns a balanced flag confirming debits equal credits across the organization’s subledger.

    CodeAccount Name
    1Carrier Payable
    2MGA Fiduciary
    3MGA Revenue
    4Producer Payable
    5Tax Authority Payable
    6Loss Fund
    7Claims Paid
    8Reserves
    9Reinsurer Payable
  • Journal Entries (getJournalEntries()) — Filtered transfer history through the MGA fiduciary hub account. Supports from/to date-range filtering (converted to TigerBeetle nanosecond timestamps) and a limit parameter (default 100). Each entry includes the transfer ID, debit/credit account IDs, amount in cents, transfer code, and timestamp.

Flexible pivot-style report builder. Saved report configurations are persisted via POST /v1/reports/builder. Results can be exported to CSV or PDF.

The Finance Portal proxies API requests through apps/finance-portal/app/api/[...path]/route.ts, gated by lib/proxy-allowlist.ts:

GET /v1/analytics/entities
GET /v1/analytics/pipeline
GET /v1/analytics/captive
GET /v1/reports/ar-aging
GET /v1/reports/statutory
GET /v1/reports/financials
GET /v1/reports/reconciliation
PATCH /v1/reports/reconciliation/:id
GET /v1/reports/chart-of-accounts
GET /v1/reports/builder
POST /v1/reports/builder
GET /v1/billing/non-pay-queue
GET /v1/ledger/accounts
GET /v1/ledger/transfers
GET /v1/ledger/trial-balance
GET /v1/ledger/journal-entries
GET /v1/ledger/lookup-transfers
GET /v1/ledger/:orgId/transfers

Any request not in the allowlist returns 403 Forbidden.

VariableDescription
FINANCE_JWT_SECRETJWT signing secret — set via wrangler secret put
API_URLAPI worker base URL (e.g., https://api.openinsure.dev)
NEXT_PUBLIC_APP_NAME"Finance Portal" — set in wrangler.toml [vars]
Terminal window
# Set the JWT secret (production)
wrangler secret put FINANCE_JWT_SECRET --name oi-sys-finance
# Local dev (.env.local)
FINANCE_JWT_SECRET=9d79c38aa7d57bd24a1afe213848b2b935519afb08a3923ac112aed71fd5bc21
API_URL=http://localhost:8787
DEMO_MODE=true

The Finance Portal deploys as an OpenNext Cloudflare Worker (oi-sys-finance):

Terminal window
# Build + deploy
cd apps/finance-portal
npx opennextjs-cloudflare deploy -- --keep-vars
# Or via pnpm filter
pnpm --filter @openinsure/finance-portal deploy

CI deploys automatically from master when apps/finance-portal/ or shared packages change. See CI/CD for the full pipeline.

Compliance Portal

Producer licensing, state filings, and compliance analytics.

Authentication

JWT roles, portal cookies, and SpiceDB authorization.