Pricing

Reading premium and death benefit values off prequalify and quote responses. Every monetary value ships as integer cents plus a verbatim display string.

Pricing

Every monetary value the API returns ships as integer cents plus a verbatim display string. Use cents for math and comparisons; use display for rendering. You never parse "$1,084.20" yourself.

v3 introduces the Money primitive. Death benefits and budgets are Money — an amount ({cents, display}) paired with a period (null for lump-sum, "monthly" for requested budget). Premium is {amount, default_mode, modes}: amount is the headline default-mode value, default_mode names which modes key it equals, and modes is the carrier grid. Premium carries no period; the mode token carries the cadence. See v3 uniform pricing below. Existing v2 integrators keep working — see v2 legacy pricing at the end.

v3 uniform pricing

On /v3/prequalify and /v3/quote, every offer carries:

  • death_benefitMoney with period: null (a one-time lump sum) for life products (fex, term, preneed), and null for premium-only products (medsup), whose coverage value lives entirely in pricing[].premium. The key is always present on the wire — null rather than omitted — so you can null-check it directly. Null-check it before reading .amount.
  • budgetMoney with period: "monthly". Present only on monthly-budget quotes; the requested budget, distinct from the solved premium.
  • pricing[] — uniform table, one row per rate class. Each row has premium (when eligible) as {amount, default_mode, modes}: amount ({cents, display}) is the headline default-mode value, default_mode names which modes key it equals, and modes is the full carrier grid.

The Money primitive

death_benefit:          # Money
  amount:               # AmountResponse
    cents:   integer    # minor units (US cents)
    display: string     # carrier-formatted, e.g. "$25,000" or "$91.22"
  period: null | "monthly" | "quarterly" | "semiannual" | "annual"
  • period: null — a one-time or lump-sum amount (death benefit).
  • period: "monthly" — a recurring amount with a billing cycle (currently only budget uses it; the other values are reserved).

premium.amount and every entry in premium.modes is an AmountResponse ({cents, display}). premium.default_mode is the modes key whose value equals amount. Premium carries no period — the mode token carries the cadence.

Example offer (face-amount request)

{
  "object": "plan_offer",
  "carrier": { "name": "Aetna Accendo" },
  "product": { "id": "prod_d7b57156-3e83-506b-8936-0692c1193dc7", "name": "Aetna Accendo Final Expense", "class": "fex" },
  "death_benefit": {
    "amount": { "cents": 2500000, "display": "$25,000" },
    "period": null
  },
  "pricing": [
    {
      "rate_class": "Preferred Plus",
      "primary": true,
      "rank": 1,
      "eligibility": { "eligible": true, "category": "immediate", "reasons": [] },
      "premium": {
        "amount": { "cents": 4287, "display": "$42.87" },
        "default_mode": "monthly",
        "modes": {
          "monthly":     { "cents": 4287,  "display": "$42.87"   },
          "annual":      { "cents": 50116, "display": "$501.16"  },
          "quarterly":   { "cents": 12614, "display": "$126.14"  },
          "semi-annual": { "cents": 24752, "display": "$247.52"  }
        }
      }
    },
    {
      "rate_class": "Standard",
      "primary": false,
      "rank": 4,
      "eligibility": { "eligible": true, "category": "immediate", "reasons": [] },
      "premium": {
        "amount": { "cents": 5921, "display": "$59.21" },
        "default_mode": "monthly",
        "modes": { "monthly": { "cents": 5921, "display": "$59.21" } }
      }
    },
    {
      "rate_class": "Graded",
      "primary": false,
      "rank": 18,
      "eligibility": { "eligible": true, "category": "graded", "reasons": [] },
      "premium": {
        "amount": { "cents": 8113, "display": "$81.13" },
        "default_mode": "monthly",
        "modes": { "monthly": { "cents": 8113, "display": "$81.13" } }
      }
    }
  ]
}

Example offer (monthly-budget request)

A monthly-budget offer carries a budget Money (period: "monthly") alongside the death_benefit the budget buys (period: null). Group multi-budget responses client-side by budget.amount.cents.

{
  "object": "plan_offer",
  "carrier": { "name": "Foresters" },
  "product": { "id": "prod_e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9", "name": "Foresters PlanRight", "class": "fex" },
  "death_benefit": {
    "amount": { "cents": 4372100, "display": "$43,721" },
    "period": null
  },
  "budget": {
    "amount": { "cents": 7500, "display": "$75.00" },
    "period": "monthly"
  },
  "pricing": [
    {
      "rate_class": "basic",
      "primary": true,
      "rank": 3,
      "eligibility": { "eligible": true, "category": "immediate", "reasons": [] },
      "premium": {
        "amount": { "cents": 7500, "display": "$75.00" },
        "default_mode": "MONTHLY-EFT",
        "modes": {
          "MONTHLY-EFT": { "cents": 7500, "display": "$75.00" },
          "ANNUAL":      { "cents": 82500, "display": "$825.00" }
        }
      }
    }
  ]
}

Reading pricing[]

FieldRead it for
row.rate_classCarrier-authored class label (Preferred Plus, Standard, Graded, …). Render verbatim.
row.primaryExactly one row per offer has primary: true. Render it as the headline.
row.rankServer-authoritative global rank across the response. Lower is better. Sort by it.
row.eligibility.categoryClosed enum: immediate | graded | rop | other.
row.eligibility.eligibletrue for offered rows. Ineligible rows surface by default with eligibility.eligible: false, a populated rate_class, and generic reasons; pass include_ineligible: false to omit them.
row.eligibility.reasonsGeneric, carrier-confidential strings. Safe to surface to agents.
row.premium.amount.centsHeadline premium for this row (the default mode), integer cents.
row.premium.amount.displayVerbatim carrier-formatted string for the headline. Render as-is.
row.premium.default_modeThe modes key whose value equals amount — the carrier's canonical billing mode for this product.
row.premium.modesOpen carrier-string map. Keys are carrier-supplied (monthly, MONTHLY-EFT, annual, …). Values are {cents, display}.

row.premium.amount is the apples-to-apples comparison value — it's always the default mode's amount, so comparing row.premium.amount.cents across rows never mixes modes. row.premium.default_mode names which mode that is, so you can label the comparison without inspecting the full grid.

Code examples

// TypeScript — render the headline, build the comparison table.
import {
  Isa, Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';

const isa = await Isa.withBearer(undefined, undefined, { apiVersion: { prequalify: 'v3' } });
const { data } = await isa.zyins.prequalify({
  applicant: {
    dob: '1962-04-18', sex: Sex.Male,
    height: Height.fromFeetInches(5, 10), weight: Weight.fromPounds(195),
    state: 'NC', nicotineUse: { lastUsed: NicotineDuration.Never },
  },
  coverage: Coverage.faceValue(25_000),
  products: ProductSelection.of([Products.Fex.AetnaAccendo]),
});

for (const offer of data.plans) {
  const headline = offer.pricing.find(row => row.primary);
  console.log(offer.carrier.name, headline?.rateClass, headline?.premium?.amount.display);

  const others = offer.pricing.filter(row => !row.primary && row.eligibility.eligible);
  for (const row of others) {
    console.log(`  ${row.rateClass}: ${row.premium?.amount.display} (${row.eligibility.category})`);
  }
}
# Python — same shape. `envelope` is the prequalify result; offers live
# under envelope.data.plans.
for offer in envelope.data.plans:
    headline = next(row for row in offer.pricing if row.primary)
    print(offer.carrier.name, headline.rate_class, headline.premium.amount.display)

    for row in offer.pricing:
        if row.primary or not row.eligibility.eligible:
            continue
        print(f"  {row.rate_class}: {row.premium.amount.display} ({row.eligibility.category})")
// Go — server picks the headline; never compute it client-side.
for _, offer := range result.Plans {
    for _, row := range offer.Pricing {
        if row.Primary {
            fmt.Println(offer.Carrier.Name, row.RateClass, row.Premium.Amount.Display)
        }
    }
}
// C# — LINQ query for headline.
foreach (var offer in result.Plans) {
    var headline = offer.Pricing.First(r => r.Primary);
    Console.WriteLine($"{offer.Carrier.Name} {headline.RateClass} {headline.Premium.Amount.Display}");
}
// PHP — filter for headline.
foreach ($result->plans as $offer) {
    $headline = array_values(array_filter($offer->pricing, fn($r) => $r->primary))[0];
    echo "{$offer->carrier->name} {$headline->rate_class} {$headline->premium->amount->display}\n";
}

All money on /v3/prequalify and /v3/quote is integer cents + display string, including every entry in row.premium.modes.

Multi-amount requests: client-side grouping

The response is always a flat data.plans[] — single amount or many. On a multi-amount probe (Coverage.faceValues([…]) / Coverage.monthlyBudgets([…])) the same product appears once per requested amount. Group client-side by the requested dimension: face-amount offers key off deathBenefit.amount.cents, monthly-budget offers off budget.amount.cents. The byAmount helper picks the right dimension for you.

// TypeScript — group the flat plans[] by the requested amount.
import {
  Isa, Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';
import { byAmount } from 'isa-sdk/zyins';

const isa = await Isa.withBearer(undefined, undefined, { apiVersion: { prequalify: 'v3' } });
const { data } = await isa.zyins.prequalify({
  applicant: {
    dob: '1962-04-18', sex: Sex.Male,
    height: Height.fromFeetInches(5, 10), weight: Weight.fromPounds(195),
    state: 'NC', nicotineUse: { lastUsed: NicotineDuration.Never },
  },
  coverage: Coverage.faceValues([10_000, 25_000, 50_000]),
  products: ProductSelection.of([Products.Term.FidelityLifeInstabrainTerm]),
});

for (const [, offers] of byAmount(data.plans)) {
  console.log(`Amount ${offers[0].deathBenefit?.amount.display}`);
  for (const offer of offers) {
    const headline = offer.pricing.find(r => r.primary);
    console.log(`  ${offer.carrier.name}: ${headline?.premium?.amount.display}`);
  }
}
# Python — group the flat plans[] by death_benefit.amount.cents.
# `envelope` is the prequalify result for a multi-amount probe.
from collections import defaultdict

by_dim = defaultdict(list)
for offer in envelope.data.plans:
    # death_benefit is None for premium-only products (medsup) — they have no
    # face-amount dimension to group on, so skip them here.
    if offer.death_benefit is None:
        continue
    by_dim[offer.death_benefit.amount.cents].append(offer)

for cents, offers in by_dim.items():
    head_db = offers[0].death_benefit
    assert head_db is not None  # narrowed: every offer in this bucket has one
    print(f"Amount: {head_db.amount.display}")
    for offer in offers:
        headline = next(r for r in offer.pricing if r.primary)
        if headline.premium is not None:
            print(f"  {offer.carrier.name}: {headline.premium.amount.display}")
// Go — group the flat plans[] by death_benefit.amount.cents.
byAmount := make(map[int64][]zyins.V3Offer)
for _, offer := range result.Plans {
    // DeathBenefit is nil for premium-only products (medsup) — they have no
    // face-amount dimension to group on, so skip them here.
    if offer.DeathBenefit == nil {
        continue
    }
    key := offer.DeathBenefit.Amount.Cents
    byAmount[key] = append(byAmount[key], offer)
}
for _, offers := range byAmount {
    fmt.Printf("Amount: %s\n", offers[0].DeathBenefit.Amount.Display)
    for _, offer := range offers {
        for _, row := range offer.Pricing {
            if row.Primary && row.Premium != nil {
                fmt.Printf("  %s: %s\n", offer.Carrier.Name, row.Premium.Amount.Display)
            }
        }
    }
}

For monthly-budget requests, group by budget.amount.cents instead. The byAmount helper detects budget responses and switches the key automatically.

death_benefit is null for premium-only products

Life products (fex, term, preneed) pay out a coverage amount, so their death_benefit is a Money value. Premium-only products — medsup (Medicare Supplement) — have no payout; their value is the monthly premium in pricing[].premium. On medsup offers, death_benefit is null.

The key is always present on the wire, so null-check it — you don't have to test for a missing key:

// fex / term / preneed offer
"death_benefit": { "amount": { "cents": 2500000, "display": "$25,000" }, "period": null }

// medsup offer — premium-only, no payout coverage
"death_benefit": null

Null-check before reading .amount, and branch on product.type when you render:

import type { V3Offer } from 'isa-sdk';

declare const offer: V3Offer;
declare const ui: { coverageLabel: { textContent: string } };

// good — null-check, then read the coverage; medsup leads with premium instead
const headline = offer.pricing.find(r => r.primary);
if (offer.deathBenefit) {
  ui.coverageLabel.textContent = offer.deathBenefit.amount.display;   // "$25,000"
} else {
  // medsup: no payout coverage — show the monthly premium as the headline
  ui.coverageLabel.textContent = headline?.premium?.amount.display ?? '—';
}

// bad — assumes deathBenefit is always populated; throws on medsup
ui.coverageLabel.textContent = offer.deathBenefit!.amount.display;
from isa_sdk import Isa
from isa_sdk.zyins import PrequalifyRequest

isa = Isa.with_bearer()
request: PrequalifyRequest
envelope = isa.zyins.prequalify(request)

for offer in envelope.data.plans:
    if offer.death_benefit is not None:
        coverage = offer.death_benefit.amount.display          # life products
    else:
        headline = next((r for r in offer.pricing if r.primary), None)
        coverage = headline.premium.amount.display if headline and headline.premium else "—"  # medsup

cents is for math, display is for humans

Never parse display for computation. Never format cents for rendering.

import type { V3Premium } from 'isa-sdk';

declare const a: { premium: V3Premium };
declare const b: { premium: V3Premium };
declare const row: { premium: V3Premium };
declare const ui: { priceLabel: { textContent: string } };

// good — compare on cents, render display verbatim
const cheaper = a.premium.amount.cents < b.premium.amount.cents;
ui.priceLabel.textContent = row.premium.amount.display;

// bad — parsing display for math, formatting cents by hand
const wrong = parseFloat(a.premium.amount.display) < parseFloat(b.premium.amount.display);
ui.priceLabel.textContent = `$${(row.premium.amount.cents / 100).toFixed(2)}`;

Carrier display strings carry formatting (commas, decimal places, conventions) that cents loses. Render exactly what the carrier intends.

rate_class and mode keys are carrier-specific

Both fields are verbatim from the carrier — never lowercased, aliased, or remapped.

Examples: a standard policy might be STANDARD or default; a preferred rate PREFERRED; a graded policy MODIFIED or GRADED depending on carrier convention.

Mode keys vary too: monthly vs MONTHLY-EFT vs PAC-EFT-MONTHLY. Treat both as opaque strings — surface them verbatim to agents.

v2 legacy pricing

The rest of this guide documents the legacy v2 wire shape: a top-level premium plus an other_offers[] array. v2 keeps working; new integrations should target v3.

Where prices appear

Two fields on every plan_offer carry a price-shaped value:

  • premium — what the applicant pays for the policy
  • death_benefit — what the policy pays out

Both follow the same {cents, display} shape. premium carries additional fields because policies are quoted across multiple payment frequencies and rate classes.

The premium shape

"premium": {
  "cents": 10159,
  "display": "$101.59",
  "mode": "MONTHLY",
  "rate_class": "PREFERRED",
  "modes": {
    "MONTHLY":     { "cents": 10159,  "display": "$101.59"   },
    "QUARTERLY":   { "cents": 29914,  "display": "$299.14"   },
    "SEMI-ANNUAL": { "cents": 58698,  "display": "$586.98"   },
    "ANNUAL":      { "cents": 112880, "display": "$1,128.80" }
  }
}
FieldRead it for
centsArithmetic, persistence, sorting. Integer USD cents, rounded.
displayUI rendering. Verbatim carrier-formatted string.
modeThe active payment frequency for cents and display (e.g. MONTHLY).
rate_classThe applicant's resolved rate class for this offer (e.g. PREFERRED, STANDARD).
modesAll payment frequencies for this offer. Each value has its own cents and display.

Use cents and display when you want the headline price. Use modes when you want to show alternate payment frequencies.

When eligibility.eligible is false, premium is null. Guard accordingly.

Multiple eligibility tiers in one response

A single product may quote at multiple eligibility tiers (immediate, graded, ROP). The "best" tier appears as the top-level premium. The others appear under other_offers[], each with its own {eligibility, premium, rank}:

{
  "rank": 2,
  "eligibility": { "category": "immediate", "coverage_tier": "PREFERRED", "eligible": true },
  "premium":     { "cents": 10159, "display": "$101.59", "mode": "MONTHLY", "rate_class": "PREFERRED", "modes": {...} },
  "other_offers": [
    {
      "eligibility": { "category": "immediate", "coverage_tier": "STANDARD", "eligible": true },
      "premium":     { "cents": 12397, "display": "$123.97", ... },
      "rank": 3
    },
    {
      "eligibility": { "category": "rop", "coverage_tier": "MODIFIED", "eligible": true },
      "premium":     { "cents": 18243, "display": "$182.43", ... },
      "rank": 10
    },
    {
      "eligibility": { "eligible": false, "reasons": ["Applicant did not qualify at this tier"] },
      "premium": null,
      "rank": null
    }
  ]
}

Use the top-level premium for the headline price. Iterate other_offers[] only when you need to show side-by-side tier comparisons.

Death benefit

death_benefit is also {cents, display}. It applies to the whole plan_offer and is not repeated per other_offer (the death benefit is a property of the product and the requested coverage amount, not the eligibility tier).

"death_benefit": { "cents": 2000000, "display": "$20,000.00" }

Reading premiums from a response

curl -s 'https://zyins.isaapi.com/v2/prequalify' \
  -H "Authorization: Bearer $ISA_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d @prequalify.json |
  jq '.data.plans[] | select(.eligibility.eligible) | {carrier: .carrier.name, product: .product.name, premium: .premium.display}'
import { Isa } from 'isa-sdk';
import type { PrequalifyRequest } from 'isa-sdk';

const isa = await Isa.withBearer();
declare const req: PrequalifyRequest;

const envelope = await isa.zyins.prequalify(req);
for (const plan of envelope.data.plans) {
  const headline = plan.pricing.find((row) => row.primary && row.eligibility.eligible);
  if (!headline?.premium) continue;
  console.log(plan.carrier.name, plan.product.name, headline.premium.amount.display);
  console.log("  paid", headline.premium.defaultMode, "—", headline.premium.amount.cents, "¢");
}
from isa_sdk import Isa
from isa_sdk.zyins import PrequalifyRequest

isa = Isa.with_bearer()
req: PrequalifyRequest

envelope = isa.zyins.prequalify(req)
for plan in envelope.data.plans:
    headline = next((row for row in plan.pricing if row.primary and row.eligibility.eligible), None)
    if headline is None or headline.premium is None:
        continue
    print(plan.carrier.name, plan.product.display_name, headline.premium.amount.display)
    print(f"  {headline.premium.amount.cents}¢ ({headline.premium.default_mode})")
envelope, err := isa.Zyins.Prequalify(ctx, req)
if err != nil {
	return err
}
for _, plan := range envelope.Data.Plans {
	if !plan.Eligibility.Eligible || plan.Premium == nil {
		continue
	}
	fmt.Println(plan.Carrier.Name, plan.Product.Name, plan.Premium.Display)
}

See also

  • Prequalify — the main endpoint that returns priced offers.
  • POST /v3/quote — comparison-table quote at one or more coverage amounts.
  • Glossaryrate_class, coverage_tier, mode, and other carrier-specific terms.