Loading documentation

Access required

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

Sign out
Skip to main content

Instrument questions

Status: stable Category: patterns Added: 2026-04-20 Last reviewed: 2026-04-21

Summary

The shared contract for every GET /api/:category/:resource/questions endpoint. Every instrument in the catalog exposes a /questions operation that returns the instrument's item list, the answer choices per item, and the set of items the paired /score operation requires.

Per-instrument docs (instruments-<resource>.md) list what is unique to that instrument: item count, scoring method, band cutoffs, worked example. They defer to this document for everything else.

Endpoint shape

FieldValue
MethodGET
Path template/api/:category/:resource/questions
AuthenticationAPI key (required)
Request bodynone
Response bodyapplication/json
IdempotentYes
Rate-limitedYes

:category and :resource must match a registered pair in the catalog. See GET /api/catalog/resources for the complete list of concrete URLs.

Purpose

  • Render a questionnaire UI. The items array carries the item IDs, prompt text, and the answer choices the UI must offer.
  • Validate input client-side before /score. Use required_items to confirm the user has answered every required item — if you send an incomplete body to /score, Trinity rejects with 400 ValidationError naming the missing ids. Better to catch that in the client.
  • Sync the instrument version your client renders against. Every response carries the rubric's version. Cache renders keyed on that version; refresh when it changes.

Authentication

Send a valid API key in the apikey header. See authentication.md.

Request

Headers

HeaderRequiredDescription
apikeyYesConsumer API key.
X-Request-IDNoOptional correlation ID. Echoed in the X-Request-ID response header.

Path parameters

ParameterTypeDescription
categorystringCatalog category (for example pain, function, psychosocial).
resourcestringCatalog resource inside that category (for example orebro, oswestry).

The (category, resource) pair must be registered in the catalog. An unknown pair returns 404.

Query / body parameters

None. GET ignores any body; query strings are not consulted.

Response

Success — 200 OK

{
"instrument_id": "pain_scale",
"version": "1.0.0",
"items": [
{
"id": "item_1",
"text": "Pain intensity right now (0 = no pain, 10 = worst imaginable)",
"answer_choices": [
{ "value": 0, "label": "0" },
{ "value": 1, "label": "1" },
{ "value": 2, "label": "2" },
{ "value": 3, "label": "3" },
{ "value": 4, "label": "4" },
{ "value": 5, "label": "5" },
{ "value": 6, "label": "6" },
{ "value": 7, "label": "7" },
{ "value": 8, "label": "8" },
{ "value": 9, "label": "9" },
{ "value": 10, "label": "10" }
]
}
],
"required_items": ["item_1"]
}

The X-Request-ID response header carries the correlation ID for this call. The response body itself does not echo a request_id; /questions is a pure lookup.

Response fields

FieldTypeAlways presentDescription
instrument_idstringYesCanonical resource name (matches the URL's :resource).
versionstring (SemVer)YesRubric version for this instrument. Bumps when items, answer choices, or required-items change.
itemsarrayYesOrdered list of items. Array position is not significant for scoring — items are keyed by their id. Rendering order is a UI choice.
items[].idstringYesItem identifier. Match when constructing the /score request body's responses map.
items[].textstringYesDisplay text for the item. Terse paraphrase today; replace with validated clinical wording before real-user-facing deployments.
items[].answer_choicesarrayYesAllowed answers. Send the value to /score; display label to the user. Ordering matters for UI presentation.
items[].answer_choices[].valueintegerYesThe numeric value to send in the /score body. Values are instrument-specific (for example 0–10 for NPRS, 0–3 for PHQ-9, 1–4 for TSK-11). See the per-instrument doc.
items[].answer_choices[].labelstringYesHuman-readable label for the choice. Render this to the user.
required_itemsarray of stringYesItem IDs that must be present in /score request bodies. Missing any of these causes 400 ValidationError on /score.

Status codes

CodeMeaningWhen
200OKQuestions returned.
401UnauthenticatedMissing or invalid API key.
404NotFound(category, resource) pair is not registered in the catalog.
429RateLimitedPer-consumer rate limit exceeded.
503ServiceUnavailableRubric for this instrument is not loaded. Transient; retry with backoff.

Examples

The language-specific invocations below assume the call() helper (or its equivalent) from quickstart.md. Per-instrument docs (for example instruments-orebro.md, instruments-depression_screen.md) carry their own worked-example pairs; this section shows the shared pattern using pain_scale as the smallest concrete instrument.

1. Canonical request and response

Send:

GET /api/pain/pain_scale/questions HTTP/1.1
Host: engine.tripod-health.com
apikey: $ENGINE_API_KEY
X-Request-ID: docs-pain_scale-questions-canonical

Receive (200 OK):

{
"instrument_id": "pain_scale",
"version": "1.0.0",
"items": [
{
"id": "item_1",
"text": "Pain intensity right now (0 = no pain, 10 = worst imaginable)",
"answer_choices": [
{ "value": 0, "label": "0" },
{ "value": 1, "label": "1" },
{ "value": 2, "label": "2" },
{ "value": 3, "label": "3" },
{ "value": 4, "label": "4" },
{ "value": 5, "label": "5" },
{ "value": 6, "label": "6" },
{ "value": 7, "label": "7" },
{ "value": 8, "label": "8" },
{ "value": 9, "label": "9" },
{ "value": 10, "label": "10" }
]
}
],
"required_items": ["item_1"]
}

2. Calling the endpoint in your language

curl

# Tiny (single-item) instrument
curl -sS "https://engine.tripod-health.com/api/pain/pain_scale/questions" \
-H "apikey: $ENGINE_API_KEY"

# Multi-item instrument
curl -sS "https://engine.tripod-health.com/api/psychosocial/depression_screen/questions" \
-H "apikey: $ENGINE_API_KEY"

Python (requests) — render a form

# Uses the call() helper from quickstart.md.
rubric = call("GET", "/api/psychosocial/depression_screen/questions")
print(f"{rubric['instrument_id']} v{rubric['version']}")
for item in rubric["items"]:
print(f" {item['id']}: {item['text']}")
for choice in item["answer_choices"]:
print(f" {choice['value']}. {choice['label']}")

Node.js (native fetch) — cache by version

// Uses the call() helper from quickstart.md.
const cache = new Map();

async function loadInstrument(category, resource) {
const key = `${category}/${resource}`;
const cached = cache.get(key);
const rubric = await call("GET", `/api/${category}/${resource}/questions`);
if (cached && cached.version === rubric.version) return cached;
cache.set(key, rubric);
return rubric;
}

const rubric = await loadInstrument("pain", "orebro");

TypeScript

// Uses the typed call<T>() helper from quickstart.md.
interface AnswerChoice { value: number; label: string }

interface RubricItem {
id: string;
text: string;
answer_choices: AnswerChoice[];
}

interface QuestionsResponse {
instrument_id: string;
version: string;
items: RubricItem[];
required_items: string[];
}

const rubric = await call<QuestionsResponse>(
"GET",
"/api/pain/orebro/questions",
);

Go

// Uses the call() helper from quickstart.md.
rubric, err := call("GET", "/api/pain/pain_scale/questions", nil, 3)
if err != nil {
panic(err)
}
items := rubric["items"].([]any)
for _, i := range items {
item := i.(map[string]any)
fmt.Printf("%v: %v\n", item["id"], item["text"])
}

Java

// Uses the call() helper from quickstart.md.
// Response is a nested JSON object; real callers should deserialize
// with Jackson/Gson for typed traversal.
String rubric = call("GET", "/api/pain/pain_scale/questions", null, 3);
System.out.println("version: " + extract(rubric, "version"));

C# (.NET HttpClient)

// Uses the Call() helper from quickstart.md.
var rubric = await Call(HttpMethod.Get, "/api/pain/pain_scale/questions");
var version = rubric.GetProperty("version").GetString();
Console.WriteLine($"{rubric.GetProperty("instrument_id").GetString()} v{version}");

foreach (var item in rubric.GetProperty("items").EnumerateArray())
{
var id = item.GetProperty("id").GetString();
var text = item.GetProperty("text").GetString();
Console.WriteLine($" {id}: {text}");
}

PowerShell

# Uses the Invoke-Engine helper from quickstart.md.
$rubric = Invoke-Engine -Method Get -Path "/api/pain/pain_scale/questions"
"$($rubric.instrument_id) v$($rubric.version)"
foreach ($item in $rubric.items) {
" $($item.id): $($item.text)"
}

3. Error scenarios

Unknown resource

Send:

curl -sS "https://engine.tripod-health.com/api/pain/frobnitz/questions" \
-H "apikey: $ENGINE_API_KEY" \
-H "X-Request-ID: docs-questions-unknown" \
-w "\nHTTP %{http_code}\n"

Receive (404):

{
"timestamp": "2026-04-21T23:10:00.000Z",
"error": "NotFound",
"message": "Unknown resource: pain/frobnitz",
"request_id": "docs-questions-unknown"
}

Handing the response into /score

The response body for /score uses the item IDs from this endpoint as keys in its responses map. Values are the numeric value fields from answer_choices, not the label:

{
"responses": {
"item_1": 4,
"item_2": 2
}
}

Every ID in required_items must appear in the responses map, or /score responds 400 ValidationError with a missing_items array naming which ones are missing. See patterns-score.md.

Item text caveat

The item text returned today is a terse paraphrase of each published instrument's content — enough to verify item identity and choice ranges in integration, but not validated clinical wording. Before showing these strings to a real user, replace them with the officially-licensed wording for the instrument (and confirm any licensing requirements for public administration). Trinity treats the value integers as the authoritative contract, not the displayed text.

Intended use

  • Populating a questionnaire UI. One call per instrument gives you everything needed to render the form.
  • Validating a client-side draft before submission. Walk required_items and ensure each has a value before POSTing to /score.
  • Version-pinning a cached form. Cache the rubric keyed on instrument_id + version; refetch when you see a new version on a subsequent pull or on a scoring response's catalog_version change.

Mistaken use

  • Do not treat items[] order as authoritative for scoring. Scoring keys on id, not position. A UI can reorder items safely; Trinity still scores the same way.
  • Do not send answer_choices[].label to /score. Send the value integer. Labels are for display; they are not part of the scoring contract.
  • Do not assume every instrument's answer_choices range is the same. Ranges vary: 0–10 (Örebro, NPRS, recovery_expectation), 0–3 (PHQ-9, GAD-7), 0–5 (ODI, NDI), 1–5 (QuickDASH), 1–4 (TSK-11), 0/1 (OSPRO-YF, ORT). Always read answer_choices from this endpoint, never hardcode ranges.
  • Do not call this endpoint on every page render. The response is stable for the lifetime of an instrument version. Cache it.

Debugging

  1. Capture the X-Request-ID response header.
  2. 404 NotFound — the (category, resource) pair is not registered. Cross-reference GET /api/catalog/resources to see what the current deploy exposes. Typos in resource (for example phq9 instead of depression_screen) are the usual cause.
  3. 503 ServiceUnavailable — the instrument is registered in the catalog but the rubric was not loaded. Transient; retry with backoff. Persistent 503s on one instrument indicate a deploy-side problem worth reporting with the request_id.
  4. answer_choices do not match what you expected. Confirm the version on the response. Rubric content changes bump version; comparing yours against the live response reveals whether your client was rendering against a stale copy.
  5. Subsequent /score fails with missing_items. Compare the keys in your responses map against required_items here. A common slip is sending item_01 when the rubric expects item_1.

Rate limits

See rate-limits.md.

  • patterns-score.md — the paired scoring operation.
  • Per-instrument docs (instruments-<resource>.md) — each one documents the specific item IDs, answer-choice ranges, required items, and scoring semantics for that instrument.
  • GET /api/catalog/resources — list of every (category, resource) pair currently exposed.

Change history

DateChange
2026-04-20Initial publication.