Structured Payload Ingest API
Overview
This ingestion-side route accepts parsed SDAC roster data as JSON and runs it through the same registration and deduplication flow used for workbook uploads.
Direct ingestion path:
POST /sdac/ingest
Widget same-origin path:
POST /api/ingestion/sdac/ingest
Use this route when your integration already has normalized SDAC data and does not need to upload an Excel workbook.
Authentication and Access Model
This ingestion route does not perform APIM bearer-token or subscription-key validation itself.
- Browser clients should call the widget same-origin proxy at
/api/ingestion/sdac/ingest. - Direct
/sdac/ingestaccess should stay behind private networking, host-application controls, or reverse-proxy authentication. - Do not expose the ingestion route directly to untrusted browser clients.
Request
| Attribute | Value |
|---|---|
| Endpoint | POST /sdac/ingest |
| Content-Type | application/json |
| Response | JSON |
Request Body
{
"user_email": "auditor@state.gov",
"user_name": "Alex Auditor",
"district": "Maplewood Richmond Heights",
"metadata": {
"quarter": "Q1",
"year": 2026
},
"main_roster": [
{
"first_name": "Jane",
"last_name": "Doe",
"job_title": "Speech Pathologist",
"source_code": "1",
"function_code": "2100",
"gross_salary": 75000,
"fringe": 18750
}
],
"replacements": [],
"source_name": "district-export.json",
"force_reingest": false
}
| Field | Type | Required | Description |
|---|---|---|---|
user_email | string | Yes | Email of the submitting user |
user_name | string | Yes | Display name of the submitting user |
district | string | Yes | District or organization label stored with the report |
metadata | object | Yes | Report-level metadata such as quarter and year |
main_roster | array | Yes | Main personnel rows |
replacements | array | No | Replacement rows |
source_name | string | No | Source label stored for traceability |
force_reingest | boolean | No | Bypass duplicate-payload detection and create a new report |
Success Response
{
"status": "success",
"reportId": "8201EDC2-2EDE-4CA1-AF44-D0F5AA185CDB",
"message": "Successfully processed 21 records and archived to https://storage.example.com/sdac-archive/...",
"recordCount": 21,
"canReingest": false,
"existingReport": null
}
Duplicate Response
{
"status": "duplicate",
"reportId": "8201EDC2-2EDE-4CA1-AF44-D0F5AA185CDB",
"message": "This content has already been ingested. Using existing report.",
"recordCount": 0,
"canReingest": true,
"existingReport": {
"district": "Maplewood Richmond Heights",
"quarter": "Q1",
"year": 2026,
"processed_at": "2026-03-30T09:15:00.000Z"
}
}
Error Response
{
"status": "error",
"reportId": null,
"message": "Processing failed: ...",
"recordCount": 0,
"canReingest": false,
"existingReport": null
}
Validation failures caused by missing required fields return FastAPI validation errors with status 422.
Error Cases
| Status | Description |
|---|---|
| 200 | Report created successfully or duplicate payload resolved to an existing report |
| 422 | FastAPI validation failed because required JSON fields were missing or typed incorrectly |
| 500 | Payload processing failed after the request was accepted |
Side Effects
- The service hashes the submitted payload for duplicate detection before it writes any new report.
- Successful requests stage a JSON payload artifact, insert personnel rows into the ingestion tables, and archive the payload plus metadata.
- Duplicate payloads return the existing report reference and do not create a new report unless
force_reingestis set.
Operational Notes
- This route is ingestion-side only. It is not part of the gateway-facing
/ai/...API surface. - The service hashes the structured payload for duplicate detection in the same way workbook uploads hash files.
- Use Report Status API after ingest if your UI needs ingestion-state confirmation.