Skip to main content

Quick Start

Direct Gateway Flow

This is the shortest end-to-end path for a server-to-server integration that calls APIM directly for the routes that APIM is confirmed to expose.

1. Acquire a Token

TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&scope={SCOPE}&grant_type=client_credentials" \
| jq -r '.access_token')

BASE_URL="https://dev-sdac-aitools-wkhc-apim.azure-api.net"
SDAC_API_PREFIX="${SDAC_API_PREFIX:-/sdac}" # set to /ai/sdac only when APIM publishes that alias

2. Upload a Workbook

UPLOAD_URL="$BASE_URL$SDAC_API_PREFIX/upload"

REPORT_ID=$(curl -s -X POST "$UPLOAD_URL" \
-H "Authorization: Bearer $TOKEN" \
-H "Ocp-Apim-Subscription-Key: $SUBSCRIPTION_KEY" \
-F "file=@./Q1-2026-district-costs.xlsx" \
-F "user_email=auditor@state.gov" \
-F "user_name=Alex Auditor" \
-F "district=Maplewood Richmond Heights" \
| jq -r '.report_id')

The source-backed Mastra upload route is /sdac/upload. If your APIM product publishes the deployment-specific /ai/sdac/upload alias, set SDAC_API_PREFIX=/ai/sdac. Do not point browser code at the ingestion worker directly.

3. Validate the Uploaded Report

curl -X POST "$BASE_URL$SDAC_API_PREFIX/validate" \
-H "Authorization: Bearer $TOKEN" \
-H "Ocp-Apim-Subscription-Key: $SUBSCRIPTION_KEY" \
-H "Content-Type: application/json" \
-d "{
\"reportId\": \"$REPORT_ID\",
\"forceRefresh\": false
}"

As with upload, set SDAC_API_PREFIX=/ai/sdac only when the target APIM environment publishes that alias. The source-backed Mastra route is POST /sdac/validate; browser/widget callers should use the widget same-origin proxy route instead.

Widget-First Flow

Use this path when the browser should stay on the widget origin and let the widget server proxy to Mastra. Mastra then calls the ingestion worker service-to-service.

1. Acquire a Widget Access Token

The host application server should request the widget access token with the OAuth client-credentials flow. Do not mint this token in browser JavaScript.

WIDGET_TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
--data-urlencode "client_id={CLIENT_ID}" \
--data-urlencode "client_secret={CLIENT_SECRET}" \
--data-urlencode "scope={SCOPE}" \
| jq -r '.access_token')

The token must be issued for the configured widget/API audience and include the required access_as_application app role. The widget uses this token to authorize protected widget API calls; district, quarter, year, and user context still come from host page metadata.

2. Render District Context Metadata

<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>

3. Load the Widget Script

Render the widget script from the SDAC widget base URL for your environment and pass the access token to the widget bootstrap. A normal browser <script> tag cannot send custom Authorization headers, so the token is passed to the iframe and then sent as a bearer token on protected widget API calls.

<script
src="{WIDGET_BASE_URL}/widget/embed.js"
data-widget-token="{WIDGET_TOKEN}">
</script>

4. Let the Widget Bootstrap Session Context

The widget calls POST /api/ingestion/sdac/sessions, resolves the latest available report_id for the district when possible, and then uses the returned session_id plus any resolved report context for follow-up validation or chat.

For validation and chat, the widget uses POST /api/ingestion/sdac/validate and POST /api/ingestion/sdac/chat from the widget origin.

For workbook uploads, the widget calls POST /api/mastra/sdac/upload, then polls /api/mastra/sdac/uploads/{jobId}/status and /api/mastra/sdac/report-analysis/{reportId}/status.