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.1.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.1-py3-none-any.whl (528.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: isa_sdk-1.0.1.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.1.tar.gz
Algorithm Hash digest
SHA256 1eaa8dfb3308ceb46a944382e2d57401e9d8d128e4ac1808e84320c26a8b545d
MD5 cbccaf0070541b04ab09e451825ad8e7
BLAKE2b-256 3dd9aa9e21be4811f0249783c7849828ac75219ef879dae833e3e63130347d89

See more details on using hashes here.

File details

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

File metadata

  • Download URL: isa_sdk-1.0.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c97135602edc91e3240426b63500b0bab1b9d4cec59cd4b94a1b9b66b550a323
MD5 3a68cf729d355b9b3f4cb8c5ed2785e9
BLAKE2b-256 4715144fffac4a6240ad6606089e320c70514430cfb7abd838a193d235be8b77

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