NEONVIL & Confidential Insurance Wholesaler
A Case Study: An Invite-Only Underwriting Portal with an AI-Assisted Review Console

Business Outcome
A production underwriting platform with two faces — an invite-only applicant wizard and an internal review console with AI-assisted risk summaries — delivered on one Postgres, two authentication planes, and a keyless deployment pipeline.
Two Audiences, One Platform
An applicant portal for invite-only guests and an underwriter console for internal staff, sharing one database and one schema pipeline — no duplicated product logic.
Underwriters Own the Forms
Schema-driven questionnaires mean new product lines, new steps, and new financial-table columns ship as configuration — no frontend deploy required.
Tunable AI, Audited
Risk summaries run through per-agent prompt and model configs the review team can edit live, with every call tracked against a token-usage audit row.
Context & Challenges
A property-and-casualty insurance wholesaler needed to replace a manual underwriting intake with a self-serve applicant portal plus an internal review console. Same product, two distinct user populations — invite-only guests and internal underwriters — three parallel underwriting product lines running through one shell, and AI-assisted risk summaries the reviewers can tune without a deploy.
Four critical engineering challenges:
01
Idempotent cross-service invites: approving an applicant chains an identity-provider invite, a portal-group add, and a database status update — a 409 on re-run looks up the existing user and continues, a 403 still persists status so the database never drifts past the IdP.
02
Two identity planes in one repo: the applicant API validates JWTs with JWKS rotation and 5-second clock-skew tolerance; the admin API trusts a platform-injected principal plus a double-submit CSRF cookie with SameSite=Strict — one IdP, two trust models, no shared bearer tokens.
03
Schema-driven forms where the underwriting team owns the UI: a custom columns uiSchema extension reorders financial-table columns server-side, and step-scoped renderers handle comma-grouped currency — new product lines ship as configuration, not a frontend deploy.
04
Direct-to-blob uploads with live progress: fetch has no upload-progress API, so a SAS-URL handshake followed by a raw XMLHttpRequest PUT with per-row progress listeners carries 100 MB files without ever touching the Function.
Project Goals
Applicant Portal
A schema-driven multi-step application wizard with direct-to-blob uploads, pre-population from prior applications, and invite-only guest authentication.
Underwriter Console
An internal review UI with grouped application views, reviewer comment threads, AI risk summaries, and per-agent prompt and model configuration.
Dual-Plane Backend
Two Azure Functions apps on one Postgres — one JWT-gated for applicants, one CSRF-hardened for underwriters — with identity-provider invites, transactional email, and Azure OpenAI wired end-to-end.
Our Solution
Three layers, one Postgres. The applicant portal carries the guest-facing wizard. The underwriter console drives triage, review, and AI-assisted decisioning. The dual-plane backend mediates both — with two separate authentication models over one system of record.
Applicant Portal
Schema-driven wizard with direct-to-blob uploads
Four-phase stepper (Start → Questionnaire → Uploads → Status) with URL-synced application ID for reload-safe resume
JsonForms questionnaire with a custom columns uiSchema extension, step-scoped renderers, and comma-grouped currency in array tables
Direct-to-Azure-Blob uploads via SAS handshake + raw XMLHttpRequest PUT — 100 MB files with per-row live progress
Pre-population from prior applications with a dismissible banner showing exactly which fields were carried over
Underwriter Console
Triage, grouped applications, AI-assisted summaries
Onboarding triage inbox with a one-click Approve that invites the guest into the IdP and adds them to the portal group
Grouped-by-user applications with reviewer comment logs and eight distinct application states driving status transitions
AI risk summaries with per-agent system prompt, user prompt template, model, temperature, and token-usage audit
PDF export of the full application record for offline review and archival
Dual-Plane Backend
Two auth planes, one system of record
Applicant-facing Azure Functions: JWT verification with JWKS rotation and 5-second clock-skew tolerance
Admin-facing Azure Functions: platform-injected principal plus double-submit CSRF cookie with SameSite=Strict
Idempotent cross-service invites: a Graph 409 resolves via mail-filter lookup; a 403 still persists status so the DB never drifts past the IdP
One Postgres with JSONB questionnaire payloads, Prisma migrations gated in CI, keyless deploys via GitHub OIDC federation
Effort Allocation
Applicant Portal UX (22%)
Underwriter Console UX (20%)
Backend Business Logic (18%)
Security, Auth & Data Modelling (16%)
Integrations (14%)
Infrastructure & CI/CD (10%)
Infrastructure & Technologies
Next.js + Static Export
Chosen because Entra ID popup auth needs a stable client origin — static export on Azure Static Web Apps removes the SSR runtime and a long list of MSAL redirect race conditions in one move.
JsonForms + Ajv
Chosen so the underwriting team owns the questionnaires — schema-driven rendering ships new product lines as config, and the same Ajv schemas validate on the client and in Azure Functions.
MSAL + Microsoft Entra ID
Chosen because the client mandated Entra ID for invite-only applicant access — sessionStorage caching, a dedicated auth-redirect page, and auth-code-aware storage flushing were all required to survive popup login on static hosting.
Azure Functions v4 (Gen 2)
Chosen for scale-to-zero on two low-to-moderate-traffic surfaces, with first-party bindings for Blob Storage, Communication Services, and Entra ID — one platform covers the applicant API and the admin API as separate function apps.
PostgreSQL + Prisma
Chosen for native JSONB on dynamic questionnaire payloads without going NoSQL, plus relational integrity between applications, schemas, uploads, and AI notes — and a singleton client that survives Function cold starts without exhausting connections.
Microsoft Graph API
Chosen as the single source of truth for guest invitations, directory lookups, and portal-group membership — avoids rolling a custom invite email plus account-provisioning flow on top of the IdP.
Azure OpenAI (agents in DB)
Chosen so reviewers can tune AI — system prompt, user prompt template, model, temperature, and max-tokens live in a Postgres agents table; every call records token usage against an audit row.
