Quickstart

From receiving your test key to a 200 response in under five minutes.

Quickstart

Copy a sample, set one environment variable, run it. You'll have a working call in under five minutes.

Your test key arrives by email within a few minutes of completing Stripe checkout. The prose below explains the choices after you see that 200 response.

Domain terms ahead. If eligibility category, rate_class, FEX, or ROP aren't familiar, keep the Glossary open in another tab — every term used in this quickstart is defined there.

Your first call (60 seconds)

The /livez probe is unauthenticated and returns a real production response. Use this to confirm the API is reachable from your network before adding credentials.

curl https://zyins.isaapi.com/livez

Expected response:

{ "status": "ok" }

If you see that envelope, the network path works and you can move to the authenticated calls below.

Your first authenticated call

Three steps. Under five minutes once your token is in hand.

1. Get a test token

Test tokens start with isa_test_ and route to the sandbox. They never touch live underwriting or consume quota.

When you complete checkout at checkout.isaapi.com, you get both a test and a live token automatically. A credentials email from [email protected] arrives within a few minutes with:

  • Your isa_live_* and isa_test_* keys
  • A working curl sample
  • A link back to this quickstart

No human approval needed, no business-day wait.

For custom procurement or partners without Stripe billing, email [email protected] (one-business-day turnaround). Self-service token management from the dashboard is coming soon.

Store the token in an environment variable:

Placeholder token below. Replace the placeholder with your real key from the credentials email. Running the snippet verbatim returns 401 unauthorized.

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

2. Pick a language and copy the sample

Each sample is a complete, runnable program that prequalifies a sample applicant — John Doe, age 64, North Carolina, no medications — for an Aetna Accendo final expense plan.

Money inputs are integer dollars. Coverage.faceValue(25000) — and the wire-level quote_options.amounts: ["25000"] — both mean $25,000, not $250.00. Money outputs always include both cents (integer minor units) and display (verbatim carrier string), so you never have to decide formatting on the way out.

curl

curl -X POST https://zyins.isaapi.com/v3/prequalify \
  -H "Authorization: Bearer $ISA_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "applicant": {
      "sex":           "male",
      "dob":           "1962-04-18",
      "height_inches": 70,
      "weight_lbs":    195
    },
    "coverage": { "face_amount_cents": 2500000, "state": "NC" },
    "products": ["prod_d7b57156-3e83-506b-8936-0692c1193dc7"]
  }'

The v3 request body is nested: separate applicant, coverage, and products objects.

Key v3 details:

  • coverage.face_amount_cents is an integer in cents (so 2,500,000¢ = $25,000)
  • products is an array of prod_<uuid> identifiers from the catalog constants, never bare slugs
  • For multi-amount comparisons, use POST /v3/quote

If you're on v2, the request body is flat (date_of_birth, gender, quote_options, …) — see the prequalify reference.

TypeScript

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]),
  });

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

Python

from isa_sdk import Isa
from isa_sdk.zyins import (
    Applicant, NicotineDuration, NicotineUsageInput, Sex, Coverage, ProductSelection,
)
from isa_sdk.catalog import Products

isa = Isa.with_bearer()

result = isa.zyins.prequalify(
    applicant=Applicant(
        dob="1962-04-18", sex=Sex.MALE, height_inches=70, weight_pounds=195,
        state="NC", nicotine_use=NicotineUsageInput(last_used=NicotineDuration.NEVER),
    ),
    coverage=Coverage.face_value(25_000),
    products=ProductSelection.of(Products.Fex.AetnaAccendo),
)

for offer in result.data.plans:
    headline = next((row for row in offer.pricing if row.primary), None)
    if headline is not None and headline.premium is not None:
        print(offer.carrier.name, offer.product.name, headline.premium.amount.display)

PHP

<?php
use Isa\Sdk\Isa;
use Isa\Sdk\Zyins\Applicant;
use Isa\Sdk\Zyins\Coverage;
use Isa\Sdk\Zyins\Height;
use Isa\Sdk\Zyins\NicotineDuration;
use Isa\Sdk\Zyins\NicotineUsageInput;
use Isa\Sdk\Zyins\Reference\PrequalifyRequest;
use Isa\Sdk\Zyins\Sex;
use Isa\Sdk\Zyins\Weight;
use Isa\Sdk\Catalog\Products;

$isa = Isa::withBearer();

$result = $isa->zyins->prequalify->run(new PrequalifyRequest(
    applicant: new Applicant(
        dob: '1962-04-18', sex: Sex::Male,
        height: Height::fromFeetInches(5, 10), weight: Weight::fromPounds(195),
        state: 'NC', nicotineUse: new NicotineUsageInput(lastUsed: NicotineDuration::Never),
    ),
    coverage: Coverage::faceValue(25000),
    products: [Products::fex()->aetnaAccendo()],
));

foreach ($result->plans as $offer) {
    foreach ($offer->pricing as $row) {
        if ($row->primary && $row->premium !== null) {
            echo $offer->carrier->name, ' ', $offer->product->name, ' ', $row->premium->amount->display, PHP_EOL;
        }
    }
}

Go

package main

import (
	"context"
	"fmt"

	sdk "github.com/isa-sdk/sdk"
	"github.com/isa-sdk/sdk/catalog"
	"github.com/isa-sdk/sdk/zyins"
)

func main() {
	isa, err := sdk.WithBearer("")
	if err != nil {
		panic(err)
	}
	products, err := zyins.NewProductSelectionOf(catalog.Products.Fex.AetnaAccendo())
	if err != nil {
		panic(err)
	}
	height, err := zyins.NewHeight(5, 10)
	if err != nil {
		panic(err)
	}
	weight, err := zyins.NewWeight(195)
	if err != nil {
		panic(err)
	}
	coverage, err := zyins.NewFaceValueCoverage(25_000) // dollars — $25,000 face value
	if err != nil {
		panic(err)
	}

	result, err := isa.Zyins.Prequalify.Run(context.Background(), &zyins.PrequalifyRequest{
		Applicant: zyins.Applicant{
			DOB:         "1962-04-18",
			Sex:         zyins.SexMale,
			State:       catalog.StateNorthCarolina,
			Height:      height,
			Weight:      weight,
			NicotineUse: zyins.NicotineUsageInput{LastUsed: zyins.NicotineNever},
		},
		Coverage: coverage,
		Products: products,
	})
	if err != nil {
		panic(err)
	}
	for _, offer := range result.Plans {
		for _, row := range offer.Pricing {
			if row.Primary && row.Premium != nil {
				fmt.Println(offer.Carrier.Name, offer.Product.Name, row.Premium.Amount.Display)
			}
		}
	}
}

C#

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

var isa = IsaClient.WithBearer();

var result = await isa.Zyins.PrequalifyAsync(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} {offer.Product.Name} {headline.Premium.Amount.Display}");
}

3. Inspect the response

Every successful 2xx response has the same five envelope fields. The payload is under data; everything else is provenance you store with your business record.

Each entry in data.plans[] represents one product and includes a death_benefit Money value plus a pricing[] table. The pricing table has one row per rate class, with the best qualifying class flagged primary: true:

{
  "object":          "prequalify_result",
  "request_id":      "req_01HZK2N5GQR9T8X4B6FJW3Y1AS",
  "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
  "livemode":        false,
  "data": {
    "plans": [
      {
        "object": "plan_offer",
        "id": "9b7d9b5c-1f3a-5c2b-9a4f-6e1c2d3b4a5e",
        "eligible": true,
        "carrier":  { "id": "7a3e4b5c-6d7e-5f80-8a9b-0c1d2e3f4a5b", "name": "Aetna Accendo",
                      "logo_url": "https://zyins.isaapi.com/v1/logo/Aetna%20Accendo" },
        "product":  { "id": "prod_d7b57156-3e83-506b-8936-0692c1193dc7",
                      "name": "Aetna Accendo Final Expense",
                      "class": "fex" },
        "plan_info": [{ "key": "telesales", "label": "Telesales", "values": ["Required in all written states"] }],
        "death_benefit": { "amount": { "cents": 2500000, "display": "$25,000.00" }, "period": null },
        "pricing": [
          {
            "rate_class": "default",
            "primary": true,
            "rank": 2,
            "eligibility": { "eligible": true, "category": "immediate", "coverage_tier": "default", "reasons": [] },
            "premium": {
              "amount": { "cents": 8742, "display": "$87.42" },
              "default_mode": "MONTHLY-EFT",
              "modes": {
                "MONTHLY-EFT": { "cents": 8742,   "display": "$87.42"   },
                "ANNUAL":      { "cents": 100265, "display": "$1002.65" }
              }
            }
          }
        ],
        "metadata": {}
      }
    ]
  }
}

That's it. You have a real underwriting decision, a real request_id for support, and working authentication. Everything below digs deeper.

Next: 15 minutes of depth

Authentication

The quickstart used a bearer token — Authorization: Bearer $ISA_TOKEN. See Authentication for token rotation, the test-vs-live token prefix, and apiVersion pinning.

Per-language deep dive

Each SDK has its own quickstart with the full surface — error subclasses, configuration knobs, raw-response access, retry tuning:

Picker UIs — autocorrect, match, autocomplete

If you're building an input form, the SDK ships three adapters that compose into one pipeline. Warm the dataset cache once, then correct → match → submit:

import { Isa } from 'isa-sdk';

const isa = await Isa.withBearer();
await isa.zyins.datasets.get();

const corrected = isa.zyins.autocorrector.correct('hi blod pressur', { mode: 'submit' });
const hbp       = isa.zyins.conditions.match(corrected);
// hbp.id === 'cond_01KSR2WVAGC05ZGR6FA4QYEA8X'

const top5 = await isa.zyins.medications.autocomplete('lisi', { limit: 5 });
void hbp; void top5;

See Reference catalog, Autocorrect, Match, and Autocomplete for the adapter contracts. The recommended autocorrect → match → prequalify pipeline is in Prequalify.

What to read next

  • Glossary — every domain term the API uses, defined once. Start here if any term in this quickstart was unfamiliar.
  • Prequalify — the prequalify guide: cents-and-display money, closed eligibility categories, and full per-mode rate grids.
  • Reference catalog — the /v3/datasets bundle shape; ULID identifiers and inline treated_with[] / used_for[].
  • Migrating from v2 to v3 — for existing integrators with useAutocorrect.js / useAutocomplete.js to delete.
  • Idempotency — every mutating request takes a UUID v4 Idempotency-Key. The SDK mints one per call; bring your own for cross-process replay safety.
  • Errors — every error code, every status, every remediation.
  • Pagination — cursor walk for every list endpoint.
  • Webhooks — verifying signed callbacks in constant time.
  • Test mode — what changes when you use an isa_test_* token vs. an isa_live_* one.
  • Rate limits — per-license quota and the headers we return alongside every response.
  • API reference — the full endpoint reference.

Troubleshooting the first call

SymptomProbable causeFix
401 unauthorizedISA_TOKEN missing or malformedConfirm the env var is set and starts with isa_test_ or isa_live_
403 forbiddenToken lacks the prequalify scopeCreate a new token with prequalify checked, or use the default scope set
400 validation_errorA field doesn't match the expected shapeThe param field on the error response is a JSON pointer to the failing field
409 idempotency_conflictYou replayed an Idempotency-Key with a different bodyGenerate a fresh UUID v4 for the new request
429 rate_limit_exceededPer-license quota exceededHonor the Retry-After header; see rate limits
Connection times outOutbound HTTPS to zyins.isaapi.com blockedAllowlist zyins.isaapi.com:443 on your egress firewall

Every error response includes a request_id. Quote that value when you escalate to support — it maps directly to the server log.