C#: Zero to first 200

Build a .NET console app that prequalifies John Doe for Final Expense coverage in under 60 minutes with no insurance domain knowledge required.

C#: Zero to first 200

Target: A .NET console application that prequalifies John Doe (NC, 64, male, no conditions) for Final Expense coverage and prints every qualifying plan with its monthly premium. Time budget: under 60 minutes from a blank directory.

Language requirement: .NET 8 SDK. No ASP.NET, no DI container — a plain console app.


What you'll build

A console project — prequalify/Program.cs — that:

  1. Reads your API bearer token from an environment variable.
  2. Asks the ISA underwriting engine whether John Doe qualifies for Final Expense coverage.
  3. Prints each qualifying plan: carrier name, plan tier, and monthly premium in cents.

"Prequalify" is a fast eligibility screen. You send an applicant's demographics and a coverage request; the engine returns every plan the applicant qualifies for with a bucketed monthly premium. It is not a binding quote.


Prerequisites

RequirementWhy
.NET 8 SDKThe SDK targets net8.0. Run dotnet --version to verify.
dotnet CLIdotnet new, dotnet add package, dotnet run.
An ISA API bearer tokenSee "Getting your test token" below.

Getting your test token

Your ISA API token arrives by email within minutes of completing checkout at checkout.isaapi.com. The email contains both an isa_test_… and an isa_live_… token. Set the test token as an environment variable:

export ISA_TOKEN="<paste your isa_test_… key here>"

Windows (PowerShell):

$env:ISA_TOKEN = "<paste your isa_test_… key here>"

Test vs live. Every call made with a test token runs against the real engine with real carrier rules, but no binding decisions are issued. Switch to your live token when you are ready to ship.


1. Create the project

dotnet new console -n prequalify
cd prequalify

2. Install the SDK

dotnet add package IsaSdk

3. Hello world

Replace the contents of Program.cs:

using Isa.Sdk;
using Isa.Sdk.Catalog;
using Isa.Sdk.Zyins;

// IsaClient.WithBearer reads ISA_TOKEN from the environment.
var isa = IsaClient.WithBearer();

// Products.Fex.AetnaAccendo carries the stable prod_<uuid> used on the wire.
// Always use catalog constants — never hardcode prod_ strings or slugs.
var result = await isa.Zyins.Prequalify.RunAsync(new PrequalifyRequest(
    Applicant: new Applicant
    {
        Dob          = "1962-04-18",
        Sex          = Sex.Male,
        HeightInches = 70,
        WeightPounds = 195,
        State        = "NC",
        NicotineUse  = new NicotineUsageInput { LastUsed = NicotineDuration.Never },
    },
    Coverage: Coverage.ByFaceValue(25_000),
    Products: new[] { Products.Fex.AetnaAccendo }));

foreach (var offer in result.Plans)
{
    var headline = offer.Pricing.FirstOrDefault(r => r.Primary);
    if (headline?.Premium is not null)
    {
        Console.WriteLine(
            $"{offer.Carrier.Name,-24} {offer.Product.Name,-40} " +
            $"category={headline.Eligibility.Category,-10} " +
            $"premium={headline.Premium.Amount.Display,-10} ({headline.Premium.Amount.Cents} cents)");
    }
}

Run it:

dotnet run

You should see a list of qualifying plans. No plans means the applicant did not qualify — confirm that ISA_TOKEN is set.


4. Add idempotency

The SDK auto-mints a UUID v4 idempotency key for every call. That works fine for one-off scripts. For production jobs where you retry the same request, mint the key once at dispatch and reuse it:

// The SDK auto-mints a UUID v4 per call.
// For replay safety across retries, generate the key at job dispatch
// and hold it stable. Pass it via RequestOptions if the SDK exposes that
// parameter, or rely on the auto-minted key for single-process use.
var result = await isa.Zyins.Prequalify.RunAsync(request, CancellationToken.None);

If the server receives the same key with the same body within 24 hours, it returns the cached response. If the body differs, the server returns 409 idempotency_conflict.


5. Retries

The SDK automatically retries 5xx and 429 rate_limit_exceeded responses on exponential backoff with jitter, reusing the same idempotency key. You do not need to write retry logic.


6. Handle errors

All exceptions inherit from IsaException. Match on the typed subclass:

using Isa.Sdk;
using Isa.Sdk.Catalog;
using Isa.Sdk.Core;
using Isa.Sdk.Zyins;

var isa = IsaClient.WithBearer();
var request = new PrequalifyRequest(
    Applicant: new Applicant
    {
        Dob = "1962-04-18",
        Sex = Sex.Male,
        HeightInches = 70,
        WeightPounds = 195,
        State = "NC",
        NicotineUse = new NicotineUsageInput { LastUsed = NicotineDuration.Never },
    },
    Coverage: Coverage.ByFaceValue(25_000),
    Products: new[] { Products.Fex.AetnaAccendo });

try
{
    var result = await isa.Zyins.Prequalify.RunAsync(request, CancellationToken.None);
}
catch (IsaIdempotencyConflictException ex)
{
    // Key reused with different body — bug in call site.
    Console.Error.WriteLine($"Idempotency conflict: key={ex.Key} first_seen={ex.FirstSeenAt}");
    throw;
}
catch (IsaRateLimitException ex)
{
    // SDK already retried; honor the retry-after window.
    var delay = ex.RetryAfter ?? TimeSpan.FromSeconds(1);
    Console.Error.WriteLine($"Rate limited; retry after {delay.TotalSeconds:F1}s");
    await Task.Delay(delay);
    throw;
}
catch (IsaException ex)
{
    // Any other API error — log request_id for support.
    Console.Error.WriteLine($"API error: code={ex.Code} request_id={ex.RequestId}");
    throw;
}

Log RequestId alongside every business record. It is the correlation ID that links your log line to the server-side trace.


7. Run it

dotnet run

Expected output (exact plans depend on carrier availability in NC):

Aetna Accendo            Aetna Accendo Final Expense              category=immediate  premium=$87.42     (8742 cents)

Premium.Amount.Cents is the integer in US cents (always); Premium.Amount.Display is the verbatim carrier-formatted string. Render Display, do arithmetic on Cents.


8. What just happened

When you called Prequalify.RunAsync(request), the SDK:

  1. Read ISA_TOKEN from the environment and set up the HTTP client.
  2. Minted a UUID v4 and sent Idempotency-Key: <uuid> on the request.
  3. Received a typed PrequalifyResult with Plans, RequestId, and Livemode.
  4. Returned a typed C# record — no JSON deserialization needed on your side.

A non-empty result.Plans means the applicant qualifies. result.Livemode == false confirms you ran against the test sandbox.


9. What's next