Skip to main content

Widget Integration

Overview

The SDAC widget is a separate deployable frontend that can be embedded into a host application such as TherapyLog. The browser talks only to the widget origin. The widget server proxies session, sync, chat, validation, feedback, and cost traffic to the ingestion service, and proxies workbook upload/status traffic to Mastra.

Authentication Boundary

Protected widget actions require a widget access token. The host application server obtains an OAuth access token with the client-credentials flow and passes it to the widget bootstrap as widget_token or data-widget-token. The widget shell and static assets are served without JWT validation; protected same-origin /widget/api/... calls validate the token at request time.

The browser does not send subscription keys to the widget's /api/ingestion/... routes. The widget server proxies those requests upstream to the ingestion service and attaches the required upstream credentials server-side.

If you are bypassing the widget and calling /ai/sdac/... directly, use the gateway authentication flow documented in Authentication.

Integration Model

Host Page Context

The embed script reads context from the host page and passes it to the iframe as query parameters.

Supported fields:

  • districtId
  • districtName
  • quarter
  • year
  • userId
  • userName
  • userEmail
  • userRole

Preferred Host Markup

Use a consolidated JSON payload:

<div
id="sdac-meta-data"
data-sdac='{
"districtId": "12345",
"districtName": "Maplewood Richmond Heights",
"quarter": "Q1",
"year": "2026",
"userId": "auditor-001",
"userName": "Alex Auditor",
"userEmail": "auditor@state.gov",
"userRole": "State Auditor"
}'>
</div>
<script
src="{WIDGET_BASE_URL}/widget/embed.js"
data-widget-token="{WIDGET_TOKEN}">
</script>

Do not put district, quarter, year, or user fields in the token. The embed script reads that context from the host page metadata and passes it internally to the cross-origin iframe.

Legacy Host Attributes

The embed script also supports legacy data-sdac-* attributes such as:

  • data-sdac-district-id
  • data-sdac-user-id
  • data-sdac-user-name
  • data-sdac-user-email
  • data-sdac-user-role
  • data-sdac-district-name
  • data-sdac-quarter
  • data-sdac-year

Widget-Side API Paths

The browser calls these same-origin widget endpoints. When served from the hosted widget base URL, these paths are under the /widget base path. The widget shell/static/bootstrap routes are public; protected data/action calls under /widget/api/... require the widget bearer token at request time.

The source-backed widget proxy OpenAPI artifact is docs/openapi/widget-api.json; it owns the Replit/browser route contract for /api/ingestion/* and /api/mastra/*.

Widget PathPurpose
GET /api/configReturns runtime widget config such as the default agent ID
POST /api/mastra/sdac/uploadUploads a workbook through Mastra, which submits the file to the ingestion worker internally
GET /api/mastra/sdac/uploads/{jobId}/statusPolls Mastra upload job state
GET /api/mastra/sdac/report-analysis/{reportId}/statusPolls Mastra report analysis state
/api/ingestion/sdac/ingestRegisters pre-normalized SDAC roster payloads
/api/ingestion/sdac/sessionsCreates or resumes a district-scoped session
/api/ingestion/sdac/sessions/{session_id}Validates and refreshes an existing session
/api/ingestion/sdac/syncResolves or refreshes the latest report for a district
/api/ingestion/sdac/chatStarts or continues streaming chat
/api/ingestion/sdac/feedbackSubmits assistant feedback
/api/ingestion/sdac/validateReturns a report issue list
/api/ingestion/sdac/report/{report_id}Returns report header metadata from the review service
/api/ingestion/sdac/costsLists available district cost records
/api/ingestion/sdac/costs/{cost_id}Returns a single district cost record

The widget proxy maps /widget/api/ingestion/sdac/* browser calls to the ingestion service's /sdac/* upstream routes. The widget proxy maps /widget/api/mastra/sdac/* browser calls to Mastra SDAC runtime routes. Browser code should not call the ingestion worker directly for workbook uploads.

Session Behavior

  • sessionId is stored in browser sessionStorage for the tab lifetime.
  • conversationId is stored per report in sessionStorage.
  • Uploaded workbook report IDs are restored only when they match the current host page context. Legacy or mismatched browser storage is ignored so the widget does not silently reuse a report from another page or reporting period.
  • The widget starts with district context from the host page and can resolve a report_id during session creation.
  • If the host page does not provide district context, or provides a district without a complete quarter and year, the widget blocks report-aware actions until the user uploads a workbook or reopens the widget from a page with complete context.
  • If the requested period is unavailable but another period exists, the widget shows the available fallback period and requires explicit user confirmation before loading it.

See Session Bootstrap API for the ingestion-side session contract.