Skip to main content

Unified Python SDK for ISA APIs (zyins, rapidsign, proxy).

Project description

sah-sdk

Python SDK for the Best Plan Pro API — powered by the ZyINS engine. Mirrors the canonical TypeScript SDK at packages/zyins/js/ with Python-idiomatic naming (snake_case) and pydantic v2 models.

Install

pip install sah-sdk

GitHub Packages fallback. Until sah-sdk is fully published on PyPI, install directly from the source repository:

pip install "git+https://github.com/Software-Automation-Holdings-LLC/sdk.git@sdk/v0.5.0#subdirectory=packages/python"

The wire surface is identical either way; only the install command changes.

Quick start

import os
from sah_sdk.zyins import Isa, Applicant, Coverage, PrequalifyInput, Sex

# Reads ISA_TOKEN from the environment — no explicit token needed.
isa = Isa.with_bearer()

result = isa.zyins.prequalify(PrequalifyInput(
    applicant=Applicant(
        dob="1962-04-18",
        sex=Sex.MALE,
        height_inches=70,
        weight_pounds=195,
        state="NC",
        nicotine_use="none",
    ),
    coverage=Coverage.face_value(100_000),
    products="colonial-penn.final-expense",
))

for plan in result.data.plans:
    print(plan.brand, plan.tier, plan.monthly_premium)

Isa.with_bearer() reads ISA_TOKEN from the environment. Authorization: Bearer <token>, Idempotency-Key, and the date-pinned Version header are set automatically.

First call in <15 lines

from sah_sdk.zyins import Isa, Coverage

isa = Isa.with_keycode(
    keycode="SDV-HWH-WDD",
    email="john.doe@acme-agency.com",
)
result = isa.zyins.prequalify_v2(
    applicant={"dob": "1962-04-18", "sex": "male", "state": "NC"},
    coverage=Coverage.face_value(25_000),
    products="colonial-penn.final-expense",
)
print(result.data.plans[0].monthly_premium)

Per-surface API versions

The ISA API is a federation of independently versioned surfaces. Every SDK release exports a frozen BUNDLED_API_VERSIONS mapping recording which /vN each surface targets:

from sah_sdk.zyins import BUNDLED_API_VERSIONS

print(BUNDLED_API_VERSIONS)
# {
#   "prequalify": "v2",
#   "quote":      "v2",
#   "datasets":   "v2",
#   "reference":  "v2",
#   "sessions":   "v1",
#   "branding":   "v1",
#   "cases":      "v1",
# }

Pin individual surfaces with a per-surface api_version map. There is no default key and no string shorthand — resolution is api_version.get(surface, BUNDLED_API_VERSIONS[surface]):

isa = await Isa.with_keycode(
    keycode="SDV-HWH-WDD",
    email="john.doe@acme-agency.com",
    api_version={"quote": "v2"},   # pin only quote; everything else bundled
)

The release that retargets prequalify / quote / datasets / reference to v3 will bump those entries. See SDK syntax proposal §2.7.

Reference data — .match()

The unversioned isa.zyins.reference namespace canonicalizes free-text medication and condition input. Unknown text never rejects — it returns a structured envelope so the final canonicalization fires server-side at /vN/prequalify:

ds = await isa.zyins.datasets.get(include=["conditions", "medications"])

insulin = isa.zyins.medications.match("insulin")
print(insulin.id, insulin.name, insulin.is_known)
# med_01KSR2WVAGC05ZGR6FA4QYEB12  INSULIN  True

# Symmetric traversal — what conditions is insulin used for?
used_for = insulin.conditions(isa.zyins.reference.Sort.MOST_COMMON_FIRST)
# frequency-ordered list; cond_01KSR2WVAGC05ZGR6FA4QYEA8X first

novel = isa.zyins.medications.match("NewExperimental XR 2026")
# → {"is_known": False, "input_text": "NewExperimental XR 2026", ...}

Sort.MOST_COMMON_FIRST and Sort.ALPHABETICAL are the two supported orderings.

Case storage — bring your own

isa.zyins.cases.* routes through a CaseStorage adapter. The default is the zero-knowledge store — ISA's servers only hold ciphertext and an opaque ID. To plug a carrier-controlled store, pass your adapter at construction:

isa = await Isa.with_keycode(
    keycode=..., email=...,
    case_storage=CarrierCaseStorage(),   # optional; default = ZeroKnowledgeCaseStorage
)

See cases guide for the full bring-your-own pattern.

Auth deviation from the TS SDK

The TypeScript SDK in this monorepo still carries the pre-#286 HMAC device signing surface (AuthContext with licenseKey + orderId + email + deviceId). The Python SDK is built against the post-#286 wire contract: a single bearer token (isa_live_* / isa_test_*) is the entire auth surface. This is the intentional simplification called out in the platform's platform_v1_architecture notes.

Surface

TypeScript Python
client.prequalify(req) client.prequalify.run(input)
client.license.activate/deactivate/check client.license.* (mirrored)
client.case.email(req) client.case.email(input)
(new) client.quote.run(input)
(new) client.datasets.list/get
(new) client.reference_data.get(kind)
(new) client.usage.summary(period)

Errors mirror the TS hierarchy: ISAError (alias ZyInsError, also exported as IsaApiError) → LicenseError, PrequalifyError, ValidationError, RateLimitError, AuthError, IsaIdempotencyConflictError.

Isa factory client

Per SDK_DESIGN.md §3, the recommended entry point is the Isa class with three named factories:

from sah_sdk.zyins import Isa

# Reads ISA_TOKEN from the environment.
isa = Isa.with_bearer()
env = isa.zyins.prequalify(req)
print(env.data, env.request_id, env.idempotency_key, env.retry_attempts)

# Or pass the token explicitly.
isa = Isa.with_bearer("isa_live_…")

# License factory — reads ISA_LICENSE_KEYCODE / ISA_LICENSE_EMAIL.
isa = Isa.with_license()

# Session factory — reads ISA_SESSION_ID / ISA_SESSION_SECRET.
isa = Isa.with_session()

Each factory raises IsaConfigError with a clear, actionable message if the required env vars are unset and no explicit arguments are supplied.

Raw HTTP access

Every method has a .with_raw_response() variant returning both the parsed envelope and the underlying HTTP metadata:

env, raw = isa.zyins.prequalify.with_raw_response(req)
raw.status     # int
raw.url        # str
raw.headers    # read-only mapping

Debug logging

Set ISA_LOG=debug to dump every request and response to stderr — never stdout, so parent processes piping the consumer's JSON output stay clean. Credential headers (Authorization, X-Device-Signature, X-Session-Signature) and PII body fields (email, dob, ssn, phone) are redacted automatically.

Idempotency conflicts

When the same Idempotency-Key is replayed with a different body the server returns 409 idempotency_conflict. The SDK raises IsaIdempotencyConflictError with .key and .first_seen_at so the caller can audit the queued-write bug class:

from sah_sdk.zyins import IsaIdempotencyConflictError

try:
    isa.zyins.prequalify(req, idempotency_key="case-42")
except IsaIdempotencyConflictError as e:
    log.error("key %s first seen at %s", e.key, e.first_seen_at)

Concurrency

The Isa client is safe for use with asyncio.gather and concurrent.futures — every request mints a fresh request-id and idempotency key, and shared client state (auth, base URL, debug logger) is read-only after construction. Reuse a single Isa instance across all concurrent requests; the underlying HTTP transport pools connections for you.

import asyncio
from sah_sdk.zyins import Isa

isa = Isa.with_bearer()

async def one(req):
    return isa.zyins.prequalify(req)

results = await asyncio.gather(*(one(r) for r in batch))
# Each result.request_id is distinct.

Development

hatch run test         # pytest
hatch run lint         # ruff + mypy --strict
hatch build            # wheel + sdist

Live-integration tests run only when ZYINS_TEST_TOKEN is set:

ZYINS_TEST_TOKEN=isa_test_... hatch run test -- -m integration

Licenses and Ready

The Python SDK exposes the public BPP license-lifecycle surface and the platform readiness probe on every ZyInsClient:

from sah_sdk.zyins import LicenseCheckInput, ZyInsClient

client = ZyInsClient("isa_live_...")

result = client.license.check(
    LicenseCheckInput(
        email="john.doe@acme-agency.com",
        keycode="ABC-123-XYZ",
        device_id="dev_01HZK2N5GQR9T8X4B6FJW3Y1AS",
    )
)
# result.status: "valid" | "invalid" | "inactive"

ready = client.health.get_readiness()
# ready.ready: True on every required probe = "serving"

The pre-existing client.license (singular) sub-client targets the authenticated /v1/license/* self-status endpoints and is untouched.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

isa_sdk-1.0.0.tar.gz (613.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

isa_sdk-1.0.0-py3-none-any.whl (528.8 kB view details)

Uploaded Python 3

File details

Details for the file isa_sdk-1.0.0.tar.gz.

File metadata

  • Download URL: isa_sdk-1.0.0.tar.gz
  • Upload date:
  • Size: 613.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for isa_sdk-1.0.0.tar.gz
Algorithm Hash digest
SHA256 02f2db456276fc7062420490364a767ec38c4591e333e6dc401841e4b0692a02
MD5 109b12d6ab2c2120d3b102ee17a9617e
BLAKE2b-256 bf49311371af8b3f771777e707dd7ffea288e2da8a40c07689bae5155b9b25d6

See more details on using hashes here.

File details

Details for the file isa_sdk-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: isa_sdk-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 528.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for isa_sdk-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1ca1a9a6b8b63273c9452ea0a5b2dc9d7042ea2afd3cf97477ce1f5b69e78b88
MD5 0ce32d1383762f6657f6b34e8e6b7845
BLAKE2b-256 7cd134c518920b7b174af0d0654fc102b0edc0460ef8277d28edbd3a09036587

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page