---
title: "SaaS metrics envelope (AR turnover, MRR, LTV, CAC)"
method: GET
path: "/companies/{companyId}/metrics/saas"
tags: ["Overview"]
---

# SaaS metrics envelope (AR turnover, MRR, LTV, CAC)

`GET /companies/{companyId}/metrics/saas`

## Request

- Base URL: `https://api.medianfi.com/api/v1`
- URL: `https://api.medianfi.com/api/v1/companies/{companyId}/metrics/saas`
- Auth: HTTP bearer

Returns the Home "SaaS Metrics" row. `enabled` reflects the home_saas_metrics rollout flag. Each metric also carries its own `available`: AR turnover is computed from the GL today; MRR reports the final sealed snapshot of the last completed calendar month (asOf; the current partial month is excluded) — this covers `currentMinorUnits` and the five waterfall components (new / expansion / reactivation / contraction / churned), while `projectedMinorUnits` and `contractedMinorUnits` stay deliberately FORWARD-LOOKING and are derived from live subscription state, not from the completed-month snapshot; LTV reads the entity's authoritative MRR snapshots plus its posted P&L gross margin and returns available:false with a `reason` when an input is missing (see LtvMetric). enabled reflects the home_saas_metrics rollout flag as UI-visibility metadata only — it never changes the computed result. When a metric is unavailable the UI shows an honest empty state (no fabricated numbers). The envelope-level `currency` / `mixedCurrencies` fields (additive) report the subscriptions' single authoritative billing currency: `currency` is null — never an assumed USD — when there are no subscriptions, the source is unwired, or currencies are mixed (`mixedCurrencies: true`). The envelope is ENTITY-scoped: `entityId` is a required concrete legal entity from the caller's authorized scope (a sole authorized entity is resolved automatically; a multi-entity actor without a selection gets a 422, and an unauthorized entity is rejected). Cross-entity sums without a consolidation authority are never fabricated.

## Query parameters

- `entityId` string, uuid, required
- `asOf` string, date

## Response `200`

SaaS metrics payload

- SaasMetricsResponse
  - `success` true, required
  - `data` SaasMetricsData, required
    - `companyId` string, uuid, required
    - `entityId` string, uuid, required
    - `currency` string, nullable, required: Authoritative uppercase ISO 4217 currency for this entity's metrics. Null — never an assumed USD — when there are no subscriptions, the source is unwired, or currencies are mixed.
    - `minorUnitExponent` integer, nullable, required
    - `enabled` boolean, required
    - `mixedCurrencies` boolean, required: True when subscriptions carry more than one currency (currency is null); false when there is one currency or simply no data.
    - `arTurnover` ArTurnoverMetric, required
      - `available` boolean, required
      - `value` number, nullable, required
      - `asOf` string, date, nullable, required
    - `mrr` MrrMetric, required
      - `available` boolean, required: True when the completed-month waterfall (asOf names its month) and/or the live current-month block is complete.
      - `live` MrrLiveFields: Live Home (founder sync 2026-09-21): the current calendar month's sealed snapshot — MRR as of today, this month's movements so far, and paying customers as of today. Present on MrrMetric.live only when the current-month snapshot is published in the current generation; independent of the completed-month fields, so a tenant certified this month has `live` and no completed month.
        - `currentMinorUnits` string, required
        - `month` string, required
        - `postedThrough` string, date, required
        - `daysInMonth` integer, required
        - `newMinorUnits` string, required
        - `expansionMinorUnits` string, required
        - `reactivationMinorUnits` string, required
        - `contractionMinorUnits` string, required
        - `churnedMinorUnits` string, required
        - `netNewMinorUnits` string, required
        - `activeCustomerCount` integer, required
        - `activeSubscriptionCount` integer, required
        - `currency` string, required
        - `minorUnitExponent` integer, required
      - `currentMinorUnits` string, nullable, required
      - `newMinorUnits` string, nullable, required
      - `expansionMinorUnits` string, nullable, required
      - `reactivationMinorUnits` string, nullable, required
      - `contractionMinorUnits` string, nullable, required
      - `churnedMinorUnits` string, nullable, required
      - `projectedMinorUnits` string, nullable, required: FORWARD-LOOKING, not part of the completed-month snapshot: the current month's projected MRR from live subscription state. Do not present it under the same period label as the waterfall components.
      - `contractedMinorUnits` string, nullable, required: FORWARD-LOOKING: contracted MRR computed from LIVE subscriptions at request time, not from the completed-month snapshot.
      - `currency` string, nullable, required
      - `minorUnitExponent` integer, nullable, required
      - `asOf` string, date, nullable, required: Completed calendar month the waterfall fields describe (YYYY-MM-01) — never the current partial month. Null when unavailable.
      - `reason` 'source_not_authorized' | 'entity_access_required' | 'syncing' | 'stale' | 'failed' | 'mixed_currency' | 'insufficient_history' | 'incomplete_provenance' | 'null', nullable: Customer-safe availability reason; present only when available:false. Distinguishes an unwired billing source from syncing, stale, failed, mixed-currency, and insufficient-history states (never internal identifiers).
      - `excludedSubscriptionCount` integer: Subscriptions in a status current MRR counts (active or past_due) left out of these figures because their recurring price cannot be confirmed from Stripe (for example a discount whose terms Stripe no longer returns and whose billed effect is ambiguous, stacked discounts, or a tiered price). Cancelled or trialing subscriptions are not counted. For a sync sealed before this count existed, every left-out subscription is counted. They are never counted or estimated; one such subscription no longer freezes the whole company's MRR. Present when available; 0 when nothing was left out.
      - `excludedSubscriptionReasons` object[]: Why the subscriptions in excludedSubscriptionCount were left out, one entry per reason with its count, largest first. Empty when nothing was left out or the reasons were not recorded.
        - `reason` 'tiered_or_metered_price' | 'discount_terms_unavailable' | 'stacked_discounts' | 'amount_unavailable', required
        - `count` integer, required
    - `ltv` LtvMetric, required: Entity LTV (#1944) = ARPA x gross margin x min(1 / monthly churn, 200 months), in integer minor units. Window W = the trailing 12 completed months ending at the last completed month (all completed months when fewer than 12 exist). ARPA = the last completed month's MRR / active customers; monthly churn = sum of churned MRR over W / sum of opening MRR over W (opening = the prior month's close), from the legal entity's authoritative MRR snapshots (the Home MRR substrate, so one-off charges, fees and refunds never move it). Gross margin = (operating revenue - COGS) / operating revenue from the same entity's posted P&L over W; operating revenue excludes other income (e.g. interest) and 4999 Uncategorized Income. `value` is major units of LTV's own `currency`. An unavailable metric carries `reason` and never a numeric zero; `no_observed_churn` means churn was zero across the whole window, so no finite lifetime is reported.
      - `available` boolean, required
      - `value` number, nullable, required: Legacy major units (ltvMoney.amountMinor / 10^minorUnitExponent). Prefer `ltvMoney`.
      - `reason` 'source_not_authorized' | 'entity_access_required' | 'syncing' | 'stale' | 'failed' | 'mixed_currency' | 'insufficient_history' | 'incomplete_provenance' | 'not_configured' | 'insufficient_inputs' | 'no_observed_churn' | 'null', nullable
      - `currency` string, nullable: LTV's own money authority (the entity's MRR billing currency). Present when available; render in this currency, never the envelope's.
      - `minorUnitExponent` integer, nullable
      - `ltvMoney` ExactMoney: Exact money value: signed integer minor units as a string (never a float), with explicit currency and minor-unit exponent.
        - `amountMinor` string, required: Signed integer minor units, e.g. "80000" = $800.00 at exponent 2.
        - `currency` string, required
        - `minorUnitExponent` integer, required
    - `cac` CacMetric, required: The CAC headline. Served under the `cac` key of GET /metrics/saas and as `metric` on GET /metrics/cac. `value` is the LEGACY major-unit float kept for old clients; new consumers read `cacMoney`, the shared ExactMoney shape (master-index pin P2). The money authority is the ENTITY's functional currency, which can differ from the envelope's subscription billing currency — render from `currency`/`minorUnitExponent` here, never from the envelope's. An unavailable metric carries `reason` and never a numeric zero.
      - `available` boolean, required
      - `value` number, nullable, required: Legacy major units (cacCents / 100). Prefer `cacMoney`.
      - `smSpendCents` integer, nullable, required
      - `newCustomers` integer, nullable, required
      - `currency` string, nullable
      - `minorUnitExponent` integer, nullable
      - `cacMoney` ExactMoney: Exact money value: signed integer minor units as a string (never a float), with explicit currency and minor-unit exponent.
        - `amountMinor` string, required: Signed integer minor units, e.g. "80000" = $800.00 at exponent 2.
        - `currency` string, required
        - `minorUnitExponent` integer, required
      - `smSpendMoney` ExactMoney: Exact money value: signed integer minor units as a string (never a float), with explicit currency and minor-unit exponent.
        - `amountMinor` string, required: Signed integer minor units, e.g. "80000" = $800.00 at exponent 2.
        - `currency` string, required
        - `minorUnitExponent` integer, required
      - `reason` 'insufficient_history' | 'source_not_authorized' | 'mixed_currency' | 'entity_scope_unavailable' | 'insufficient_inputs' | 'truncated' | 'failed': Customer-safe reason for an unavailable CAC. `truncated` (a capped scan) and `failed` (a thrown read, added by the /metrics/saas assembler) are the FAILURE class the UI renders as an explicit unavailable state (master-index pin P4); every other value is honest absence.

## Other responses

- `401`: Authentication required
- `403`: Insufficient permissions or not a company member
- `422`: Request failed validation
- `503`: The legal-entity scope authority could not be read

---

[API](https://skmtc.dev/medianfi/apis/median-api.md) · [All operations](https://skmtc.dev/medianfi/apis/median-api/llms.txt) · [OpenAPI document](https://skmtc.dev/medianfi/apis/median-api/revisions/20090ea32b0d?raw)
