Loading documentation

Access required

Your account doesn't have access to the documentation. Contact your administrator.

Sign out
Skip to main content

Reflection Questions

Status: stable Category: trinityassist Added: 2026-05-11 Last reviewed: 2026-07-01

Summary

Generate 3 to 10 typed reflection questions tailored to a clinical case -- the longitudinal episode of care across one or more encounters. Every question carries a required kind (yes_no, numeric_scale, date, single_select, multi_select, or short_text) that tells the consumer how to render its input and validate its answer. The engine returns a relevant mix, not a flat list of free-text prompts.

Use this when the consumer's case has reached a natural close point -- "summarize what worked", "what would you do differently", "what stayed uncertain" -- and the answers are about the case's full arc, not the last visit alone. The signed answers are forwarded to /api/case_reflection/ingest.

Endpoint

FieldValue
MethodPOST
Path/api/case_reflection/questions
Full URLhttps://engine.tripod-health.com/api/case_reflection/questions
AuthenticationAPI key (required)
Request bodyapplication/json
Response bodyapplication/json
IdempotentNo (each call may return a new question mix)
Rate-limitedYes

Generated text passes the engine's outbound de-identification scan before returning; identifier-shaped output fails the call with 502 UpstreamError rather than scrubbing.

Authentication

Standard API-key. See authentication.md.

Request

Headers

HeaderRequiredNotes
apikeyYesYour engine API key.
Content-TypeYesapplication/json.
X-Request-IDNoEchoed back; lets you correlate logs.

Body

{
"case_meta": {
"case_id": "cas-xyz789",
"case_type": "work_injury",
"case_subtype": null,
"encounters_count": 4
}
}
FieldTypeAlways presentDescription
case_metaobjectYesWrapper.
case_meta.case_idstringYesConsumer-supplied opaque correlation token (1-64 chars). The engine treats it as opaque.
case_meta.case_typeenumYesOne of non_injury_consult, work_injury, non_work_injury, prevention.
case_meta.case_subtypestring | nullNoOptional free-form subtype (max 64 chars). Only meaningful when case_type='prevention'.
case_meta.encounters_countintegerYesNumber of encounters under the case at call time. Allowed 0..1000. Frames "across N visits" language.

The body MUST NOT contain identifier-shaped values. Inbound de-identification is enforced on every request and rejects identifier-shaped payloads with 400 DeIdentificationViolation. See conventions.md.

Response

Success -- 200 OK

{
"questions": [
{
"question_id": "outcome_matched_expectation",
"prompt_text": "Did the clinical outcome across the case match what you expected when the injury first presented?",
"hint": "Consider the case as a whole, not any single visit.",
"kind": "yes_no"
},
{
"question_id": "confidence_in_care_plan",
"prompt_text": "How confident are you that the care plan you pursued was the right call for this case?",
"kind": "numeric_scale",
"scale": {
"min": 0,
"max": 10,
"min_label": "not at all confident",
"max_label": "completely confident"
}
},
{
"question_id": "recovery_trajectory_clarity",
"prompt_text": "How clear was the recovery trajectory to you across the encounters?",
"kind": "numeric_scale",
"scale": {
"min": 0,
"max": 10,
"min_label": "very unclear",
"max_label": "very clear"
}
},
{
"question_id": "primary_recovery_barrier",
"prompt_text": "What was the primary barrier to recovery on this case?",
"kind": "single_select",
"options": [
{ "value": "persistent_pain", "label": "Persistent pain" },
{ "value": "psychosocial", "label": "Psychosocial factors" },
{ "value": "access_to_care", "label": "Access to care" },
{ "value": "none", "label": "No significant barrier" }
]
},
{
"question_id": "case_summary_what_worked",
"prompt_text": "Anything else worth capturing about what worked on this case?",
"kind": "short_text"
}
],
"request_id": "req-xyz",
"timestamp": "2026-05-11T14:23:00.000Z"
}

The example above is a real production response for a work_injury case spanning four encounters: a yes_no judgment, two numeric_scale questions (the analytical backbone), a single_select, and one short_text. The exact mix and the question_id values vary with the metadata you submit and are regenerated on every call -- persist whatever the engine returned and pair it verbatim with the clinician's answers on the ingest call.

Response fields

FieldTypeAlways presentDescription
questionsarrayYesBetween 3 and 10 entries.
questions[].question_idstringYesStable opaque key (lowercase snake_case, max 64 chars). Correlates the answer the clinician provides.
questions[].prompt_textstringYesThe question text the clinician answers. Max 2000 chars.
questions[].hintstringNoOptional supplemental guidance the UI may render under the prompt. Max 500 chars; absent when the engine supplied none.
questions[].kindenumYesOne of yes_no, numeric_scale, date, single_select, multi_select, short_text. Drives input rendering and answer validation.
questions[].scaleobjectNoPresent only on numeric_scale questions. Absent on every other kind. See scale.
questions[].optionsarrayNoPresent only on single_select and multi_select questions. Absent on every other kind. See options.

scale

Present only when kind is numeric_scale. The range is always the fixed 0-10 integer scale; the engine authors only the two end-anchor labels in the case's language.

FieldTypeAlways presentDescription
scale.minintegerYesAlways 0. The low end of the scale.
scale.maxintegerYesAlways 10. The high end of the scale.
scale.min_labelstringYesHuman-readable label for 0 (for example "not at all confident"). Max 120 chars.
scale.max_labelstringYesHuman-readable label for 10 (for example "completely confident"). Max 120 chars.

options

Present only when kind is single_select or multi_select. Always at least two entries (2-6), each a value/label pair. The clinician's answer references option values, never labels.

FieldTypeAlways presentDescription
options[].valuestringYesStable machine token (lowercase snake_case, max 64 chars). This is what the answer carries.
options[].labelstringYesHuman display text (max 200 chars).

The typed question mix

The engine returns a relevant mix rather than a fixed set. The yes_no and numeric_scale kinds are the always-present backbone; the remaining kinds appear only when the engine judges them relevant to the specific case.

KindAnswer value typeHow manyNotes
yes_noboolean1-3 (always ≥ 1)A binary judgment. Carries no scale and no options.
numeric_scaleinteger 0..102-3 (always ≥ 2)The analytical backbone. Fixed 0-10 scale; carries scale anchor labels.
date"YYYY-MM-DD" string0-1A single calendar date (for example an expected return-to-duty milestone). Included only when relevant.
single_selectone option value string0-1Pick exactly one. Carries options (2-6). Included only when relevant.
multi_selectarray of option value strings0-1Select all that apply. Carries options (2-6). Included only when relevant.
short_textstring0-1The one optional free-narrative question. Carries no scale/options.

The total is always between 3 and 10 questions. Per-kind counts are guidance the engine follows, not a caller input, and are not returned in the response.

Status codes

CodeMeaningWhen
200OK3-10 typed questions returned.
400ValidationErrorBody missing case_meta, an enum is unknown, encounters_count is out of range, or an unknown field is present.
400DeIdentificationViolationBody carries an identifier-shaped value.
401UnauthenticatedMissing or invalid API key.
413PayloadTooLargeBody exceeds 512 KB.
415UnsupportedMediaTypeContent-Type is not application/json.
429RateLimitedPer-consumer rate limit exceeded.
500InternalErrorUnexpected failure.
502UpstreamErrorUpstream model call failed, or generated text failed the outbound de-id pass.
503ServiceUnavailableService temporarily unhealthy.
504UpstreamTimeoutUpstream model call exceeded the engine's question-generation budget.

All non-2xx responses use the uniform error envelope; see errors.md.

Examples

curl

curl -sS -X POST "https://engine.tripod-health.com/api/case_reflection/questions" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"case_meta": {
"case_id": "cas-xyz789",
"case_type": "work_injury",
"case_subtype": null,
"encounters_count": 4
}
}'

Python (requests)

# Uses the call() helper from quickstart.md.
result = call("POST", "/api/case_reflection/questions", {
"case_meta": {
"case_id": "cas-xyz789",
"case_type": "work_injury",
"case_subtype": None,
"encounters_count": 4,
},
})
for q in result["questions"]:
print(q["kind"], q["question_id"], "->", q["prompt_text"])

Node.js (native fetch)

// Uses the call() helper from quickstart.md.
const result = await call("POST", "/api/case_reflection/questions", {
case_meta: {
case_id: "cas-xyz789",
case_type: "work_injury",
case_subtype: null,
encounters_count: 4,
},
});
for (const q of result.questions) {
console.log(q.kind, q.question_id, "->", q.prompt_text);
}

Intended use

  • Case-close reflection capture once all encounters under the case are finalized.
  • Dispatch the input widget on each question's kind -- a yes/no toggle, a 0-10 slider anchored by scale.min_label / scale.max_label, a date picker, a single- or multi-select from options, or a free-text box -- and collect a typed answer per question.
  • Case-level retrospective when revising the case-handling process; the typed answers make the captured reflections comparable across cases.

Mistaken use

  • Rendering every question as a free-text box. The kind is required for a reason -- dispatch the input on it so the answer value matches the type the ingest endpoint validates.
  • Inventing a scale range other than 0-10, or reading scale.min / scale.max as anything but 0 and 10. The scale is always fixed; only the anchor labels vary.
  • Treating questions as a clinical assessment. The engine's clinical assessment surface is /api/cdri/compute, /api/phenotype/classify, and /api/acuity/compute.
  • Sending raw patient identifiers in case_id. Use an opaque consumer- side surrogate.

De-identification contract

This endpoint enforces both directions of the engine's de-id contract:

  • Inbound: the request body is walked for identifier-shaped strings and blocked field names. Failures return 400 DeIdentificationViolation.
  • Outbound: generated question text, hints, option labels, and anchor labels are scanned for identifier shapes before return. A match fails the call with 502 UpstreamError; the engine never silently scrubs.

See conventions.md.

Rate limits

Per-consumer limits are managed at the gateway. See rate-limits.md.

Change history

DateChange
2026-05-11Initial publication.
2026-07-01Renamed from /api/cases/outcome/questions to /api/case_reflection/questions; questions are now typed (kind per question, fixed 0-10 numeric_scale, single_select/multi_select options) and return a 3-10 mix instead of a flat 5-7 free-text set.