ExonaExona API
Guides

Guide: Application Questionnaire Workflow

How to integrate Exona scans into an existing application intake flow.

The scenario

You have an existing application form for AI & Tech E&O or Cyber insurance. When an insured submits their application, you want to automatically enrich it with Exona's risk intelligence: including the applicant's own answers: before an underwriter reviews it.


Step 1: Map your application questions

You do not need to change your existing application form. Pass your questions and the insured's answers directly into the questionnaire field. The mapping is straightforward:

# Your application form submission (example structure)
application = {
    "company_name": "Acme AI Ltd",
    "company_website": "https://acme.ai",
    "contact_email": "cto@acme.ai",
    "answers": {
        "q1": "Yes, our claims model approves and denies claims automatically.",
        "q2": "Claims above £25,000 are reviewed by a human adjuster.",
        "q3": "We process medical records, financial data, and photographic evidence.",
        "q4": "We are in FCA dialogue regarding Consumer Duty obligations.",
        "q5": "We conduct quarterly model performance reviews but no third-party audits.",
    }
}
 
# Map to Exona questionnaire format
questions_text = {
    "q1": "Does your AI system make autonomous decisions without human review?",
    "q2": "What safeguards exist for automated decisions?",
    "q3": "What categories of personal data does your system process?",
    "q4": "Are you subject to any regulatory investigation or ongoing dialogue?",
    "q5": "What model governance and monitoring processes do you have in place?",
}
 
questionnaire = [
    {"question": questions_text[key], "answer": answer}
    for key, answer in application["answers"].items()
]

Step 2: Create the scan

import requests
 
API_KEY = "exo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
BASE_URL = "https://platform.exonalab.com/api/v1"
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}
 
response = requests.post(
    f"{BASE_URL}/scans",
    headers=HEADERS,
    json={
        "company_name": application["company_name"],
        "company_website": application["company_website"],
        "questionnaire": questionnaire,
    },
)
response.raise_for_status()
scan_id = response.json()["id"]
 
# Store the scan_id against this application in your database
# db.applications.update(application_id, {"exona_scan_id": scan_id})

Step 3: Poll for completion (or use a background job)

For synchronous workflows (e.g. a webhook response), poll directly:

import time
 
while True:
    r = requests.get(f"{BASE_URL}/scans/{scan_id}", headers=HEADERS)
    data = r.json()
 
    if data["status"] == "completed":
        scan_result = data["result"]
        break
    elif data["status"] == "failed":
        raise RuntimeError(data["error"]["message"])
 
    time.sleep(10)

For asynchronous workflows (recommended for production), enqueue the scan creation as a background task and store the scan_id. A separate worker polls until complete and then notifies the underwriter.


Step 4: Use the result

assessment = scan_result["risk_assessment"]
enrichment = scan_result["enrichment"]
incidents = scan_result["matched_incidents"]
 
# Route to appropriate underwriting queue
overall = assessment["overall_risk_level"]
if overall in ("Low", "Medium"):
    queue = "standard"
elif overall == "High":
    queue = "specialist_review"
else:
    queue = "senior_underwriter"
 
# Build underwriter briefing
briefing = {
    "scan_id": scan_id,
    "company": application["company_name"],
    "sector": enrichment["industry_sector"],
    "revenue_band": enrichment["revenue_band"],
    "overall_risk": overall,
    "key_risks": [
        f"{dim}: {assessment[dim]['label']}: {assessment[dim]['rationale']}"
        for dim in ["ai_intensity", "autonomy", "domain_risk", "control_governance"]
        if assessment[dim]["score"] >= 2
    ],
    "relevant_incidents": [
        f"{i['year']}: {i['risk_domain']}: {i['description'][:120]}..."
        for i in incidents
        if i["similarity_score"] >= 0.75
    ],
    "routing_queue": queue,
    "data_as_of": scan_result["data_freshness"]["sources_last_checked"],
}
 
print(briefing)

Complete end-to-end example

import requests
import time
 
API_KEY = "exo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
BASE_URL = "https://platform.exonalab.com/api/v1"
 
def process_application(company_name, company_website, application_qa):
    """
    Run an Exona scan for an incoming application with questionnaire data.
    Returns a structured underwriting briefing.
    """
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    }
 
    # Create scan
    r = requests.post(
        f"{BASE_URL}/scans",
        headers=headers,
        json={
            "company_name": company_name,
            "company_website": company_website,
            "questionnaire": application_qa,
        },
        timeout=15,
    )
    r.raise_for_status()
    scan_id = r.json()["id"]
 
    # Poll
    while True:
        r = requests.get(f"{BASE_URL}/scans/{scan_id}", headers=headers, timeout=15)
        r.raise_for_status()
        data = r.json()
        if data["status"] == "completed":
            return scan_id, data["result"]
        elif data["status"] == "failed":
            raise RuntimeError(data["error"]["message"])
        time.sleep(int(r.headers.get("Retry-After", 10)))
 
 
scan_id, result = process_application(
    company_name="Acme AI Ltd",
    company_website="https://acme.ai",
    application_qa=[
        {
            "question": "Does your AI make decisions without human review?",
            "answer": "Yes, claims under £10,000 are automated."
        },
        {
            "question": "What data does your system process?",
            "answer": "Medical records, photos, and financial transaction history."
        },
    ],
)
 
print(f"Scan: {scan_id}")
print(f"Risk Level: {result['risk_assessment']['overall_risk_level']}")
print(f"Matched Incidents: {len(result['matched_incidents'])}")

On this page