Django
Integrate the ISA SDK into a Django project: AppConfig singleton, middleware, and webhook verification.
Django
This guide wires the ISA SDK into a Django project using an AppConfig singleton so one client instance serves all requests. It covers the prequalify call, idempotency, error handling, and webhook verification.
Prerequisites
- Python 3.10+, Django 4.2+
pip install isa-sdk djangoISA_TOKENin your environment (via.env, secret manager, or deployment config)
Client singleton via AppConfig
Construct the Isa client once at startup. Django's AppConfig.ready() runs after all models are loaded — it's the right place.
# isa_integration/apps.py
from django.apps import AppConfig
class IsaIntegrationConfig(AppConfig):
name = "isa_integration"
isa = None # populated in ready()
def ready(self):
import os
from isa_sdk.zyins import Isa
# Reads ISA_TOKEN from the environment.
IsaIntegrationConfig.isa = Isa.with_bearer()# isa_integration/__init__.py
default_app_config = "isa_integration.apps.IsaIntegrationConfig"Add "isa_integration" to INSTALLED_APPS in settings.py.
Retrieve the singleton from any view:
from isa_integration.apps import IsaIntegrationConfig
isa = IsaIntegrationConfig.isaPrequalify view
# isa_integration/views.py
from django.http import JsonResponse
from django.views import View
from isa_sdk.zyins import (
Applicant,
Coverage,
ISAError,
IsaApiError,
NicotineDuration,
NicotineUsageInput,
ProductSelection,
Sex,
ValidationError,
)
from isa_sdk.catalog import Products
from isa_integration.apps import IsaIntegrationConfig
class PrequalifyView(View):
def post(self, request):
isa = IsaIntegrationConfig.isa
try:
# The SDK mints a UUID v4 idempotency key per call automatically.
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),
)
except ValidationError as err:
return JsonResponse(
{"error": str(err), "param": err.param}, status=400
)
except IsaApiError as err:
return JsonResponse(
{"error": str(err), "code": err.code, "request_id": err.request_id},
status=502,
)
except ISAError as err:
return JsonResponse({"error": str(err)}, status=500)
plans = []
for offer in result.data.plans:
# Exactly one pricing row is the headline (primary=True).
primary = next((row for row in offer.pricing if row.primary), None)
premium = primary.premium if primary else None
plans.append({
"carrier": offer.carrier.name,
"product": offer.product.name,
# 'immediate' | 'graded' | 'rop'
"category": primary.eligibility.category if primary else None,
# verbatim carrier string / integer cents
"premium_display": premium.amount.display if premium else None,
"premium_cents": premium.amount.cents if premium else None,
})
return JsonResponse({"plans": plans, "request_id": result.request_id})# isa_integration/urls.py
from django.urls import path
from .views import PrequalifyView
urlpatterns = [
path("prequalify/", PrequalifyView.as_view()),
]Webhook verification
Django's CSRF middleware exempts views decorated with @csrf_exempt. The ISA SDK does not ship a Django-specific helper, but the verification is three lines.
# isa_integration/webhook_views.py
import hashlib
import hmac
import json
import os
import time
from django.http import HttpResponse, HttpResponseBadRequest
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
WEBHOOK_SECRET = os.environ["ISA_WEBHOOK_SECRET"]
TIMESTAMP_TOLERANCE_SECONDS = 300
def _verify_signature(raw_body: bytes, sig_header: str) -> bool:
# X-ZyINS-Signature is "t=<unix-seconds>,v1=<hex>".
fields = dict(part.split("=", 1) for part in sig_header.split(",") if "=" in part)
try:
ts = int(fields["t"])
sig = fields["v1"]
except (KeyError, ValueError):
return False
if abs(time.time() - ts) > TIMESTAMP_TOLERANCE_SECONDS:
return False
payload = f"{ts}.".encode() + raw_body
expected = hmac.new(
WEBHOOK_SECRET.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, sig)
@method_decorator(csrf_exempt, name="dispatch")
class WebhookView(View):
def post(self, request):
sig_header = request.headers.get("X-ZyINS-Signature", "")
if not _verify_signature(request.body, sig_header):
return HttpResponseBadRequest("invalid signature")
event = json.loads(request.body)
event_type = event.get("type")
if event_type == "license.activated":
_handle_license_activated(event)
return HttpResponse(status=200)
def _handle_license_activated(event):
pass # persist event["data"]["license_id"], update your recordsSettings checklist
# settings.py (additions only)
# ISA_TOKEN loaded from environment; never hardcode here.
# Use python-decouple, django-environ, or your secrets manager.
INSTALLED_APPS = [
# ...
"isa_integration",
]
# The SDK honors ISA_BASE_URL for staging.
# ISA_BASE_URL = "https://staging.zyins.isaapi.com"See also
Updated about 9 hours ago