---
# === IDENTITY ===
id: software/startup-dashboard/financial-operations-dashboard/2026
canonical_question: "How do I build a finance dashboard — accounting integration, actual vs plan, runway calculator, unit economics?"
aliases:
  - "financial operations dashboard setup"
  - "startup finance dashboard build"
  - "runway calculator dashboard implementation"
  - "unit economics dashboard with QuickBooks or Xero"
entity_type: execution_recipe
domain: software > startup-dashboard > financial-operations-dashboard
region: global
jurisdiction: global
temporal_scope: 2025-2026

# === VERIFICATION ===
last_verified: 2026-03-12
confidence: 0.88
version: 1.0
first_published: 2026-03-12

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: evolving
  last_breaking_change: "Xero API moved to OAuth 2.0 only (legacy OAuth 1.0 removed Q2 2025); QuickBooks Online deprecated v3 minor endpoints (Q3 2025)"
  next_review: 2026-09-08
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Accounting API rate limits: QuickBooks Online = 500 requests/minute, Xero = 60 requests/minute. Batch requests and implement retry logic."
  - "Financial data must be read-only in the dashboard — never write back to accounting systems from the dashboard to prevent data corruption."
  - "Runway calculations assume linear burn rate by default. For startups with seasonal revenue or stepped hiring, use segmented projections."
  - "Unit economics (LTV, CAC) require at least 6 months of customer data for statistical validity. 3 months gives directional estimates only."
  - "All financial figures must use the accounting system as source of truth — never derive P&L from payment processor data alone."

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs financial planning or modeling, not a dashboard"
    use_instead: "finance/modeling/startup-financial-model/2026"
  - condition: "User wants only a runway calculator without full financial dashboard"
    use_instead: "a standalone runway calculator (current cash / monthly net burn)"
  - condition: "User is building enterprise FP&A, not a startup finance dashboard"
    use_instead: "Search knowledgelib.io for enterprise FP&A architecture — no dedicated unit yet"

# === AGENT HINTS ===
inputs_needed:
  - key: accounting_platform
    question: "Which accounting system does the company use?"
    type: choice
    options: ["QuickBooks Online", "Xero", "Wave", "FreshBooks", "no accounting system yet"]
  - key: technical_skill
    question: "What is the user's technical skill level?"
    type: choice
    options: ["non-technical (no-code only)", "semi-technical (can edit code)", "developer (can write code)"]
  - key: budget_for_tools
    question: "What's the dashboard tool budget?"
    type: choice
    options: ["free tier only", "up to $50/month", "up to $200/month", "no limit"]
  - key: revenue_model
    question: "What's the primary revenue model?"
    type: choice
    options: ["SaaS/subscription", "transactional/marketplace", "one-time sales", "pre-revenue"]

# === EXECUTION METADATA ===
execution:
  required_inputs:
    - name: "Accounting system with API access"
      source: "user's accounting platform"
      format: "OAuth2 credentials"
    - name: "Annual budget/plan"
      source: "user's financial plan"
      format: "Monthly P&L budget breakdown"
    - name: "Current bank balance"
      source: "user's bank account or accounting system"
      format: "Dollar amount as of last reconciliation"

  outputs:
    - name: "Financial operations dashboard"
      format: "deployed web application"
      description: "Live dashboard showing P&L actual vs plan, cash runway projection, burn rate trend, unit economics (LTV/CAC), and revenue metrics"
    - name: "Automated accounting sync"
      format: "scheduled ETL job"
      description: "Daily extraction from accounting system to dashboard data store"
    - name: "Runway projection model"
      format: "SQL-based calculation"
      description: "Auto-updating runway in months based on current burn rate and cash balance"

  tools_required:
    - name: "Retool or Metabase"
      purpose: "Dashboard UI builder"
      tier: "free"
      cost: "$0 (free tier) to $10/user/month"
      alternatives: ["Google Looker Studio", "Grafana", "Preset"]
    - name: "PostgreSQL"
      purpose: "Financial data warehouse"
      tier: "free"
      cost: "$0 (Supabase free tier) to $25/month"
      alternatives: ["BigQuery", "DuckDB", "SQLite"]
    - name: "Unified accounting API (Apideck or Merge)"
      purpose: "Normalize QuickBooks/Xero/Wave data into single schema"
      tier: "free"
      cost: "$0 (limited) to $49/month"
      alternatives: ["Direct API integration", "n8n custom workflow"]

  credentials_needed:
    - service: "QuickBooks Online"
      type: "OAuth2 client ID + secret"
      where_to_get: "https://developer.intuit.com (My Apps > Create an App)"
      free_tier_limits: "Sandbox: unlimited; Production: 500 req/min"
    - service: "Xero"
      type: "OAuth2 client ID + secret"
      where_to_get: "https://developer.xero.com (My Apps)"
      free_tier_limits: "60 requests/minute per tenant"

  estimated_duration: "5-8 hours for initial setup; 2-3 hours for runway model calibration"
  estimated_cost: "$0 (all free tiers) to $75/month (paid tiers)"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/startup-dashboard/financial-operations-dashboard/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-12)"

# === RELATED UNITS ===
related_kos:
  depends_on:
    - id: "software/startup-dashboard/sales-operations-dashboard/2026"
      label: "Sales dashboard provides revenue pipeline data"
    - id: "software/startup-dashboard/marketing-operations-dashboard/2026"
      label: "Marketing dashboard provides spend data"
  feeds_into:
    - id: "software/startup-dashboard/executive-command-center-design/2026"
      label: "Executive command center design — cross-functional 8-12 KPI selection, RAG thresholds, alert triggers and action rules for a single aggregated screen"
  related_to:
    - id: "finance/saas-metrics/cac-ltv-benchmarks/2026"
      label: "B2B SaaS CAC and LTV benchmarks — the core unit-economics metrics — and how to apply them"
  alternative_to:
    - id: "business/startup-metrics/dashboard-design-for-startups/2026"
      label: "Startup dashboard design with audience-specific views including a dedicated investor monthly-reporting view built on month-end snapshots rather than real-time data"

# === SOURCES ===
sources:
  - id: src1
    title: "QuickBooks Online API Reference"
    author: Intuit
    url: https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/account
    type: official_docs
    published: 2025-11-01
    reliability: authoritative
  - id: src2
    title: "Xero API Documentation"
    author: Xero
    url: https://developer.xero.com/documentation/api/accounting/overview
    type: official_docs
    published: 2025-12-01
    reliability: authoritative
  - id: src3
    title: "How to Build a Runway Calculator for Startups"
    author: Model Monkey
    url: https://modelmonkey.io/guides/build-runway-calculator
    type: industry_guide
    published: 2025-10-15
    reliability: high
  - id: src4
    title: "Startup Cash Runway Model 2026"
    author: The VC Corner
    url: https://www.thevccorner.com/p/startup-cash-runway-model-2026
    type: industry_guide
    published: 2025-12-01
    reliability: high
  - id: src5
    title: "How to Calculate Runway: Guide for Founders"
    author: Puzzle
    url: https://puzzle.io/blog/how-to-calculate-your-runway-a-guide-for-founders
    type: industry_guide
    published: 2025-09-01
    reliability: high
  - id: src6
    title: "Accounting Dashboard Guide"
    author: Fuel Finance
    url: https://fuelfinance.me/blog/accounting-dashboard
    type: industry_guide
    published: 2025-10-01
    reliability: high
---

# Financial Operations Dashboard

## Purpose

This recipe produces a startup financial operations dashboard that integrates with the accounting system (QuickBooks or Xero), displays actual vs planned P&L, calculates real-time cash runway projections, tracks burn rate trends, and computes unit economics (LTV, CAC, CAC payback). The output is a deployed internal tool giving founders and finance leads a single source of truth for financial health, replacing manual spreadsheet updates. [src6]

## Prerequisites
<!-- Agents: verify ALL prerequisites before executing. Missing prerequisites = failed execution. -->

- [ ] **Accounting system** with API access — [QuickBooks Online](https://quickbooks.intuit.com) or [Xero](https://www.xero.com) with OAuth2 app configured
- [ ] **At least 3 months of bookkeeping data** in the accounting system — required for meaningful trend analysis; 6+ months for unit economics
- [ ] **Annual budget or financial plan** — monthly P&L budget by category (revenue, COGS, payroll, marketing, G&A)
- [ ] **Current bank balance** — as of last reconciliation, to seed runway calculation
- [ ] **PostgreSQL database** — free tier at [Supabase](https://supabase.com) or [Neon](https://neon.tech)
- [ ] **Dashboard tool account** — [Retool](https://retool.com) or [Metabase](https://www.metabase.com)
- [ ] **Payment processor access** (optional) — Stripe API for real-time revenue if SaaS model

## Constraints
<!-- Hard rules. Agents: enforce throughout execution. -->

- Accounting API rate limits: QuickBooks = 500 requests/minute, Xero = 60 requests/minute. Use batch endpoints and cache P&L data daily. [src1]
- Dashboard must be READ-ONLY on accounting data. Never write back to accounting systems — data corruption risk is catastrophic and auditors will flag it. [src2]
- Runway calculation assumes linear burn rate by default. Startups with seasonal revenue or planned hiring steps need segmented monthly projections. [src3]
- Unit economics (LTV, CAC) require 6+ months of cohort data for validity. With < 3 months, show directional estimates with explicit confidence warnings. [src5]
- All financial figures must use the accounting system as the canonical source. Payment processor revenue (Stripe) should reconcile to accounting, not replace it.

## Tool Selection Decision

```
Which path?
├── User is non-technical AND budget = free
│   └── PATH A: No-Code Free — Google Sheets + QBO/Xero export + manual update
├── User is non-technical AND budget > $0
│   └── PATH B: No-Code Paid — Retool + Supabase + Apideck ($49/mo)
├── User is semi-technical or developer AND budget = free
│   └── PATH C: Code + Free — Metabase + PostgreSQL + cron sync script
└── User is developer AND budget > $0
    └── PATH D: Code + Paid — Retool + PostgreSQL + n8n + direct API
```

| Path | Tools | Cost | Speed | Output Quality |
|------|-------|------|-------|---------------|
| A: No-Code Free | Google Sheets + CSV exports | $0 | 2-3 hours | Basic — manual weekly updates |
| B: No-Code Paid | Retool + Supabase + Apideck | $49/mo | 4-6 hours | Good — auto-sync, interactive |
| C: Code + Free | Metabase + PostgreSQL + cron | $0 | 6-8 hours | Good — full SQL control |
| D: Code + Paid | Retool + PostgreSQL + n8n | $25-50/mo | 5-7 hours | Excellent — real-time, custom |

## Execution Flow

### Step 1: Design the Financial Data Model

**Duration**: 30-60 minutes
**Tool**: SQL client

```sql
-- Financial operations data model
CREATE TABLE chart_of_accounts (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  type TEXT NOT NULL, -- 'revenue', 'cogs', 'expense', 'asset', 'liability', 'equity'
  sub_type TEXT, -- 'payroll', 'marketing', 'hosting', 'rent', 'saas_tools'
  accounting_id TEXT UNIQUE -- ID from QuickBooks/Xero
);

CREATE TABLE actuals_monthly (
  id SERIAL PRIMARY KEY,
  month DATE NOT NULL, -- first day of month
  account_id TEXT REFERENCES chart_of_accounts(id),
  amount NUMERIC(14,2) NOT NULL,
  currency TEXT DEFAULT 'USD',
  synced_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE budget_monthly (
  id SERIAL PRIMARY KEY,
  month DATE NOT NULL,
  account_id TEXT REFERENCES chart_of_accounts(id),
  amount NUMERIC(14,2) NOT NULL,
  notes TEXT
);

CREATE TABLE cash_balance (
  id SERIAL PRIMARY KEY,
  date DATE NOT NULL,
  balance NUMERIC(14,2) NOT NULL,
  source TEXT DEFAULT 'accounting', -- 'accounting', 'bank_feed', 'manual'
  synced_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE revenue_metrics (
  id SERIAL PRIMARY KEY,
  month DATE NOT NULL,
  mrr NUMERIC(12,2) DEFAULT 0,
  arr NUMERIC(14,2) DEFAULT 0,
  new_mrr NUMERIC(12,2) DEFAULT 0,
  churned_mrr NUMERIC(12,2) DEFAULT 0,
  expansion_mrr NUMERIC(12,2) DEFAULT 0,
  customer_count INTEGER DEFAULT 0,
  new_customers INTEGER DEFAULT 0,
  churned_customers INTEGER DEFAULT 0
);

CREATE TABLE unit_economics (
  id SERIAL PRIMARY KEY,
  month DATE NOT NULL,
  ltv NUMERIC(10,2) DEFAULT 0,
  cac NUMERIC(10,2) DEFAULT 0,
  ltv_cac_ratio NUMERIC(5,2) DEFAULT 0,
  cac_payback_months NUMERIC(5,1) DEFAULT 0,
  gross_margin_pct NUMERIC(5,2) DEFAULT 0,
  arpu NUMERIC(10,2) DEFAULT 0
);

-- Indexes
CREATE INDEX idx_actuals_month ON actuals_monthly(month);
CREATE INDEX idx_actuals_account ON actuals_monthly(account_id);
CREATE INDEX idx_budget_month ON budget_monthly(month);
CREATE INDEX idx_cash_date ON cash_balance(date);
CREATE INDEX idx_revenue_month ON revenue_metrics(month);
CREATE INDEX idx_unit_econ_month ON unit_economics(month);
```

**Verify**: All 6 tables created successfully.
**If failed**: Check PostgreSQL connection and permissions.

### Step 2: Build Accounting Data Sync

**Duration**: 1-2 hours
**Tool**: n8n, custom script, or Apideck

QuickBooks Online sync:

```javascript
// QuickBooks Profit & Loss report API
// GET https://quickbooks.api.intuit.com/v3/company/{realmId}/reports/ProfitAndLoss
// Headers: Authorization: Bearer {ACCESS_TOKEN}
// Params: start_date=2026-01-01&end_date=2026-03-31&summarize_column_by=Month

// Extract monthly P&L data
const columns = report.Columns.Column;
const rows = report.Rows.Row;

for (const row of rows) {
  if (row.type === 'Data') {
    const accountName = row.ColData[0].value;
    const monthlyAmounts = row.ColData.slice(1).map((col, i) => ({
      month: columns[i + 1].value, // "Jan 2026", "Feb 2026", etc.
      amount: parseFloat(col.value) || 0
    }));
    // Upsert to actuals_monthly
  }
}
```

Xero sync:

```javascript
// Xero Profit & Loss report
// GET https://api.xero.com/api.xro/2.0/Reports/ProfitAndLoss
// Headers: Authorization: Bearer {ACCESS_TOKEN}, xero-tenant-id: {TENANT_ID}
// Params: fromDate=2026-01-01&toDate=2026-03-31&periods=3&timeframe=MONTH

// Extract P&L rows and map to actuals_monthly
const sections = report.Reports[0].Rows;
for (const section of sections) {
  if (section.RowType === 'Section') {
    for (const row of section.Rows || []) {
      if (row.RowType === 'Row') {
        const accountName = row.Cells[0].Value;
        const amounts = row.Cells.slice(1).map(cell => parseFloat(cell.Value) || 0);
        // Map to monthly actuals
      }
    }
  }
}
```

Cash balance sync (daily):

```javascript
// QuickBooks: GET /v3/company/{realmId}/reports/BalanceSheet
// Extract "Total Bank Accounts" row for current cash position
// OR: GET /v3/company/{realmId}/query?query=SELECT * FROM Account WHERE AccountType = 'Bank'
// Sum all bank account balances

// Xero: GET /api.xro/2.0/Reports/BalanceSheet
// Extract "Bank Accounts" section total
```

Schedule: daily at 7:00 AM for P&L and cash balance. Monthly reconciliation run on 2nd business day.

**Expected output**: actuals_monthly populated with 3+ months of P&L data. cash_balance has today's balance.
**Verify**: `SELECT month, SUM(amount) FROM actuals_monthly GROUP BY month ORDER BY month;` returns monthly totals matching accounting system.
**If failed**: Check OAuth2 token refresh flow. QuickBooks tokens expire every 60 minutes; Xero tokens every 30 minutes.

### Step 3: Build Financial Analytics Queries

**Duration**: 1-2 hours
**Tool**: SQL

```sql
-- 1. P&L actual vs budget
SELECT
  coa.sub_type as category,
  coa.type as account_type,
  COALESCE(a.actual, 0) as actual,
  COALESCE(b.budget, 0) as budget,
  COALESCE(a.actual, 0) - COALESCE(b.budget, 0) as variance,
  CASE WHEN COALESCE(b.budget, 0) != 0
    THEN ROUND((COALESCE(a.actual, 0) / b.budget - 1) * 100, 1)
    ELSE NULL END as variance_pct
FROM chart_of_accounts coa
LEFT JOIN (
  SELECT account_id, SUM(amount) as actual
  FROM actuals_monthly
  WHERE month = DATE_TRUNC('month', CURRENT_DATE)
  GROUP BY account_id
) a ON coa.id = a.account_id
LEFT JOIN (
  SELECT account_id, SUM(amount) as budget
  FROM budget_monthly
  WHERE month = DATE_TRUNC('month', CURRENT_DATE)
  GROUP BY account_id
) b ON coa.id = b.account_id
WHERE coa.type IN ('revenue', 'cogs', 'expense')
ORDER BY coa.type, coa.sub_type;

-- 2. Monthly burn rate and trend
SELECT
  month,
  SUM(CASE WHEN coa.type = 'revenue' THEN am.amount ELSE 0 END) as revenue,
  SUM(CASE WHEN coa.type IN ('cogs', 'expense') THEN am.amount ELSE 0 END) as total_expenses,
  SUM(CASE WHEN coa.type = 'revenue' THEN am.amount ELSE 0 END) -
    SUM(CASE WHEN coa.type IN ('cogs', 'expense') THEN am.amount ELSE 0 END) as net_burn,
  AVG(SUM(CASE WHEN coa.type = 'revenue' THEN am.amount ELSE 0 END) -
    SUM(CASE WHEN coa.type IN ('cogs', 'expense') THEN am.amount ELSE 0 END))
    OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as burn_3mo_avg
FROM actuals_monthly am
JOIN chart_of_accounts coa ON am.account_id = coa.id
WHERE month >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '12 months'
GROUP BY month
ORDER BY month;

-- 3. Runway calculation
WITH burn AS (
  SELECT AVG(net_burn) as avg_monthly_burn
  FROM (
    SELECT month,
      SUM(CASE WHEN coa.type = 'revenue' THEN am.amount ELSE 0 END) -
      SUM(CASE WHEN coa.type IN ('cogs', 'expense') THEN am.amount ELSE 0 END) as net_burn
    FROM actuals_monthly am
    JOIN chart_of_accounts coa ON am.account_id = coa.id
    WHERE month >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '3 months'
    GROUP BY month
  ) recent
),
cash AS (
  SELECT balance FROM cash_balance
  ORDER BY date DESC LIMIT 1
)
SELECT
  cash.balance as current_cash,
  burn.avg_monthly_burn as avg_monthly_burn,
  CASE WHEN burn.avg_monthly_burn < 0
    THEN ROUND(cash.balance / ABS(burn.avg_monthly_burn), 1)
    ELSE NULL -- profitable, infinite runway
  END as runway_months,
  CASE WHEN burn.avg_monthly_burn < 0
    THEN CURRENT_DATE + (cash.balance / ABS(burn.avg_monthly_burn) * 30)::INTEGER
    ELSE NULL
  END as runway_end_date
FROM burn, cash;

-- 4. Unit economics (SaaS)
SELECT
  month,
  mrr,
  arr,
  ltv,
  cac,
  ltv_cac_ratio,
  cac_payback_months,
  gross_margin_pct,
  arpu,
  CASE
    WHEN ltv_cac_ratio >= 3 THEN 'healthy'
    WHEN ltv_cac_ratio >= 1 THEN 'marginal'
    ELSE 'unsustainable'
  END as health_status
FROM unit_economics
ORDER BY month DESC
LIMIT 12;

-- 5. Revenue trend and growth
SELECT
  month,
  mrr,
  new_mrr,
  churned_mrr,
  expansion_mrr,
  customer_count,
  ROUND((mrr - LAG(mrr) OVER (ORDER BY month))::NUMERIC /
    NULLIF(LAG(mrr) OVER (ORDER BY month), 0) * 100, 1) as mrr_growth_pct,
  ROUND(churned_mrr::NUMERIC / NULLIF(LAG(mrr) OVER (ORDER BY month), 0) * 100, 2) as gross_churn_rate
FROM revenue_metrics
WHERE month >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '12 months'
ORDER BY month;
```

**Verify**: P&L query returns categories with non-zero actuals. Runway calculation returns positive months.
**If failed**: Check chart_of_accounts mapping. Type/sub_type must match accounting system categories.

### Step 4: Assemble the Dashboard UI

**Duration**: 1-2 hours
**Tool**: Retool or Metabase

```
Dashboard Layout:
┌─────────────────────────────────────────────────────┐
│  KPI Row: Cash Balance | Runway (months) | Net Burn │
│           MRR | LTV:CAC Ratio                        │
├─────────────────────────┬───────────────────────────┤
│  P&L Actual vs Budget   │  Runway Projection Chart  │
│  (grouped bar chart)    │  (area chart with         │
│                         │   zero-cash date line)    │
├─────────────────────────┼───────────────────────────┤
│  Monthly Burn Rate      │  MRR Waterfall            │
│  Trend (line + bar)     │  (new/expansion/churn)    │
├─────────────────────────┴───────────────────────────┤
│  Unit Economics Trend: LTV, CAC, Payback (lines)     │
├─────────────────────────────────────────────────────┤
│  Expense Breakdown Table (sortable by variance)      │
└─────────────────────────────────────────────────────┘
```

Date range selector: This Month, This Quarter, YTD, Last 12 Months.

Key visual: Runway projection chart shows a declining cash balance area, with a red dashed line at the zero-cash date. If profitable (positive net burn), show "Profitable — infinite runway" badge.

**Verify**: All 6 sections render. Cash balance matches accounting system.
**If failed**: Check query bindings and data source connections.

### Step 5: Configure Financial Alerts

**Duration**: 30-60 minutes
**Tool**: n8n or Pipedream + Slack/Email

```javascript
// Weekly financial health check (run Monday 8:00 AM)
const alerts = [];

// Runway below 6 months
if (runwayMonths < 6) {
  alerts.push(`URGENT: Runway is ${runwayMonths.toFixed(1)} months. Begin fundraising or cut expenses.`);
}

// Actual spend > 120% of budget for any category
for (const cat of categories) {
  if (cat.actual > cat.budget * 1.2 && cat.variance > 1000) {
    alerts.push(`${cat.name}: $${cat.actual.toLocaleString()} actual vs $${cat.budget.toLocaleString()} budget (+${cat.variance_pct}%)`);
  }
}

// LTV:CAC below 1 (losing money on each customer)
if (latestUnitEcon.ltv_cac_ratio < 1) {
  alerts.push(`LTV:CAC ratio is ${latestUnitEcon.ltv_cac_ratio} — acquiring customers at a loss`);
}

// MRR declined month-over-month
if (mrrGrowthPct < 0) {
  alerts.push(`MRR declined ${Math.abs(mrrGrowthPct)}% month-over-month`);
}
```

**Verify**: Test alert fires in Slack/email with sample data.
**If failed**: Check webhook URL and n8n workflow execution.

### Step 6: Deploy and Share Access

**Duration**: 30 minutes
**Tool**: Dashboard platform settings

- Share with CEO, CFO/finance lead, and board observer (if applicable)
- Set viewer permissions for department heads, editor for finance team only
- Configure monthly email report for board/investors (redacted version)
- Enable export to PDF for board meeting preparation

**Verify**: CEO can view dashboard. Finance lead can edit budget figures.

## Output Schema

```json
{
  "output_type": "financial_operations_dashboard",
  "format": "deployed web application",
  "components": [
    {"name": "pl_actual_vs_budget", "type": "chart", "description": "P&L categories with actual, budget, and variance columns", "required": true},
    {"name": "runway_projection", "type": "chart", "description": "Cash balance projection with zero-cash date indicator", "required": true},
    {"name": "burn_rate_trend", "type": "chart", "description": "Monthly net burn with 3-month moving average", "required": true},
    {"name": "mrr_waterfall", "type": "chart", "description": "New, expansion, and churned MRR stacked by month", "required": true},
    {"name": "unit_economics", "type": "chart", "description": "LTV, CAC, and payback period trend lines", "required": true},
    {"name": "kpi_cards", "type": "metrics", "description": "Cash balance, runway months, net burn, MRR, LTV:CAC", "required": true}
  ],
  "refresh_interval": "daily (accounting sync) + real-time (cash balance)",
  "data_source": "PostgreSQL synced from accounting system"
}
```

## Quality Benchmarks

| Quality Metric | Minimum Acceptable | Good | Excellent |
|---------------|-------------------|------|-----------|
| Data freshness | < 48 hours lag | < 24 hours | Same-day sync |
| P&L accuracy | Within 5% of accounting | Within 1% | Exact match (automated) |
| Runway accuracy | Within 2 months of actual | Within 1 month | Within 2 weeks |
| Unit economics coverage | LTV and CAC only | + payback + margin | Full cohort analysis |
| Budget completeness | Revenue + top 3 expenses | All P&L categories | + headcount plan + cash flow |

**If below minimum**: Check accounting API sync logs. P&L discrepancies usually mean unmapped accounts or timing differences between accrual and cash basis.

## Error Handling

| Error | Likely Cause | Recovery Action |
|-------|-------------|----------------|
| QuickBooks API 401 | OAuth2 access token expired | Refresh token via POST to token endpoint; if refresh fails, re-authorize |
| Xero API 429 (rate limit) | More than 60 requests/minute | Implement rate limiter with 1-second delay between calls |
| P&L totals don't match accounting | Unmapped accounts or accrual vs cash basis mismatch | Audit chart_of_accounts mapping, verify reporting basis matches |
| Runway shows "infinite" for unprofitable company | Net burn calculation includes non-cash items or timing offset | Use cash-basis burn or exclude depreciation/amortization from burn calc |
| Unit economics show extreme values | Small customer count amplifies outliers | Apply minimum sample size (50+ customers) before calculating LTV/CAC |
| Cash balance negative or zero | Sync picked up pending transactions or wrong account | Filter to posted transactions only, verify bank account ID mapping |

## Cost Breakdown

| Component | Free Tier | Paid Tier | At Scale (Series A+) |
|-----------|-----------|-----------|----------|
| Dashboard (Retool) | $0 (5 users) | $10/user/mo | $50/mo (5 users) |
| Database (Supabase) | $0 (500MB) | $25/mo | $25/mo |
| ETL (n8n) | $0 (self-hosted) | $20/mo | $50/mo |
| Accounting API (Apideck) | N/A | $49/mo | $49/mo |
| **Total** | **$0** | **$104/mo** | **$174/mo** |

## Anti-Patterns

### Wrong: Calculating runway from revenue minus expenses without cash basis
Accrual accounting includes non-cash items (depreciation, prepaid amortization) and timing differences (invoiced but uncollected revenue). Runway calculated from accrual P&L overstates remaining months by 10-30%. [src3]

### Correct: Use cash balance and cash-basis burn rate
Runway = actual bank balance / average net cash outflow over last 3 months. For SaaS with annual prepayments, adjust for deferred revenue timing.

### Wrong: Showing LTV:CAC ratio without minimum cohort size
With 10 customers, one whale customer or one unusual acquisition can distort LTV:CAC from 0.5 to 5.0. Presenting this as reliable leads to bad investment decisions. [src5]

### Correct: Display confidence interval alongside unit economics
Show LTV:CAC with sample size and confidence range. Below 50 customers, label metrics as "directional estimate" and flag for manual review.

## When This Matters

Use when a startup has at least 3 months of bookkeeping data and needs automated financial visibility beyond manual spreadsheets. Critical for companies with less than 12 months of runway or preparing for board meetings and fundraising. This recipe builds the dashboard — for financial planning or modeling, use a playbook card instead.

## Related Units

- [Sales Operations Dashboard](/software/startup-dashboard/sales-operations-dashboard/2026) — provides revenue pipeline data
- [Marketing Operations Dashboard](/software/startup-dashboard/marketing-operations-dashboard/2026) — provides marketing spend data
- [SaaS Unit Economics Benchmarks](/finance/saas-benchmarks/saas-unit-economics/2026) — benchmark targets for LTV:CAC, payback
- [People Operations Dashboard](/software/startup-dashboard/people-operations-dashboard/2026) — provides headcount cost data
