Quickstart: TypeScript
Get a prequalify decision from a fresh Node project in under five minutes.
TypeScript Quickstart
Get a prequalify decision from a fresh Node project in under five minutes. No HTTP libraries, no auth plumbing, no boilerplate.
1. Install
npm install isa-sdkThe SDK targets Node 20+ and ships TypeScript declarations. ESM and CommonJS both work.
2. Hello world
Set the bearer token and run prequalify:
export ISA_TOKEN="<paste your isa_test_… key here>"import {
Isa, Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';
async function main() {
const isa = await Isa.withBearer();
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]),
});
// data.plans is a flat array — one entry per product.
for (const offer of data.plans) {
const headline = offer.pricing.find(row => row.primary);
console.log(
offer.carrier.name,
offer.product.name,
headline?.rateClass,
headline?.premium?.amount.display,
);
}
}
main().catch(console.error);Isa.withBearer() is static async — it returns a Promise<Isa>, so await it inside an async function. It reads ISA_TOKEN from the environment. No transport setup, no request plumbing.
Top-level await works in Node 20+ ESM; wrapping in async function main() is portable across both ESM and CommonJS.
The response structure is Envelope<PrequalifyResult>. The offers live under .data.plans. The requestId, idempotencyKey, and livemode sit alongside for your records.
The inputs use typed builders, not raw JSON. Coverage.faceValue(25_000) takes integer dollars (not cents). Height.fromFeetInches(5, 10) and Weight.fromPounds(195) construct demographics. ProductSelection.of([…]) picks products from the typed Products catalog.
Each offer's deathBenefit and pricing[].premium.amount carry both cents (for arithmetic) and display (verbatim carrier string, e.g., "$87.42").
3. A real example
A full prequalify sequence for a real applicant. John Doe, born 1962-04-18, lives in North Carolina, 5'10", 195 lb, no medications, looking at Aetna Accendo final expense at $25,000 face value.
import {
Isa, Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';
async function main() {
const isa = await Isa.withBearer();
// Single face amount — $25,000 coverage in NC
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,
Products.Term.FidelityLifeInstabrainTerm,
]),
});
// data.plans is a flat array — one entry per product.
// Each offer carries a deathBenefit Money value and a flat pricing[] table.
for (const offer of data.plans) {
console.log(offer.deathBenefit?.amount.display); // "$25,000.00"
const headline = offer.pricing.find(row => row.primary);
console.log(
offer.carrier.name,
headline?.rateClass,
headline?.premium?.amount.display, // verbatim carrier string, e.g. "$87.42"
headline?.premium?.amount.cents, // integer cents for arithmetic
);
// Alternate qualifying tiers (e.g. graded when best is immediate) are sibling rows:
for (const row of offer.pricing) {
if (row.primary || !row.eligibility.eligible) continue;
console.log(' also:', row.rateClass, row.premium?.amount.display, row.eligibility.category);
}
}
}
main().catch(console.error);Every method resolves to an Envelope<T>. The offers are at .data.plans; requestId, idempotencyKey, and livemode sit alongside for your records.
4. Multi-amount face-value quote
Request several coverage amounts in one call with Coverage.faceValues([…]). The same product appears once per requested amount in the flat data.plans[] response.
Group client-side with the byAmount helper (or by deathBenefit.amount.cents) for a side-by-side comparison table.
import {
Isa, Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';
import { byAmount } from 'isa-sdk/zyins';
async function main() {
const isa = await Isa.withBearer();
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]),
});
// Flat plans[], grouped client-side by death_benefit.amount.cents.
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}`);
}
}
}
main().catch(console.error);5. Monthly-budget prequalify
Solve for coverage given a monthly budget. Use Coverage.monthlyBudget(75) (integer dollars per month).
Each offer returns both the requested budget and the deathBenefit the budget buys. The budget has period: "monthly", while deathBenefit has period: null.
import {
Isa, Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';
async function main() {
const isa = await Isa.withBearer();
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.monthlyBudget(75), // $75/mo budget
products: ProductSelection.of([Products.Fex.AetnaAccendo]),
});
for (const offer of data.plans) {
const headline = offer.pricing.find(r => r.primary);
console.log(
`Budget: ${offer.budget?.amount.display} → Coverage: ${offer.deathBenefit?.amount.display}`,
);
console.log(` Premium: ${headline?.premium?.amount.display} (${headline?.rateClass})`);
}
}
main().catch(console.error);6. Errors
Every SDK exception extends IsaError. Match on the specific subclass — don't switch on error.message:
import {
Isa, IsaApiError, IsaIdempotencyConflictError, IsaConfigError,
Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';
async function run() {
const isa = await Isa.withBearer();
return 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]),
});
}
try {
await run();
} catch (err) {
if (err instanceof IsaIdempotencyConflictError) {
// Replayed key with a different body. Almost always a bug — log and bail.
console.error('idempotency conflict:', err.key, 'first seen', err.firstSeenAt);
throw err;
}
if (err instanceof IsaApiError) {
// 4xx / 5xx from the API. Stable `code`, machine-readable, human-safe.
console.error(err.code, err.message, 'request', err.requestId);
throw err;
}
if (err instanceof IsaConfigError) {
// Thrown synchronously when ISA_TOKEN is missing or malformed.
console.error('configuration error:', err.message);
throw err;
}
throw err;
}The four you see first:
| Error | When it fires | What to do |
|---|---|---|
IsaConfigError | Isa.withBearer() called with no token and no ISA_TOKEN in the environment | Set the env var or pass { token: '...' } explicitly |
IsaApiError | Any 4xx/5xx from the API | Match on err.code (stable enum), log err.requestId |
IsaIdempotencyConflictError | Same Idempotency-Key, different body | Treat as a bug; the SDK never produces this on retry |
IsaError | Base class | Catch-all for typed SDK errors |
7. Configuration
The defaults match what most consumers need. You should not have to override anything in the quickstart:
| Default | Value | When to override |
|---|---|---|
| Base URL | Production | Set ISA_BASE_URL for staging or self-hosted |
| Timeout | 30s per request | Pass { timeout: ms } to a specific call |
| Retries | 3 attempts, exponential backoff, 5xx + 429 only | { retries: 0 } to disable per-call |
Idempotency-Key | UUID v4 minted per call | Pass your own for cross-process replays |
Version header | The SDK's pinned API version | Override per-call for staged rollouts |
| Transport | Built-in fetch | Inject your own for proxies, mocking, telemetry |
If you find yourself overriding more than one of these in normal usage, file an issue — the default is probably wrong.
8. What's next
- Prequalify guide — full envelope, eligibility categories, premium mode grid, and field contract
- Authentication guide — when to use bearer vs. license vs. session, lifecycle, rotation
- Idempotency guide — how the SDK mints keys, when to bring your own
- Errors reference — every
codevalue, status mapping, remediation - Webhooks guide — verifying signed webhook deliveries with constant-time comparison
- API Reference — every endpoint, every parameter, every response field
Need raw HTTP details (status, headers, request ID)? Every method has a [methodName]Raw sibling:
import {
Isa, Sex, Height, Weight, NicotineDuration, Coverage, ProductSelection, Products,
} from 'isa-sdk';
const isa = await Isa.withBearer();
const { data, response } = await isa.zyins.prequalifyRaw({
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]),
});
console.log(response.status, data.requestId);Updated about 10 hours ago