Official .NET client for the CommissionSight API — commission-statement intelligence for insurance agencies. Ingest carrier statements, score every member month-over-month (green / yellow / red), surface the commission you're owed, trace chargebacks, and more.
- Modern & async —
Task-based,CancellationTokeneverywhere,IAsyncDisposable-friendlyHttpClientusage. - Minimal dependencies — zero third-party runtime packages; just
System.Net.Http+System.Text.Jsonfrom the framework. - Multi-targeted —
net9.0andnet10.0. - Complete — covers 100% of the API surface (parity with the TypeScript
@commissionsight/sdk).
dotnet add package CommissionSightusing CommissionSight;
using var client = new CommissionSightClient(new CommissionSightClientOptions
{
BaseUrl = "https://api.commissionsight.com/v1",
Token = Environment.GetEnvironmentVariable("COMMISSIONSIGHT_TOKEN"),
});
// Who am I?
var me = await client.MeAsync();
Console.WriteLine($"{me.Name} ({me.Status})");
// Upload a monthly statement (async ingest → returns the queued job).
using var upload = FileUpload.FromFile("humana-2026-04.csv");
var job = await client.UploadFileAsync(upload, carrierId: "carrier_123", periodYear: 2026, periodMonth: 4,
idempotencyKey: Guid.NewGuid().ToString());
// Read the scored results once the job completes.
var results = await client.GetJobResultsAsync(job.JobId, status: "yellow");
foreach (var row in results.Data)
{
Console.WriteLine($"{row.MemberName}: {row.Status} [{string.Join(", ", row.Flags)}] owed {row.CommissionOwed:C}");
}Pass a bearer token (an account API token, or a web session token) via CommissionSightClientOptions.Token,
or set it later with client.SetToken("..."). The only endpoint that needs no token is HealthAsync().
Supply your own HttpClient so the client integrates with IHttpClientFactory and your DI container:
services.AddHttpClient("commissionsight");
services.AddSingleton(sp =>
{
var http = sp.GetRequiredService<IHttpClientFactory>().CreateClient("commissionsight");
return new CommissionSightClient(
new CommissionSightClientOptions { BaseUrl = "https://api.commissionsight.com/v1", Token = "..." },
http);
});When you pass your own HttpClient, the client does not dispose it (the factory owns its lifetime).
The parameterless-HttpClient constructor creates and owns one, disposed with the client.
Non-success responses throw CommissionSightApiException, which surfaces the API's RFC-9457
problem+json body:
try
{
await client.UploadFileAsync(upload, "carrier_123", 2026, 4);
}
catch (CommissionSightApiException ex) when (ex.Code == "period_exists")
{
// A statement already exists for this carrier+period — replace it:
await client.UploadFileAsync(upload, "carrier_123", 2026, 4, replace: true);
}
catch (CommissionSightApiException ex)
{
Console.Error.WriteLine($"{ex.Status} {ex.Title}: {ex.Detail}");
}ex.StatusCode, ex.Status (int), ex.Title, ex.Detail, ex.Code, and the raw ex.Body
(JsonElement?) are all available.
List endpoints return Page<T> with a Pagination block (Limit, Offset or NextCursor, HasMore):
var page = await client.ListFilesAsync(limit: 50);
while (true)
{
foreach (var f in page.Data) Console.WriteLine(f.OriginalFilename);
if (page.Pagination is not { HasMore: true, NextCursor: { } cursor }) break;
page = await client.ListFilesAsync(limit: 50, cursor: cursor);
}var rollup = await client.RollupAsync("2026-04");
Console.WriteLine($"At risk: {rollup.Totals.CommissionAtRisk:C}, owed: {rollup.Totals.CommissionOwed:C}");
var series = await client.AttritionSeriesAsync(months: 12);
var quality = await client.DataQualityAsync("2026-04"); // incomplete / wrong-period file detection
var cbs = await client.ListChargebacksAsync("2026-04"); // each traced to its original payout
var journey = await client.GetMemberJourneyAsync("member_ref_id"); // full per-member audit history
// Cumulative audit totals over a period range (owed / at-risk / chargebacks summed,
// with byPeriod[] + byCarrier[] breakdowns) — the figures you take to a carrier audit.
var audit = await client.CumulativeAsync(from: "2026-01", to: "2026-04");
Console.WriteLine($"Owed over range: {audit.Totals.CommissionOwed:C} ({audit.Totals.OwedCoverage:P0} coverage)");Multi-workspace accounts partition data by workspace; uploads and reports take an optional workspaceId.
var ws = await client.ListWorkspacesAsync();
if (ws.Enabled)
{
var created = await client.CreateWorkspaceAsync("West Region");
await client.UploadFileAsync(file, "carrier1", 2026, 4, workspaceId: created.Id);
var rollup = await client.RollupAsync("2026-04", workspaceId: created.Id);
}Back-office endpoints (require an allowlisted admin session token) live under client.Admin:
var accounts = await client.Admin.ListAccountsAsync(status: "pending");
await client.Admin.ApproveAccountAsync(accounts.Data[0].Id);
var metrics = await client.Admin.MetricsAsync();- Enums (
Status,Flag,JobStatus,StabilityLevel,RateType, …) serialize to/from their wire strings. Unrecognized values deserialize toUnknownrather than throwing, so a newer server never breaks an older SDK. - Timestamps surface as
DateTimeOffsetwhether the API sends an epoch (ms) number or an ISO string. - Monetary dollar amounts are
decimal; rates/fractions aredouble; cents arelong. - Opaque/free-form payloads (raw configs, member rows, deltas) surface as
System.Text.Json.JsonElement.
dotnet build -c Release
dotnet test -c Release
dotnet pack src/CommissionSight/CommissionSight.csproj -c Release -o ./artifactsMIT © CommissionSight. See LICENSE.