Reports & analytics
Reports read pre-computed period rollups, so they’re fast enough for dashboards and trend charts. All amounts and counts come from the same scored dataset as the grid and the comparison API.
Rollup — green / yellow / red for a period
curl "https://api.commissionsight.com/v1/reports/rollup?period=2026-04&carrierId=$CARRIER_ID" \ -H "Authorization: Bearer $TOKEN"period defaults to the latest period with data; carrierId is optional.
{ "period": "2026-04", "totals": { "memberCount": 9672, "green": 8120, "yellow": 940, "red": 612, "new": 210, "reappeared": 34, "commissionAtRisk": 48230.55, // dollars on yellow + red members this period "commissionDropped": 31010.00, // the red (dropped) slice of at-risk "commissionReduced": 17220.55, // the yellow (reduced) slice of at-risk "reducedCount": 410, // members whose commission went down "commissionOwed": 12640.40, // recoverable shortfall (expected − actual) "owedEvaluated": 8900, // members the owed engine could evaluate "owedTotal": 9100, // members owed was attempted on "owedEstimated": false, // true if owed is built on an auto-seeded estimate rate "chargebackCount": 18, "chargebackAmount": 2240.00 }, "byCarrier": [ { "carrierId": "…", "memberCount": 1260, "green": 1190, "yellow": 55, "red": 15, "new": 8, "reappeared": 2, "attritionRate": 0.011, "commissionAtRisk": 5120.00, "commissionOwed": 1840.00, "chargebackAmount": 0 } ]}Each byCarrier entry carries the same per-carrier fields as totals (plus attritionRate).
owedEvaluated ÷ owedTotal is the owed-coverage ratio — how much of the book the recoverable
figure is based on. owedEstimated is true when the owed figure is (partly) built on an
auto-seeded estimate rate rather than a rate you’ve confirmed — treat the dollars as approximate
until you set your contracted rate (it flips to false once you do). See
Commission owed.
Attrition — point in time
curl "https://api.commissionsight.com/v1/reports/attrition?period=2026-04" \ -H "Authorization: Bearer $TOKEN"period is required. Attrition ≈ red ÷ members present in the prior period, returned overall and
per carrier. (The projection approximates the prior-period denominator as present + dropped; for
exact member-level movement use /comparisons.)
Attrition — trend series
curl "https://api.commissionsight.com/v1/reports/attrition-series?months=12&carrierId=$CARRIER_ID" \ -H "Authorization: Bearer $TOKEN"Month-over-month attrition for the last N periods (1–36, default 12), oldest → newest — ready to
plot. Each point: { period, year, month, red, memberCount, attritionRate, commissionAtRisk } —
commissionAtRisk is the dollars on yellow + red members that period, so you can plot lost-revenue
alongside the rate.
Cumulative — audit totals over a range
curl "https://api.commissionsight.com/v1/reports/cumulative?from=2025-01&to=2025-12&carrierId=$CARRIER_ID" \ -H "Authorization: Bearer $TOKEN"The audit roll-up: commission owed, at-risk, and chargebacks summed across a period
range, with month-by-month and per-carrier breakdowns. from/to (YYYY-MM) are inclusive and both
optional — omit them for the entire book. Scope with carrierId / workspaceId.
{ "range": { "from": "2025-01", "to": "2025-12", "requestedFrom": "2025-01", "requestedTo": "2025-12", "periodsCovered": 12 }, "totals": { "commissionOwed": 4204.10, // Σ expected-vs-actual shortfall over the range (recoverable) "commissionAtRisk": 12880.00, "chargebackAmount": 3540.00, "chargebackCount": 24, "owedEvaluated": 7600, "owedTotal": 13800, "owedCoverage": 0.55, // cumulative Σ evaluated ÷ Σ total "memberMonths": 13800, // Σ per-period member counts — a VOLUME, not distinct members "avgMembers": 1150, "peakMembers": 1260, "red": 540, "new": 410, "reappeared": 120, "owedEstimated": false }, "byPeriod": [ { "period": "2025-12", "memberCount": 1260, "commissionOwed": 268.22, "commissionAtRisk": 167.12, "chargebackAmount": 0, "owedEvaluated": 1180, "owedTotal": 1260, "owedCoverage": 0.94, "red": 6, "new": 12, "reappeared": 4 } ], "byCarrier": [ { "carrierId": "…", "carrierName": "Aetna Senior Supplemental", "commissionOwed": 4204.10, "commissionAtRisk": 12880.00, "chargebackAmount": 3540.00, "owedEvaluated": 7600, "owedTotal": 13800, "owedCoverage": 0.55, "memberMonths": 13800, "periodsCovered": 12 } ]}
memberMonthsis a volume, not a head-count. It’s the sum of each period’s member count, so a 12-month range over a ~1,150-member book is ~13,800 — useavgMembers/peakMembersfor the size of the book.commissionOwedis correctly cumulative: each period’s owed is that period’s expected-minus-actual, so summing them is the total recoverable over the window.
Statement-quality (data-quality) signal
curl "https://api.commissionsight.com/v1/reports/data-quality?period=2026-04" \ -H "Authorization: Bearer $TOKEN"A per-carrier, per-period grade — ok · watch · alert — of whether the statement you uploaded
looks complete and from the right period. It’s a guard that runs before you trust a carrier’s
fallout and attrition numbers: an incomplete or wrong-period file makes a slice of the book look like
it churned when it didn’t, and this flags it so you don’t act on bad data.
Each carrier returns a level, a plain-English reason, and the underlying rates. When a carrier is
alert, treat the statement as suspect — re-pull the correct, complete file and re-upload (and
re-process the affected period); the numbers then reflect reality.
Medicare rapid-disenrollment (RDE) radar
curl "https://api.commissionsight.com/v1/reports/medicare-rde?carrierId=$CARRIER_ID&period=2026-06" \ -H "Authorization: Bearer $TOKEN"CMS advances Medicare commission and claws back the full amount if a member disenrolls inside the
first ~3 months (a rapid disenrollment). For one carrier and period this returns a radar with:
realized—{ count, clawedBackAmount }: rapid disenrollments whose commission was clawed back this period (the money already gone).exposure—{ count, atRiskAmount }: active members still inside the RDE window — if they leave now, the full advance claws back. The “see it coming” figure.disenrollments—{ total, rapid, rapidRatePct }: of everyone who left, the share that was rapid.byPlan/byAgent— where the rapid disenrollments concentrate (which products and producers churn early), each with the dollars clawed back.
carrierId is required; period defaults to the carrier’s latest. Classification needs an effective
date and a disenrollment/term date on the statement — records missing either are counted in total
but never guessed, and evaluated ÷ total reports the coverage.
Medicare renewal & book-value forecast
curl "https://api.commissionsight.com/v1/reports/medicare-renewal-forecast?carrierId=$CARRIER_ID" \ -H "Authorization: Bearer $TOKEN"Projects the next horizonMonths (default 12, max 24) of renewal commission across the
active book. Each active member renews once a year on their enrollment anniversary at the CMS
renewal rate (your configured schedule, else the bundled CMS maximums — ratesInferred: true). Over a
12-month window that’s the book’s annual recurring renewal income — the figure agencies value a
book on. Returns a forecast with annualRenewalValue, activeMembers, evaluated (members we could
price), byMonth (anniversary seasonality), and byPlan. It assumes retention — pair it with the RDE
radar above for the downside. carrierId required; period (the “as-of” book) defaults to the latest.
Unpaid members (census reconciliation)
curl -X POST "https://api.commissionsight.com/v1/reports/census-reconcile" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{ "carrierId": "car_123", "members": [ { "memberExternalId": "M-1001" }, { "memberExternalId": "M-1002" } ] }'The biggest find-money check: members you know are active that the carrier never paid you for.
POST your active-member roster (members[].memberExternalId, optional name/planName) and we compare
it to who the carrier actually paid on the statement. Returns a result with unpaid (roster members
with no paid record — the recoverable list), estimatedUnpaidAmount (unpaid count × the matched
members’ median paid commission — a basis grounded in your own statement), matched / censusTotal
coverage, and unrostered (paid members missing from your roster — a stale roster, or members you
forgot). Nothing is stored — the roster is reconciled in memory and discarded. carrierId required;
period defaults to the latest. Matching is by member id (case/space-insensitive).
Carrier scorecards
curl "https://api.commissionsight.com/v1/reports/carrier-scorecards?from=2025-01&to=2025-12" \ -H "Authorization: Bearer $TOKEN"Ranks your carriers worst grade first — who to lean on, who to chase. The grade scores carrier
behavior: reliability (a statement every active month, 60%) + cleanliness (statements that
don’t look incomplete/wrong-period, 40%). The money metrics are reported alongside (not baked into the
grade with arbitrary thresholds): commissionOwed, owedPerMemberMonth, chargebackAmount,
dropRatePct, plus consistencyPct, periodsReported/periodsExpected, qualityFlags, and
avgMembers. from/to (both optional) bound the window; with neither, it uses every active month.
Reads the fast projection — no data-plane hit.
Year-end 1099 reconciliation
curl "https://api.commissionsight.com/v1/reports/tax-1099?year=2025" \ -H "Authorization: Bearer $TOKEN"Tallies the commission each carrier actually paid over a tax (calendar) year — paidNet (net of
chargebacks), paidGross, chargebacks, and periodsCovered per carrier — so you can check it against
the carrier’s 1099 box amount. A mismatch is mis-reported income worth a conversation with the
carrier (and your accountant). year is required; the tally reflects the statements you’ve loaded, so
load every month for an apples-to-apples check.
Saved & scheduled reports
Any plain-English question you ask the assistant generates a read-only query; you can save that query as a named report and re-run it any time, or have it emailed on a schedule. These are app-only endpoints (not part of the SDK).
| Method & path | What it does |
|---|---|
POST /v1/reports/saved | Save a report. Body: { name, question?, sql }. The sql must pass the same read-only guard the assistant uses (a single SELECT/WITH, bounded) — anything else is rejected 422. |
GET /v1/reports/saved | List your saved reports (name, question, cadence, last run). |
POST /v1/reports/saved/:id/run | Re-run the stored query against your data → { columns, rows, truncated }. Deterministic — no AI. |
PUT /v1/reports/saved/:id | Set the email digest cadence. Body: { cadence: "off" | "weekly" | "monthly" }. |
DELETE /v1/reports/saved/:id | Delete a saved report. |
When a report has a weekly/monthly cadence, a scheduled job runs it and emails the results to your
account on that interval. In the app, save and schedule reports right from the Ask AI screen.