Onboarding a carrier & statements
Onboarding a carrier is drop a file → review the draft → save → upload your monthly statements. No spreadsheets to wrangle and no engineering tickets — CommissionSight reads a sample statement, maps the columns for you, and detects the formatting (dollars, percentages, even spreadsheet dates). Here’s the whole flow.
Carriers already live
These carrier statement formats are already supported in CommissionSight: Aetna, Ambetter, Anthem Blue Cross and Blue Shield, Delta Dental, Humana, Medico, Molina, Network Health, Quartz, UnitedHealthcare, and WPS Health Solutions. The list isn’t a limit — any carrier is added the same way, by sending a sample file (the steps below).
Carrier brands — pick the brand, we detect the product
Some carriers sell several distinct products that each need their own format and reconcile differently — UnitedHealthcare, for example, has Commercial, Medicare Supplement, and Medicare Advantage lines. CommissionSight groups those under a single brand, so you don’t have to know which specific product a statement is: pick the brand, drop the file, and we detect the exact carrier for you.
List the brands available to your account (only brands with at least one configured product appear):
curl "$CS_BASE/carriers/groups" -H "Authorization: Bearer $CS_TOKEN"# → { data: [ { id, name, slug, members: [ { id, name, slug } ] } ] }Then resolve a sample file against a brand — we parse it with each product’s real config and rank them by fit:
curl -X POST "$CS_BASE/carriers/resolve" \ -H "Authorization: Bearer $CS_TOKEN" \ -F "groupId=$GROUP_ID" -F "file=@sample.csv"# → { groupId, best: { carrierId, name, productLine, confidence, reason }, ranked: [...], ambiguous }best.carrierId is the detected product. When ambiguous is true — no clear winner, or two
products score too close to call — confirm which carrierId to use instead of guessing. Either
way, you then upload to that concrete carrierId in Step 4:
the brand is only used to detect the product, never to store data. (Carriers returned by
GET /v1/carriers carry an optional groupId — the brand they belong to, or null for standalone
carriers; analytics and filters always operate on the concrete carrier.)
The mental model
A carrier is defined once by a small JSON config that maps its file’s columns onto CommissionSight’s unified format. After that, every monthly statement you upload for that carrier is parsed, scored against the prior month, and added to your book — automatically.
Sample file ──▶ infer draft config ──▶ review/adjust ──▶ save (versioned) │Monthly statement ──▶ upload ──▶ ingest job ──▶ scored results (green/yellow/red)Step 1 — Drop a sample, get an auto-mapped draft
Upload one representative statement. CommissionSight detects the header row, matches each column to a canonical field, and infers the right transform for amounts, rates, and dates — then previews the mapped rows so you can see it worked.
In the onboarding wizard: choose the carrier, drop the file, and review the draft it proposes (the CommissionSight team can do this with you). Or via the API:
curl -X POST "$CS_BASE/carriers/$CARRIER_ID/configs/infer" \ -H "Authorization: Bearer $CS_TOKEN" \ -F "file=@sample.csv"# → { config, confidence, headerRow, sheets, mapped, unmapped, notes, preview }What you get back:
config— the draft (header row,columnMapping,transforms,naturalKeys).mapped— the columns it matched, with a confidence score each.unmapped— columns it left alone; these are never lost — every original value is kept in the row’srawJSON.preview— the first rows mapped through the draft, so you can confirm before saving.
Works for CSV and XLSX, any layout. Carrier-specific columns it doesn’t recognize stay in
raw; the columns that matter for scoring (member id, commission, premium, dates, agent, plan)
are mapped for you.
Step 2 — Review & adjust
Skim the draft. Common tweaks:
- Point the natural key at the right column (the stable member or policy id used to match a
member month-over-month). If the carrier zero-pads ids inconsistently, the cross-month matching
control (
idNormalization) is covered in the in-app Integration docs. - Re-target any column the inference guessed wrong, or move a column to
extra.<name>to keep it. - Confirm transforms (
currency,percent,date,excel-date, …).
Validate the adjusted config against your sample without saving:
curl -X POST "$CS_BASE/carriers/$CARRIER_ID/configs/test" \ -H "Authorization: Bearer $CS_TOKEN" \ -F "config=$(cat acme.json)" -F "file=@sample.csv"# → { totalRows, mapped, failed, preview, warnings }Full schema and rules: Authoring carrier configs.
Step 3 — Save the config
curl -X POST "$CS_BASE/carriers/$CARRIER_ID/configs" \ -H "Authorization: Bearer $CS_TOKEN" -H "content-type: application/json" -d @acme.jsonConfigs are versioned and may be global (the carrier default) or account-scoped (your override). Ingest always picks the highest-version active config, preferring your override.
Step 4 — Upload monthly statements
Now just upload each period’s file. The first month establishes the baseline (every member is
NEW); each later month is scored against the prior one.
curl -X POST "$CS_BASE/files" \ -H "Authorization: Bearer $CS_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -F "file=@2026-04.csv" -F "carrierId=$CARRIER_ID" \ -F "periodYear=2026" -F "periodMonth=4"# → 202 { jobId, fileId, status: "queued", _links }Uploads are idempotent (an Idempotency-Key makes a re-POST safe) and async — the file is
stored, a job is queued, and ingest runs on the edge. Poll the job or subscribe to a webhook:
curl "$CS_BASE/jobs/$JOB_ID" -H "Authorization: Bearer $CS_TOKEN"# status: queued → processing → completed (with stats: green/yellow/red/new/reappeared)See Uploading statements and Polling jobs for the full API, and the End-to-end walkthrough for a complete SDK example.
Step 5 — Read the results
Once a job completes, the results grid shows every member color-coded against the prior month, with the exact deltas. Filter by status, drill into a member’s timeline, compare any two periods, or export to CSV. What the colors mean: End-to-end walkthrough.
Fixing a bad file
Re-uploading is never silently destructive. When you upload a statement for a carrier + period
that already has one, the API returns 409 period_exists instead of overwriting. To replace it,
re-submit with replace=true (or the X-Replace-Period: true header) — the web app turns the 409
into a confirm dialog.
A confirmed replace retracts the existing period and re-ingests the corrected file as one atomic operation, so members the corrected file drops are fully removed (a plain re-upsert would leave them behind). Re-scoring is deterministic — identical input yields identical statuses — and the immediately-following month is re-scored automatically, since its baseline just changed.
To remove a period entirely without re-uploading, DELETE /v1/files/{fileId}. This retracts the
whole carrier + period (all files for it) and re-scores the following month. Retracted and replaced
files are hidden from GET /v1/files.