Score a patient's screening answers, let a doctor review and correct a result, generate a plain-language explanation, and answer follow-up questions about it in a chat.
Service is running| Format | JSON in, JSON out. Set Content-Type: application/json on every POST/PATCH. |
|---|---|
| Who's asking | Every request should include one header: X-User-Role, set to either patient or doctor. |
patient or doctor and the API adjusts what it gives back automatically.| Level | Meaning |
|---|---|
| Less Likely | Screening did not find meaningful signs of this condition. |
| Likely | Some signs were found — worth a closer look. |
| Most Likely | Strong signs were found — recommend follow-up. |
Call this once the patient's (or the doctor's) answers are ready to submit. You don't need to send every possible answer — anything left out is treated as normal.
POST /api/v1/rule-engine/evaluate Headers: Content-Type: application/json, X-User-Role: doctor
{
"age": 8,
"fields": {
"overjet_mm": 7,
"crossbite_assessment": "Anterior",
"mouth_breathing_assessment": "Frequently"
}
}
{
"id": "res_036afe8bbc3948b9952f2a01b29ba136",
"age": 8,
"visibility": "doctor",
"diagnoses": [
{
"diagnosis_key": "tonsils_and_adenoids",
"display_name": "Tonsils and Adenoids",
"category": "Most Likely",
"score": 7.0,
"edited": false,
"items": [
{ "label": "Does your child snore during sleep?", "value": "Frequently", "signal_type": "Confirmatory" }
]
}
]
}
If the header said patient instead, each entry in "diagnoses" would only contain display_name, category, and edited — no score, no items.
GET /api/v1/rule-engine/results/{id}
Headers: X-User-Role: patient (or doctor)
Same response shape as scoring — use this to re-show a result on page load without re-submitting the form.
Only works with X-User-Role: doctor — anything else is rejected.
PATCH /api/v1/rule-engine/results/{id}/corrections
Headers: Content-Type: application/json, X-User-Role: doctor
{
"diagnosis_key": "tongue_tie",
"category": "Likely",
"edited_by": "dr_jane_doe"
}
The updated value is what everyone sees afterwards — including patients — the next time you fetch this result.
No body needed — just the result's id in the address. This can take up to about 40 seconds, so show a loading state.
POST /api/v1/rule-engine/results/{id}/explanation
{
"overview": "After reviewing the screening results...",
"final": "After reviewing the screening results, it appears that several areas could benefit from a closer look...",
"per_diagnosis": [
{ "name": "Tonsils and Adenoids", "category": "Most Likely", "explanation": "This was flagged because..." }
]
}
Show "final" as the main explanation. Once generated, re-fetch it any time without waiting again: GET /api/v1/rule-engine/results/{id}/explanation
No body needed — just the result's id in the address. This looks at whichever conditions came back Likely or Most Likely and suggests next steps from a fixed, clinic-approved list for that age group. Does not require an explanation to exist first — it only needs a scored result. If nothing was flagged, it returns an empty list instantly.
POST /api/v1/rule-engine/results/{id}/treatment-recommendation
{
"recommendations": [
{
"variable_name": "tongue_tie_release",
"name": "Tongue Tie release",
"description": "A minor procedure (frenotomy or frenectomy) that releases a restrictive band of tissue under the tongue...",
"justification": "The patient has multiple indicators of tongue tie, including speech therapy history and delayed language development..."
}
]
}
Show each recommendation's "name" and "justification" together. Once generated, re-fetch it any time: GET /api/v1/rule-engine/results/{id}/treatment-recommendation
Lets a user ask follow-up questions about an explanation. Generate the explanation first — chatting before that returns an error telling you to do so.
POST /api/v1/chat/init
{ "result_id": "res_036afe...", "question": "Why is this flagged?" }
{
"chat_id": "chat_a1addf9ee40347f2b980020fd6fce456",
"current_turn": { "question": "Why is this flagged?", "answer": "..." },
"history": [ { "question": "...", "answer": "..." } ]
}
POST /api/v1/chat/{chat_id}/messages
{ "question": "Is this urgent?" }
GET /api/v1/chat/{chat_id}
Call "Start a chat" again with the same result_id — that begins a brand-new conversation. You can optionally delete the old one: DELETE /api/v1/chat/{chat_id}
Every error looks like this:
{ "error": { "code": "not_found", "message": "..." } }
| error.code | What it means | What to do |
|---|---|---|
forbidden | This action needs the doctor role. | Only show the action to doctor users. |
not_found | The id you sent doesn't exist. | Double check the id you saved earlier. |
conflict | You skipped a step (e.g. chatting before an explanation exists). | Do the earlier step first — the message says which. |
llm_unavailable | The AI explanation/chat service is temporarily down. | Show a "try again" message; retry shortly. |
validation_error | Something in your request was missing or the wrong type. | Check the field list for that request. |