Oswestry Disability Index
Status:
stableCategory:functionAdded:2026-04-20Last reviewed:2026-04-21
Summary
The Oswestry Disability Index (ODI) is Trinity's lumbar-specific
functional disability measure. Ten items, each scored 0–5,
converted to a 0–100% disability score and mapped to five bands
from minimal to bed_bound. Higher score means more disability.
⚠ Scoring-formula note
Trinity's ODI score is computed as:
score = mean_of_responded_items × 20
where the mean is taken across the items actually present in the
request body (all ten are required in the current rubric). With the
response scale of 0–5 per item, this produces 0–100%.
Some legacy implementations compute ODI as sum × 2 (over ten items
scored 0–5, producing 0–100) or as (sum / responded_items) × 10
(which yields 0–50, half the conventional scale). Trinity uses
mean × 20. Callers migrating from another ODI implementation
should confirm the formula before comparing scores, or bands may
appear to shift even when the underlying responses have not.
Resource identity
| Field | Value |
|---|---|
| Category | function |
| Resource | oswestry |
| Instrument ID | oswestry |
| Rubric version | 1.0.0 |
| Path prefix | /api/function/oswestry |
Operations
| Operation | Method | Path | Contract |
|---|---|---|---|
| Questions | GET | /api/function/oswestry/questions | patterns-questions.md |
| Score | POST | /api/function/oswestry/score | patterns-score.md |
Items
Ten items, each on a 0–5 scale.
| ID | Prompt (paraphrased) |
|---|---|
item_1 | Pain intensity. |
item_2 | Personal care (washing, dressing). |
item_3 | Lifting. |
item_4 | Walking. |
item_5 | Sitting. |
item_6 | Standing. |
item_7 | Sleeping. |
item_8 | Sex life (if applicable). |
item_9 | Social life. |
item_10 | Traveling. |
Answer choices per item:
| Value | Label |
|---|---|
0 | No problem |
1 | Mild problem |
2 | Moderate problem |
3 | Fairly severe problem |
4 | Very severe problem |
5 | Complete disability |
Scoring
| Field | Value |
|---|---|
| Method | response_mean_scaled with multiplier = 20, offset = 0. Formula: (mean_of_items + 0) × 20. |
| Score range | 0 – 100 (percentage). |
Bands
| Band | Range (inclusive) | Clinical meaning |
|---|---|---|
minimal | 0 – 20 | Minimal disability. |
moderate | 21 – 40 | Moderate disability. |
severe | 41 – 60 | Severe disability. |
crippled | 61 – 80 | Crippled — pain is the main issue. |
bed_bound | 81 – 100 | Bed-bound or condition-exaggerating. |
Required items
All ten items are required for a strict /score call.
Worked example
Every item answered with 3. See quickstart.md for
per-language scaffolding and patterns-score.md
for the full /score contract.
Send:
POST /api/function/oswestry/score HTTP/1.1
Host: engine.tripod-health.com
apikey: $ENGINE_API_KEY
Content-Type: application/json
X-Request-ID: docs-oswestry-score-basic
{
"responses": {
"item_1": 3, "item_2": 3, "item_3": 3, "item_4": 3, "item_5": 3,
"item_6": 3, "item_7": 3, "item_8": 3, "item_9": 3, "item_10": 3
}
}
Receive (200 OK):
{
"instrument": "oswestry",
"category": "function",
"finding": {
"algorithm_id": "oswestry",
"algorithm_version": "1.0.0",
"score": 60,
"band": "severe",
"raw": {
"rubric_version": "1.0.0",
"responded_items": 10
},
"context": null,
"imputed": false,
"sd": null,
"prior_source": null
},
"confidence": {
"overall": 1,
"tier": "high",
"breakdown": {
"input_completeness": 1,
"imputation_variance": null,
"model_certainty": null
}
},
"recommendations": null,
"depth": "basic",
"catalog_version": "0.13.0",
"request_id": "docs-oswestry-score-basic",
"timestamp": "2026-04-21T23:14:54.245147+00:00"
}
Computation: mean of responses = 3.0 × 20 = 60.0 → upper edge of
the severe band (41–60). If every item were 5, the mean would be
5.0 × 20 = 100.0 (bed_bound band, 81–100).
Calling in your language
curl
curl -sS -X POST "https://engine.tripod-health.com/api/function/oswestry/score" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"responses":{"item_1":3,"item_2":3,"item_3":3,"item_4":3,"item_5":3,"item_6":3,"item_7":3,"item_8":3,"item_9":3,"item_10":3}}'
Python (requests)
# Uses the call() helper from quickstart.md.
responses = {f"item_{i}": 3 for i in range(1, 11)}
result = call("POST", "/api/function/oswestry/score", {"responses": responses})
print(result["finding"]["score"], result["finding"]["band"])
Node.js (native fetch)
// Uses the call() helper from quickstart.md.
const responses = Object.fromEntries(
Array.from({ length: 10 }, (_, i) => [`item_${i + 1}`, 3]),
);
const result = await call("POST", "/api/function/oswestry/score", { responses });
console.log(result.finding.score, result.finding.band);
TypeScript
// Uses the typed call<T>() helper from quickstart.md.
const responses = Object.fromEntries(
Array.from({ length: 10 }, (_, i) => [`item_${i + 1}`, 3]),
);
const result = await call<ScoreResponse>(
"POST",
"/api/function/oswestry/score",
{ responses },
);
Go
// Uses the call() helper from quickstart.md.
responses := make(map[string]int, 10)
for i := 1; i <= 10; i++ {
responses[fmt.Sprintf("item_%d", i)] = 3
}
result, err := call("POST", "/api/function/oswestry/score",
map[string]any{"responses": responses}, 3)
if err != nil {
panic(err)
}
finding := result["finding"].(map[string]any)
fmt.Printf("oswestry: %v (%v)\n", finding["score"], finding["band"])
Java
// Uses the call() helper from quickstart.md.
StringBuilder body = new StringBuilder("{\"responses\":{");
for (int i = 1; i <= 10; i++) {
if (i > 1) body.append(",");
body.append("\"item_").append(i).append("\":3");
}
body.append("}}");
String result = call("POST", "/api/function/oswestry/score", body.toString(), 3);
System.out.println(result);
C# (.NET HttpClient)
// Uses the Call() helper from quickstart.md.
var responses = Enumerable.Range(1, 10)
.ToDictionary(i => $"item_{i}", _ => 3);
var result = await Call(HttpMethod.Post, "/api/function/oswestry/score",
new { responses });
var finding = result.GetProperty("finding");
Console.WriteLine($"oswestry: {finding.GetProperty("score").GetDouble()} ({finding.GetProperty("band").GetString()})");
PowerShell
# Uses the Invoke-Engine helper from quickstart.md.
$responses = @{}
1..10 | ForEach-Object { $responses["item_$_"] = 3 }
$result = Invoke-Engine -Method Post -Path "/api/function/oswestry/score" -Body @{ responses = $responses }
"oswestry: $($result.finding.score) ($($result.finding.band))"
Clinical interpretation
- What it measures. Self-reported functional disability attributed to low-back pain, across ten activities of daily living.
- Higher is worse. Disability increases with score. This is the inverse of LEFS, where higher means better function — a common source of sign-flip bugs when clients consume both.
- Rapid sensitivity. The ODI is responsive to change during acute low-back pain episodes, which makes it a reasonable time-series instrument in that context.
Intended use
- Baseline and follow-up disability measurement in low-back pain workflows.
- Outcome tracking for non-surgical and peri-surgical interventions.
- Feeding the CDRI composite as the function-domain signal for lumbar complaints.
Mistaken use
- Do not use for cervical or upper-extremity function. Use
neck_disabilityorquick_dashinstead. - Do not confuse the formula.
mean × 20, notsum × 2, and notmean × 10. A3on every item yields60%, not30and not30%. - Do not omit item 8 for all subjects. The rubric today marks
every item as required. A
/scorecall that omitsitem_8returns400 ValidationError. In clinical practice, item 8 may be left blank when not applicable; your client should elicit an explicit response (even if symbolic) before submitting. - Do not compare ODI scores to QuickDASH scores. They both
scale
0–100but measure different regions and are not interchangeable.
Debugging
- Capture
request_id. Present in every response body. - Score unexpectedly halved or doubled. Confirm your client
uses
mean × 20for comparisons, notsum × 2ormean × 10. Compare against the worked example above (mean3, score60). 400 ValidationErrorwithmissing_items: ["item_8"]. Your form skipped the sex-life item. Add an explicit response path (the instrument's published wording permits "not applicable" which is conventionally coded to0).
Related
- patterns-questions.md, patterns-score.md — shared contracts.
- instruments-neck_disability.md — NDI; the neck analog. Structurally similar but distinct scale.
- instruments-quick_dash.md — upper-extremity function.
- instruments-lower_extremity_function.md — LEFS; lower-extremity function, with the opposite sign convention.
- composite-cdri-compute.md — where ODI feeds the function domain signal.
Change history
| Date | Change |
|---|---|
| 2026-04-20 | Initial publication. |