Loading documentation

Access required

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

Sign out
Skip to main content

Neck Disability Index

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

Summary

The Neck Disability Index (NDI) is Trinity's cervical-specific functional disability measure. Ten items, each scored 05, producing a total of 0–100 after a ×2 weight per item. Higher score means more disability.

Structurally similar to oswestry (lumbar), but with different item content and a different scoring formula. They are not interchangeable.

Resource identity

FieldValue
Categoryfunction
Resourceneck_disability
Instrument IDneck_disability
Rubric version1.0.0
Path prefix/api/function/neck_disability

Operations

OperationMethodPathContract
QuestionsGET/api/function/neck_disability/questionspatterns-questions.md
ScorePOST/api/function/neck_disability/scorepatterns-score.md

Items

Ten items, each on a 05 scale.

IDPrompt (paraphrased)
item_1Pain intensity.
item_2Personal care (washing, dressing).
item_3Lifting.
item_4Reading.
item_5Headaches.
item_6Concentration.
item_7Work.
item_8Driving.
item_9Sleeping.
item_10Recreation.

Answer choices per item:

ValueLabel
0No pain / no limitation
1Very mild
2Moderate
3Fairly severe
4Very severe
5Worst imaginable / unable

Scoring

FieldValue
Methodweighted_sum with every weight set to 2. Equivalent to (sum of 0–5 responses) × 2.
Score range0100.

The weighted_sum method with uniform weight of 2 produces the same result as the published NDI convention of (sum × 2) = percentage disability, for a 0–100% output.

Bands

BandRange (inclusive)Clinical meaning
none08No / minimal disability.
mild1028Mild disability.
moderate3048Moderate disability.
severe5068Severe disability.
complete70100Complete disability.

Band ranges are designed around the (sum × 2) output. Because the multiplier is 2, scores are always even integers (0, 2, 4, ..., 100), so the "gaps" at 9, 29, 49, 69 between bands are unreachable in practice.

Required items

All ten items are required for a strict /score call.

Worked example

Every item answered with 3 ("fairly severe"). See quickstart.md for per-language scaffolding and patterns-score.md for the shared /score contract.

Send:

POST /api/function/neck_disability/score HTTP/1.1
Host: engine.tripod-health.com
apikey: $ENGINE_API_KEY
Content-Type: application/json
X-Request-ID: docs-neck_disability-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": "neck_disability",
"category": "function",
"finding": {
"algorithm_id": "neck_disability",
"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-neck_disability-score-basic",
"timestamp": "2026-04-21T23:14:56.756123+00:00"
}

Computation: weighted sum — sum(3 × 2 for i in 1..10) = 60, lands in the severe band (50–68). Every item at 0 yields 0 (none); every item at 5 yields 100 (complete).

Calling in your language

curl

curl -sS -X POST "https://engine.tripod-health.com/api/function/neck_disability/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/neck_disability/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/neck_disability/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/neck_disability/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/neck_disability/score",
map[string]any{"responses": responses}, 3)
if err != nil {
panic(err)
}
finding := result["finding"].(map[string]any)
fmt.Printf("neck_disability: %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/neck_disability/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/neck_disability/score",
new { responses });
var finding = result.GetProperty("finding");
Console.WriteLine($"neck_disability: {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/neck_disability/score" -Body @{ responses = $responses }
"neck_disability: $($result.finding.score) ($($result.finding.band))"

Clinical interpretation

  • What it measures. Self-reported functional disability attributed to neck pain, across ten activities of daily living.
  • Higher is worse. Disability increases with score.
  • Percentage scale. Trinity's output is already on a 0–100 scale; no further normalization is needed.
  • Comparison to ODI. NDI and ODI share the ten-item structure and 0–100 output, but ask about different body regions and have different band cutoffs. A subject who scores 50% on ODI and 50% on NDI has different clinical implications depending on the condition.

Intended use

  • Cervical outcome tracking for neck pain, whiplash, or cervical-radicular conditions.
  • Baseline vs. follow-up after interventions targeting the neck.
  • Feeding the CDRI composite as the cervical function signal.

Mistaken use

  • Do not use this for lumbar or upper-extremity complaints. Use oswestry or quick_dash instead.
  • Do not confuse NDI and ODI numerically. They produce similar 0–100 outputs but with different band cutoffs and different item content. 50% on ODI is not equivalent to 50% on NDI.
  • Do not apply a ×2 conversion client-side. Trinity does it. A double-applied conversion pushes the score above 100.

Debugging

  1. Capture request_id. Present in every response body.
  2. Score is exactly half of what you expected. You likely pre-multiplied by 2 on the client; drop the client-side multiplier.
  3. Score is exactly double what you expected. You may be interpreting the raw sum (0–50) as the score; Trinity returns the ×2 conversion. Check the finding.raw.method field on the response — it says weighted_sum, and the weights are 2 per item.
  4. 400 ValidationError with missing_items. Send all ten items.

Change history

DateChange
2026-04-20Initial publication.