Opioid Risk Tool
Status:
stableCategory:iatrogenicAdded:2026-04-20Last reviewed:2026-04-21
Summary
The Opioid Risk Tool (ORT) is Trinity's pre-prescription risk
screen for opioid misuse. Five yes/no items, with item weights that
differ by self-reported sex. Totals map to three tiers from low
to high.
⚠ Sex-conditional weighting
The ORT is unusual in the catalog: item weights change based on the
caller's reported demographics.sex. Male and female subjects who
answer identically can receive different scores.
- Weights for
sex = "male":item_1=3,item_2=4,item_3=1,item_4=0,item_5=2. - Weights for
sex = "female":item_1=1,item_2=2,item_3=1,item_4=3,item_5=1. - If
demographics.sexis missing,null, or any value other than"male"or"female", Trinity falls back to the male weights as the default.
The default-to-male fallback matches the original ORT's behavior
when sex is not recorded, but it means reporting the wrong sex
value silently produces a wrong score. Confirm demographics.sex
is filled out and accurate before relying on ORT scores.
Non-binary and non-disclosed subjects are not accommodated by the published ORT; clinical interpretation in those populations is outside what the score was validated on.
Reduced-item form
The published ORT has ten sub-items (items 1 and 2 each split into three sub-items for alcohol, illegal drugs, and prescription drugs). Trinity implements a five-item flattened form where items 1 and 2 are single yes/no questions covering all substance classes.
A score from this five-item form is not directly comparable to a score from a full ten-sub-item ORT administered elsewhere. Keep your clinical interpretation anchored to the five-item tiers documented below.
Resource identity
| Field | Value |
|---|---|
| Category | iatrogenic |
| Resource | opioid_risk |
| Instrument ID | opioid_risk |
| Rubric version | 1.0.0 |
| Path prefix | /api/iatrogenic/opioid_risk |
Operations
| Operation | Method | Path | Contract |
|---|---|---|---|
| Questions | GET | /api/iatrogenic/opioid_risk/questions | patterns-questions.md |
| Score | POST | /api/iatrogenic/opioid_risk/score | patterns-score.md |
Items
Five binary yes/no items.
| ID | Prompt (paraphrased) |
|---|---|
item_1 | Family history of substance abuse (alcohol / illegal drugs / prescription). |
item_2 | Personal history of substance abuse (alcohol / illegal drugs / prescription). |
item_3 | Age between 16 and 45. |
item_4 | History of preadolescent sexual abuse. |
item_5 | Psychological disease (ADD, OCD, bipolar, schizophrenia, depression). |
Answer choices per item:
| Value | Label |
|---|---|
0 | No |
1 | Yes |
Scoring
| Field | Value |
|---|---|
| Method | weighted_sum_conditional with conditional_on = "sex". |
| Score range | 0 – 10 in practice. (The rubric's high band extends to 30 to absorb any upstream scoring change; no realistic input produces a score above 10 on either weight table.) |
Weight tables
Male weights (also the default when sex is missing or not one of
"male"/"female"):
| Item | Weight |
|---|---|
item_1 | 3 |
item_2 | 4 |
item_3 | 1 |
item_4 | 0 |
item_5 | 2 |
Female weights:
| Item | Weight |
|---|---|
item_1 | 1 |
item_2 | 2 |
item_3 | 1 |
item_4 | 3 |
item_5 | 1 |
Bands
| Band | Range (inclusive) | Clinical meaning |
|---|---|---|
low | 0 – 3 | Low risk for opioid misuse. |
moderate | 4 – 7 | Moderate risk — consider monitoring / alternative plans. |
high | 8 – 30 | High risk — generally a contraindication absent compensating controls. |
The 0–3, 4–7, 8+ cutoffs follow the published ORT
interpretation for the categorical risk tier.
Required items
All five items are required for a strict /score call.
demographics.sex is not a required item for request validation
(the request will succeed without it), but its absence means the
Trinity uses male-default weights, which may not be what the caller
intends.
Worked example
Every item answered with 1 (every flag raised), sex = male. See
quickstart.md for per-language scaffolding and
patterns-score.md for the shared /score
contract.
Send:
POST /api/iatrogenic/opioid_risk/score HTTP/1.1
Host: engine.tripod-health.com
apikey: $ENGINE_API_KEY
Content-Type: application/json
X-Request-ID: docs-opioid_risk-score-basic
{
"responses": {
"item_1": 1, "item_2": 1, "item_3": 1, "item_4": 1, "item_5": 1,
"sex": "male"
}
}
Receive (200 OK):
{
"instrument": "opioid_risk",
"category": "iatrogenic",
"finding": {
"algorithm_id": "opioid_risk",
"algorithm_version": "1.0.0",
"score": 10,
"band": "high",
"raw": {
"rubric_version": "1.0.0",
"responded_items": 6
},
"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-opioid_risk-score-basic",
"timestamp": "2026-04-21T23:15:03.422789+00:00"
}
Computation with male weights: 1 × 3 + 1 × 4 + 1 × 1 + 1 × 0 + 1 × 2 = 10 → high tier (≥ 8).
Sex-conditional scoring
Identical yes/no answers can produce different bands depending on the
value of responses.sex. Trinity's weighted_sum_conditional
method reads the condition value from responses.sex, not from
demographics.sex. Two subjects answering {item_1: 1, item_2: 0, item_3: 1, item_4: 0, item_5: 0}:
- With
sex: "male":1 × 3 + 0 × 4 + 1 × 1 + 0 × 0 + 0 × 2 = 4→moderate. - With
sex: "female":1 × 1 + 0 × 2 + 1 × 1 + 0 × 3 + 0 × 1 = 2→low.
Omitting responses.sex makes Trinity fall back to the _default
weights (which match male today) — which can inflate the score for a
female subject. Always pass sex inside responses to pin the
weight table explicitly. A future rubric revision may move this
condition into demographics; today it lives in responses.
Calling in your language
curl
curl -sS -X POST "https://engine.tripod-health.com/api/iatrogenic/opioid_risk/score" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"responses":{"item_1":1,"item_2":1,"item_3":1,"item_4":1,"item_5":1,"sex":"male"}}'
Python (requests)
# Uses the call() helper from quickstart.md.
result = call("POST", "/api/iatrogenic/opioid_risk/score", {
"responses": {
"item_1": 1, "item_2": 1, "item_3": 1, "item_4": 1, "item_5": 1,
"sex": "male",
},
})
print(result["finding"]["score"], result["finding"]["band"])
Node.js (native fetch)
// Uses the call() helper from quickstart.md.
const result = await call("POST", "/api/iatrogenic/opioid_risk/score", {
responses: {
item_1: 1, item_2: 1, item_3: 1, item_4: 1, item_5: 1,
sex: "male",
},
});
console.log(result.finding.score, result.finding.band);
TypeScript
// Uses the typed call<T>() helper from quickstart.md.
const result = await call<ScoreResponse>(
"POST",
"/api/iatrogenic/opioid_risk/score",
{
responses: {
item_1: 1, item_2: 1, item_3: 1, item_4: 1, item_5: 1,
sex: "male",
},
},
);
Go
// Uses the call() helper from quickstart.md.
responses := map[string]any{
"item_1": 1, "item_2": 1, "item_3": 1, "item_4": 1, "item_5": 1,
"sex": "male",
}
result, err := call("POST", "/api/iatrogenic/opioid_risk/score",
map[string]any{"responses": responses}, 3)
if err != nil {
panic(err)
}
finding := result["finding"].(map[string]any)
fmt.Printf("ORT: %v (%v)\n", finding["score"], finding["band"])
Java
// Uses the call() helper from quickstart.md.
String body = "{\"responses\":{"
+ "\"item_1\":1,\"item_2\":1,\"item_3\":1,\"item_4\":1,\"item_5\":1,"
+ "\"sex\":\"male\"}}";
String result = call("POST",
"/api/iatrogenic/opioid_risk/score", body, 3);
System.out.println(result);
C# (.NET HttpClient)
// Uses the Call() helper from quickstart.md.
var result = await Call(HttpMethod.Post,
"/api/iatrogenic/opioid_risk/score",
new {
responses = new Dictionary<string, object> {
["item_1"] = 1, ["item_2"] = 1, ["item_3"] = 1,
["item_4"] = 1, ["item_5"] = 1,
["sex"] = "male",
},
});
var finding = result.GetProperty("finding");
Console.WriteLine($"ORT: {finding.GetProperty("score").GetDouble()} ({finding.GetProperty("band").GetString()})");
PowerShell
# Uses the Invoke-Engine helper from quickstart.md.
$result = Invoke-Engine -Method Post -Path "/api/iatrogenic/opioid_risk/score" -Body @{
responses = @{
item_1 = 1; item_2 = 1; item_3 = 1; item_4 = 1; item_5 = 1
sex = "male"
}
}
"ORT: $($result.finding.score) ($($result.finding.band))"
Clinical interpretation
- What it screens. Risk of aberrant opioid-related behavior before starting opioid therapy.
- Not a diagnosis. The ORT flags candidates for heightened monitoring or alternative plans; it does not diagnose opioid-use disorder.
- Population. Validated primarily in adult chronic-pain populations considering long-term opioid therapy.
Intended use
- Pre-prescription risk stratification in workflows considering opioid therapy.
- Prompting alternative-plan conversations at
moderateorhightiers. - Feeding the CDRI composite as the iatrogenic-risk signal.
Mistaken use
- Do not omit
sexand accept the resulting score as unisex-appropriate. The default is male-weighted; for female subjects the default yields an inflated score. - Do not use the ORT as a clinical gatekeeper in isolation. It
is one signal among several (history, clinical interview,
medication reconciliation). A
lowtier does not guarantee safety; ahightier does not preclude careful opioid therapy with adequate controls. - Do not compare ORT scores from this endpoint to ORT scores from a full ten-sub-item administration. Trinity uses the five-item flattened form.
- Do not administer to non-binary or non-disclosed subjects and expect a validated result. The published ORT scoring assumes binary sex; Trinity does the same.
Debugging
- Capture
request_id. Present in every response body. - Score does not change when
sexis flipped. Confirmsexis sent insideresponses, not insidedemographics, per the example above. Trinity's conditional scoring reads from theresponsesobject. - Unexpectedly
moderateorhightier for a female subject with low risk factors. You likely forgot thesexkey; the Trinity applied the male-default weights, which tend to assign higher weight to items 1 and 2. 400 ValidationErrorwithmissing_items. Send all five item responses. Omittingsexdoes not raise this error (it is not in the rubric'srequired_items).
Related
- patterns-questions.md, patterns-score.md — shared contracts.
- conventions.md —
demographics.sexis a structural descriptor, not an identifier; sending it does not trip the de-identification filter. - composite-cdri-compute.md — composite.
Change history
| Date | Change |
|---|---|
| 2026-04-20 | Initial publication. |