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:
districtIddistrictNamequarteryearuserIduserNameuserEmailuserRole
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-iddata-sdac-user-iddata-sdac-user-namedata-sdac-user-emaildata-sdac-user-roledata-sdac-district-namedata-sdac-quarterdata-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 Path | Purpose |
|---|---|
GET /api/config | Returns runtime widget config such as the default agent ID |
POST /api/mastra/sdac/upload | Uploads a workbook through Mastra, which submits the file to the ingestion worker internally |
GET /api/mastra/sdac/uploads/{jobId}/status | Polls Mastra upload job state |
GET /api/mastra/sdac/report-analysis/{reportId}/status | Polls Mastra report analysis state |
/api/ingestion/sdac/ingest | Registers pre-normalized SDAC roster payloads |
/api/ingestion/sdac/sessions | Creates or resumes a district-scoped session |
/api/ingestion/sdac/sessions/{session_id} | Validates and refreshes an existing session |
/api/ingestion/sdac/sync | Resolves or refreshes the latest report for a district |
/api/ingestion/sdac/chat | Starts or continues streaming chat |
/api/ingestion/sdac/feedback | Submits assistant feedback |
/api/ingestion/sdac/validate | Returns a report issue list |
/api/ingestion/sdac/report/{report_id} | Returns report header metadata from the review service |
/api/ingestion/sdac/costs | Lists 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
sessionIdis stored in browsersessionStoragefor the tab lifetime.conversationIdis stored per report insessionStorage.- 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_idduring 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.
Related Pages
Direct gateway auth versus widget same-origin access
Create and validate widget sessions
Upload workbooks through the widget or ingestion service
Check ingestion-side report status by report ID
Register pre-parsed roster data directly as JSON
Look up district cost records through the widget proxy
Resolve or refresh the latest report for a district