---
title: "Practice metrics envelope (four practice widgets)"
method: GET
path: "/companies/{companyId}/metrics/practice"
tags: ["Overview"]
---

# Practice metrics envelope (four practice widgets)

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

## Request

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

Returns the Home practice-metrics envelope: the two-axis widget catalog (services axis from the stored business model, ownership axis from the entity type), the practice config, the company's pinned widgets, and the four widgets assembled from the accountant-uploaded patient-free aggregate feed. Honest denominators: a widget whose inputs are absent reports `available: false` with the missing metric keys, and a figure whose denominator is unknown is null; nothing is fabricated (`costPerCustomerCents: null` means "cost per customer unknown"). Returns 404 unless `practice_metrics` is in the company's `additionalMetrics` list; the surface does not exist for an unlisted company.

## Response `200`

Practice metrics envelope

- PracticeMetricsEnvelopeResponse
  - `success` true, required
  - `data` object, required
    - `businessModel` string, nullable, required: The company's stored business model (#1967), the catalog's services axis.
    - `catalog` object, required: Two-axis widget catalog: `services` gates the per-person widgets, `owner` keys on the ownership axis (entity type), and spend-by-channel is shared — never gated.
      - `services` boolean, required
      - `owner` boolean, required
      - `shared` true, required
    - `config` PracticeConfig, required
      - `overheadAllocationBasis` 'revenue' | 'hours' | 'headcount', required: How the overhead pool is allocated across members.
      - `ownerCompensationCents` integer, nullable, required: S-corp reasonable-comp reference, integer cents; null until set.
      - `ownerDrawSplitPct` number, nullable, required
    - `pins` PinnedMetric[], required
      - `widgetKey` string, required
      - `position` integer, required
    - `latestPeriod` string, nullable, required: Newest YYYY-MM on any stored point. Not proof every widget is complete through that month.
    - `computedAt` string, date-time, nullable, required: Newest source-computation timestamp across stored points; independent of import time.
    - `importedAt` string, date-time, nullable, required: Latest database import timestamp across the feed rows.
    - `sources` string[], required: Distinct feed-source labels represented in the returned rows.
    - `widgetPeriods` string[], required: Distinct YYYY-MM values the available widgets actually assembled.
    - `widgets` object, required
      - `profitByTeamMember` union, required
        - PracticeWidgetUnavailable
          - `available` false, required
          - `reason` 'not_in_catalog' | 'missing_data', required
          - `missing` PracticeMetricKey[]: Metric keys whose absence keeps the widget unavailable.
        - object
          - `available` true, required
          - `period` string, required
          - `currency` string, nullable, required: ISO 4217 code that every profit input for the month (member revenue, direct cost, overhead pool) declares. Null when any input is undeclared or they disagree: the member figures are then bare cents of unconfirmed currency.
          - `ledgerRevenueCurrency` string, nullable, required: The books' functional currency for `ledgerRevenueCents`; null exactly when that is. Compare the two figures only when this equals `currency`.
          - `ledgerRevenueCents` integer, nullable, required: Revenue in the company's books for the same month (operating revenue as on Home: other income and Uncategorized Income excluded), for comparison with the practice feed's member revenue. Null — unknown, not zero — when the month is still in progress, has no posted journal entries yet, or the ledger figure could not be read.
          - `overheadPoolCents` integer, nullable, required: Null when no overhead pool was uploaded — overhead allocation is then unknown, not zero.
          - `allocationBasis` 'revenue' | 'hours' | 'headcount', required
          - `members` object[], required
            - `member` string, required
            - `revenueCents` integer, required
            - `directCostCents` integer, nullable, required
            - `allocatedOverheadCents` integer, nullable, required
            - `contributionCents` integer, nullable, required: Null whenever any input is unknown — never guessed.
      - `realizationRate` union, required
        - PracticeWidgetUnavailable
          - `available` false, required
          - `reason` 'not_in_catalog' | 'missing_data', required
          - `missing` PracticeMetricKey[]: Metric keys whose absence keeps the widget unavailable.
        - object
          - `available` true, required
          - `period` string, required
          - `totalHoursBooked` number, required
          - `totalHoursDelivered` number, required
          - `totalRealizationPct` number, nullable, required
          - `members` object[], required
            - `member` string, required
            - `hoursBooked` number, required
            - `hoursDelivered` number, required
            - `realizationPct` number, nullable, required
            - `unrealizedValueCents` integer, nullable, required: Booked-but-undelivered value at the member's effective rate; null without revenue.
      - `spendByChannel` union, required
        - PracticeWidgetUnavailable
          - `available` false, required
          - `reason` 'not_in_catalog' | 'missing_data', required
          - `missing` PracticeMetricKey[]: Metric keys whose absence keeps the widget unavailable.
        - object
          - `available` true, required
          - `period` string, required
          - `spendSource` 'ledger' | 'upload', required: ledger = posted GL expense on marketing accounts, one channel per account (tier 1), used when two or more marketing accounts carry spend in the month or nothing was uploaded; upload = the accountant feed otherwise.
          - `totalSpendCents` integer, required
          - `unattributedSpendPct` number, nullable, required: Share of spend whose channel has no customer denominator.
          - `channels` object[], required
            - `channel` string, required
            - `spendCents` integer, required
            - `customers` integer, nullable, required
            - `costPerCustomerCents` integer, nullable, required: Null = cost per customer unknown (no denominator) — never fabricated.
      - `ownerPayVsProfit` union, required
        - PracticeWidgetUnavailable
          - `available` false, required
          - `reason` 'not_in_catalog' | 'missing_data', required
          - `missing` PracticeMetricKey[]: Metric keys whose absence keeps the widget unavailable.
        - object
          - `available` true, required
          - `period` string, required
          - `ownerPayCents` integer, nullable, required
          - `ownerDrawsCents` integer, nullable, required
          - `ownerBillingsCents` integer, nullable, required
          - `netIncomeBeforeOwnerPayCents` integer, nullable, required
          - `profitAfterOwnerPayCents` integer, nullable, required
          - `reasonableCompCents` integer, nullable, required: The configured S-corp reasonable-comp reference; null until set.

## Other responses

- `401`: Authentication required
- `403`: Insufficient permissions or not a company member
- `404`: practice_metrics is not in this company's additionalMetrics list

---

[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)
