Authentication
Every request must include one of:
- X-API-Key header (used by the generator page itself):
X-API-Key: DF@2026:DF@2026 - HTTP Basic Auth (for 3rd-party integrations / Postman / curl
-u):curl -u DF@2026:DF@2026 ...
The key is defined in app/config/api_key.php (default shown above). Change this before any production deployment.
To rotate the key
- Copy
app/config/api_key.local.php.exampletoapp/config/api_key.local.phpand set a long random value — or set theTRUPATH_API_KEYenvironment variable, which takes priority over both files. - Update the
API_KEYconstant inindex.php'scallServerApi()function to the same value, since that page sends it as a literal (it has no server-side templating). - Restart/redeploy. Requests using the old key get
401 Unauthorized.
POST /api/generate-report
Headers: Content-Type: application/json, X-API-Key: <key>
.php extension is never required on this or any other route in the app (/index, /report, /dashboard/login, etc.) — a root-level .htaccess rewrite (not a redirect) strips it internally, so POST bodies, query strings, and auth headers all pass through unchanged. The .php form (/api/generate-report.php) still works too, if you already have it hardcoded somewhere.Request body
{
"mode": "initial_pcr",
"case": { "...": "see shape below" }
}
| Field | Description |
|---|---|
mode |
"pcr_only" — definitive PCR-only report. Always rendered with MAST forced off regardless of any MAST order on the case (no "results pending" header). Organism/MAST validation is relaxed since MAST isn't being reported at all."initial_pcr" — preliminary PCR / MAST-pending report. Requires case.requisition.mastOrder to be something other than "NOT_ORDERED"; if a MAST-eligible bacterial organism is present (clinically significant, category BACTERIA, participates true, and has a standard MAST plate in Evolve Antibiotic Plate Types(3)), the header shows "PCR RESULTS ONLY / MAST RESULTS PENDING"."final_pcr_mast" — the final report. Requires case.requisition.mastOrder to be something other than "NOT_ORDERED", requires at least one MAST-eligible bacterial organism as described above, and requires at least one S/R result in case.mastResults for every such organism.
|
case |
The same shape tpl9's own readCase() produces client-side: schemaVersion, requisition (mastOrder, billing/ICD-10 fields, etc.), patient, order, organisms (up to 12, each {organismId, load, source} where source is "PCR" or "MAST_ONLY", defaulting to "PCR"), detectedGenes (gene acronyms or ids), empiric ({drugId|genericName, displayName, instructions}), allergies (free-text strings), clinical ({pregnancy, renalIssue, hepaticIssue}), mastResults ({organismId: {drugId: "S"|"R"|"-"}}).See reference_source/sample_imports/Sample_Complete_PCR_Case.json and Sample_Complete_PCR_MAST_Case.json for complete real examples, and app/src/CaseInput.php for the exact validation rules. |
Response — success (200)
{
"success": true,
"message": "Report generated successfully",
"mode": "final_pcr_mast",
"renderer": "chrome",
"reportId": "20260725085315_27443829",
"pdfUrl": "{{BASE_URL}}/uploads/pdf/20260725085315_27443829.pdf",
"htmlUrl": "{{BASE_URL}}/uploads/html/20260725085315_27443829.html",
"getApi": "{{BASE_URL}}/api/get-report?reportId=20260725085315_27443829"
}
renderer is which engine actually produced the PDF: "chrome" (the primary path - headless Chrome printing the same self-contained HTML the in-browser preview renders, so the PDF matches it exactly) or "dompdf-fallback" (used automatically, only if Chrome can't run on this host at all - a simpler plain HTML/CSS layout, not pixel-matched to the preview, so a request never hard-fails just because Chrome is unavailable).
Response — validation error (422)
{ "success": false, "message": "A final PCR + MAST report requires a MAST order." }
Response — PDF generation failure (500)
Only happens if both renderers fail:
{ "success": false, "message": "PDF generation failed on both renderers.\nChrome: ...\nDompdf fallback also failed." }
Response — unauthorized (401)
{ "success": false, "message": "Unauthorized" }
GET /api/get-report?reportId=<id>
Headers: X-API-Key: <key>
Returns the same case payload that was submitted plus the pdfUrl, or 404 if the id doesn't exist. Report ids are validated against ^\d{14}_[0-9a-f]{8}$ before touching the filesystem, so this endpoint can't be used for path traversal.
curl examples
# Final PCR + MAST report (wrap the sample file's contents in
# {"mode":"final_pcr_mast","case": ...} first)
curl -X POST {{BASE_URL}}/api/generate-report \
-H "Content-Type: application/json" \
-H "X-API-Key: DF@2026:DF@2026" \
--data @final_pcr_mast_payload.json
# Retrieve it again later
curl "{{BASE_URL}}/api/get-report?reportId=20260725085315_27443829" \
-H "X-API-Key: DF@2026:DF@2026"
Postman collection
TruPath_API.postman_collection.json includes all three mode values (PCR Report Only, Preliminary PCR / MAST Pending, Final PCR + MAST) plus Get Report and an unauthorized-request check. Import it into Postman, then set the collection variables:
| Variable | Default | Set to |
|---|---|---|
baseUrl | {{BASE_URL}} | Your actual host, no trailing slash - use http://127.0.0.1/TruPath/tpl9 for local testing instead |
apiKey | DF@2026:DF@2026 | Whatever app/config/api_key.php resolves to on that host |
Run either "Generate..." request first — its test script captures the returned reportId into the reportId collection variable automatically, so "Get Report" works immediately afterward with no manual copy/paste.
What the API does NOT do
- It does not change
app/src/ClinicalEngine.php's rules based on where the request came from — the generator page's client-side preview and this API run the same logic, just against two independently-maintained copies of the reference data (the page's embedded snapshot vs. the MySQL database). Keep them in sync by re-runningtools/import_data_library.phpwheneverreference_source/TruPath_Generator_Data_Library_v3_1_2.jsonchanges, and re-embedding that same JSON intoindex.phpif the client sends an updated data library package. - It does not manage reference data (pathogens/genes/antimicrobials/recommendations) — use the admin dashboard for that.