Recordability Key Factors
Status:
stableCategory:recordabilityAdded:2026-07-01Last reviewed:2026-07-01
Summary
Analyzes one OSHA recordability factor for a de-identified injury
or illness case and returns a weighted, cited assessment of that factor
alone. Five sibling endpoints share this page: each takes the same case
body, runs a single AI analysis scoped to its factor, and returns the
same response shape. A factor call is cheaper and faster than the full
composite — use it when you need one factor's view, and
/api/recordability/analyze when you
need the overall answer.
The engine never makes the final recordability determination. Every response from these endpoints is decision support: a likelihood, a leaning, and reasoning grounded in cited regulation text and official OSHA guidance. A qualified recordkeeping professional weighs that output and makes the call. Treat everything on this page as advisory.
Endpoint
| Field | Value |
|---|---|
| Method | POST |
| Path | /api/recordability/{factor} |
| Full URL | https://engine.tripod-health.com/api/recordability/{factor} |
| Authentication | API key (required) |
| Request body | application/json |
| Response body | application/json |
| Idempotent | No |
| Rate-limited | Yes |
Factors
{factor} must be exactly one of the five factor keys. The response's
likelihood has a factor-specific meaning — always the probability
of the proposition in the table, never a generic "recordability score":
| Factor key | Assesses | likelihood is the probability that... | items carries |
|---|---|---|---|
work_relatedness | The 29 CFR 1904.5 work-relatedness presumption, weighed dimension by dimension. | ...the case is work-related under the presumption. | One determination per analysis dimension: geographic_presumption, mechanism_plausibility, timing_correlation, corroboration, clinical_consistency, preexisting_contribution. |
exceptions | The nine 1904.5(b)(2) exceptions to work-relatedness, each ruled in or out individually. | ...at least one exception applies. A high value argues against recordability — this is the one factor whose direction is inverted. | One entry per exception, keys 1904.5(b)(2)(i) through 1904.5(b)(2)(ix). |
recurrence | New case vs. recurrence under 1904.6, and significant aggravation of a pre-existing condition under 1904.5(b)(4). | ...the case counts as a new case (or a significant aggravation) rather than a continuation of an old one. | Per-question determinations where applicable. |
treatment | Medical treatment beyond first aid, measured against the exhaustive 1904.7(b)(5)(ii) first-aid list. | ...at least one provided treatment goes beyond first aid. | One entry per treatment in case.treatment.provided, each classified individually. |
significance | Significant diagnosed conditions under 1904.7(b)(7) and the specific recording criteria of 1904.8–1904.11. | ...the case meets a significance criterion or one of the specific criteria. | Per-criterion determinations where applicable. |
Any other final path segment does not exist and returns 404.
If you need the presumption and the exceptions combined into one
work-relatedness determination, call
/api/work_relatedness/analyze
instead of making both factor calls yourself.
Authentication
Send a valid API key in the apikey header. See
authentication.md.
Request
Headers
| Header | Required | Description |
|---|---|---|
apikey | Yes | Consumer API key. |
Content-Type | Yes | Must be application/json. |
X-Request-ID | No | Optional correlation ID, echoed in the response body's request_id and the X-Request-ID response header. |
Path parameters
| Parameter | Type | Description |
|---|---|---|
factor | string | One of the five factor keys. |
Query parameters
None.
Body
All five endpoints accept the same body: a de-identified case
object plus an optional lens. A factor endpoint reads the slice of
the case it needs, so you can send one assembled body to any or all of
them.
Only case.incident.description and case.injury.description are
required (with their parent objects). Everything else is optional —
but the more of the case you provide, the more the analysis has to work
with, and the higher the response's certainty. Fields not listed
below are rejected with 400 ValidationError.
Body fields
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
case.incident.description | string | Yes | 1–8000 chars | Narrative of what happened. Refer to people by role, never by name. |
case.incident.incident_date | string (date) | No | YYYY-MM-DD | Date of the incident. |
case.incident.reported_date | string (date) | No | YYYY-MM-DD | Date the incident was reported to the employer. The gap between incident and report is a corroboration signal. |
case.incident.location_context | string | No | on_premises, parking_lot, company_travel, work_from_home, offsite_work, non_work_location, unknown | Where the incident occurred, structurally. |
case.incident.activity_at_time | string | No | ≤ 1000 chars | What the employee was doing when it happened. |
case.incident.during_assigned_hours | boolean | No | — | Whether the incident occurred during assigned working hours. |
case.injury.description | string | Yes | 1–8000 chars | Narrative of the injury or illness and its progression. |
case.injury.body_part | string | No | ≤ 200 chars | Affected body part. |
case.injury.diagnosis | string | No | ≤ 500 chars | Diagnosis, if one has been made. |
case.injury.icd_category | string | No | ≤ 40 chars | ICD category code — the structural descriptor, not a full clinical record. |
case.injury.symptom_onset_date | string (date) | No | YYYY-MM-DD | When symptoms first appeared; may differ from incident_date. |
case.injury.onset_type | string | No | acute, gradual, unknown | Discrete acute event vs. gradual/cumulative presentation — the work-relatedness analysis differs materially between the two. |
case.employee.job_title | string | No | ≤ 200 chars | Job title. |
case.employee.job_duties | string | No | ≤ 2000 chars | What the job actually involves (physical demands, typical tasks) — grounds mechanism-plausibility reasoning beyond a bare title. |
case.employee.department | string | No | ≤ 200 chars | Department or work area. |
case.employee.tenure_bracket | string | No | ≤ 40 chars | Tenure as a bracket (e.g. "1-3 years"), never a hire date. |
case.treatment.provided | string[] | No | ≤ 25 items, each 1–1000 chars | Each treatment provided, one string per treatment. The treatment factor classifies each entry individually. |
case.treatment.provider_type | string | No | ≤ 200 chars | Who provided care (e.g. "occupational medicine clinic"), by role, not by name. |
case.treatment.prescription_issued | boolean | No | — | Whether any prescription was issued. Recordability can attach at issuance, even if never filled. |
case.prior_condition.exists | boolean | No | — | Whether a relevant pre-existing condition exists. |
case.prior_condition.description | string | No | ≤ 4000 chars | Narrative of the prior condition and its resolution. |
case.prior_condition.prior_incident_date | string (date) | No | YYYY-MM-DD | Date of the prior incident or episode. |
case.statements | object[] | No | ≤ 20 items | Accounts of the incident from different sources. |
case.statements[].source | string | Yes (per statement) | employee, witness, clinician, employer, injury_report | Who the statement is from, by role. |
case.statements[].text | string | Yes (per statement) | 1–8000 chars | The statement itself. Refer to people by role, never by name. |
case.work_status.days_away | integer | No | 0–3650 | Calendar days away from work. |
case.work_status.restricted_duty | boolean | No | — | Whether the employee is on restricted duty. |
case.work_status.job_transfer | boolean | No | — | Whether the employee was transferred to another job. |
case.work_status.loss_of_consciousness | boolean | No | — | Whether the employee lost consciousness. |
case.work_status.fatality | boolean | No | — | Whether the case is a fatality. |
lens | string | No | safety, occupational_medicine | Flavors the reasoning style only — exposure/process framing vs. clinical framing. It never changes the determination. |
De-identification requirement
All request bodies are scanned for identifiers on the way in, and a
body containing one returns 400 DeIdentificationViolation with the
field extra naming the offending path.
Two rules specific to this surface:
- Event dates are exempt; other precise dates are not. Precise
dates are normally rejected as potential dates of birth, but the four
named event-date fields —
incident_date,reported_date,symptom_onset_date, andprior_incident_date— are exempt, because an event date describes what happened, not who it happened to. Do not put dates anywhere else, including free text. - Free-text fields carry no identifiers.
description,activity_at_time,job_duties, statementtext, and every other free-text field must never contain names, initials, SSNs, email addresses, or phone numbers. Refer to people by role: the employee, the witness, the treating clinician.
Response
Success — 200 OK
All five factors return the same shape. The example below is a real
capture of POST /api/recordability/treatment for the
canonical example case — a slip on parking-lot ice followed by
an ice pack, prescription naproxen, and a physical-therapy referral.
The reasoning and citations are abbreviated here for readability;
a real reasoning runs several paragraphs, explicitly weighing both
directions, and typically carries more citations.
{
"factor": "treatment",
"likelihood": 0.92,
"leaning": "yes",
"reasoning": "The employee received three distinct treatments following the slip-and-fall injury to the right shoulder. The assessment turns on whether any of these constitute medical treatment beyond first aid as defined in 29 CFR 1904.7(b)(5)(ii).\n\nThe ice pack is the only first-aid item provided. Cold therapy appears explicitly on the first-aid list at 1904.7(b)(5)(ii)(E) and is therefore not recordable as medical treatment.",
"citations": [
{
"source_type": "regulation",
"ref": "1904.7(b)(5)(ii)(E)",
"quote": "Using hot or cold therapy"
},
{
"source_type": "regulation",
"ref": "1904.7(b)(5)(ii)(A)",
"quote": "Using a non-prescription medication at nonprescription strength (for medications available in both prescription and non-prescription form, a recommendation by a physician or other licensed health care professional to use a non-prescription medication at prescription strength is considered medical treatment for recordkeeping purposes)"
},
{
"source_type": "regulation",
"ref": "1904.7(b)(5)(ii)(M)",
"quote": "Using massages (physical therapy or chiropractic treatment are considered medical treatment for recordkeeping purposes)"
}
],
"items": [
{
"key": "Ice pack applied on site",
"determination": "no",
"note": "Cold therapy is explicitly listed as first aid at 1904.7(b)(5)(ii)(E). Professional status of provider does not change this classification per 1904.7(b)(5)(iv)."
},
{
"key": "Prescription naproxen",
"determination": "yes",
"note": "Prescription medications are medical treatment beyond first aid. The first-aid list (1904.7(b)(5)(ii)(A)) permits only non-prescription medications at non-prescription strength. Per guidance 7-10a, medical treatment is provided once a prescription is issued, regardless of whether the employee fills it."
},
{
"key": "Referral to physical therapy",
"determination": "yes",
"note": "Physical therapy is explicitly excluded from first aid and is classified as medical treatment per 1904.7(b)(5)(ii)(M): 'physical therapy or chiropractic treatment are considered medical treatment for recordkeeping purposes.' A referral indicates planned beyond-first-aid management."
}
],
"missing_information": [],
"certainty": 0.95,
"catalog_version": "0.20.0",
"request_id": "c169f039-de3c-46aa-8e96-22b0360299ff",
"timestamp": "2026-07-02T00:43:55.835038+00:00"
}
The same case sent to /api/recordability/exceptions returns
likelihood: 0.25 with leaning: "no" and nine items — one per
1904.5(b)(2) exception, each ruled out with a note — plus a
missing_information list naming the facts that would firm up the
answer (for this case: whether the employer controls the parking lot).
That is the inverted factor working as designed: a low likelihood that
any exception applies points toward recordability.
Response fields
| Field | Type | Always present | Description |
|---|---|---|---|
factor | string | Yes | Echoes the factor key you called. |
likelihood | number | Yes | 0–1. Probability of the factor-specific proposition in the factors table — remember exceptions is inverted. |
leaning | string | Yes | yes, no, or unsure — the direction of likelihood, on the same factor-specific proposition. |
reasoning | string | Yes | Cited prose weighing both directions. Grounded only in stored regulation text and official OSHA guidance; every claim cites its source. |
citations | object[] | Yes | Sources the reasoning relies on. At least one, at most 12. |
citations[].source_type | string | Yes | regulation, faq, interpretation, or preamble. |
citations[].ref | string | Yes | Regulation cite (e.g. "1904.5(b)(2)(v)") or an official-guidance identifier (e.g. FAQ "7-10a"). |
citations[].quote | string | null | Yes | Verbatim quotation from the cited source. |
items | object[] | null | No | Per-item determinations where the factor decomposes — see the factors table for what each factor puts here. |
items[].key | string | Yes (per item) | The exception cite, the treatment string, or the analysis-dimension key being determined. |
items[].determination | string | Yes (per item) | yes, no, or unsure. |
items[].note | string | null | Yes (per item) | One-paragraph rationale for this item's determination. |
missing_information | string[] | Yes | The facts that would most reduce uncertainty, in priority order. Empty when the case was sufficient. |
certainty | number | Yes | 0–1. How much reliable information the analysis had to work with — distinct from likelihood. A confident "unsure" and an information-starved "yes" read very differently. |
catalog_version | string | Yes | Version of the regulation and guidance catalog the analysis was grounded in. |
request_id | string | Yes | Correlation ID. Quote in support tickets. |
timestamp | string (ISO 8601 UTC) | Yes | Server time when the response was emitted. |
Status codes
| Code | Meaning | When |
|---|---|---|
200 | OK | Analysis completed. |
400 | ValidationError | Malformed JSON, missing case.incident.description or case.injury.description, a constraint violation, or an unrecognized field. field names the offender. |
400 | DeIdentificationViolation | The body contained an identifier. field names the offending path. |
401 | Unauthenticated | Missing or invalid API key. |
404 | NotFound | {factor} is not one of the five factor keys. |
413 | PayloadTooLarge | Body exceeds the size limit. |
415 | UnsupportedMediaType | Content-Type is not application/json. |
429 | RateLimited | Per-consumer rate limit exceeded. |
500 | InternalError | Unexpected failure. Quote request_id in the ticket. |
502 | UpstreamError | The AI analysis backend failed, or its output failed the outbound safety scan. Retry once; if it persists, report with request_id. |
504 | UpstreamTimeout | The AI analysis did not complete in time. Safe to retry. |
Examples
curl
The canonical example case — a warehouse associate who slipped on ice
in the company parking lot — sent to the treatment factor. The same
body works on all five endpoints.
curl -sS -X POST "https://engine.tripod-health.com/api/recordability/treatment" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Request-ID: docs-recordability-treatment" \
-d '{
"case": {
"incident": {
"description": "Employee slipped on ice in the company parking lot while walking from their parked car to the building entrance at the start of their shift, landing on their right side.",
"incident_date": "2026-06-22",
"reported_date": "2026-06-24",
"location_context": "parking_lot",
"activity_at_time": "walking from personal vehicle to the building entrance before clocking in",
"during_assigned_hours": false
},
"injury": {
"description": "Right shoulder pain and limited range of motion, worsening over two days after the fall.",
"body_part": "right shoulder",
"diagnosis": "rotator cuff strain",
"symptom_onset_date": "2026-06-22",
"onset_type": "acute"
},
"employee": {
"job_title": "warehouse associate",
"job_duties": "Repetitive overhead lifting of boxes up to 50 lbs, pallet loading, operating a manual pallet jack across an 8-hour shift.",
"tenure_bracket": "1-3 years"
},
"treatment": {
"provided": [
"ice pack applied on site",
"prescription naproxen",
"referral to physical therapy"
],
"provider_type": "occupational medicine clinic",
"prescription_issued": true
},
"prior_condition": {
"exists": true,
"description": "Prior right shoulder impingement treated with physical therapy, resolved eight months before this incident.",
"prior_incident_date": "2025-10-15"
},
"statements": [
{
"source": "employee",
"text": "I parked in the employee lot and slipped on a patch of ice near the walkway. I did not think it was serious until the shoulder got worse at work."
},
{
"source": "witness",
"text": "Another employee saw the fall from the entrance and helped the employee up."
}
],
"work_status": {
"days_away": 0,
"restricted_duty": true
}
},
"lens": "safety"
}'
The response is the worked example above.
Python (requests)
# Uses the call() helper from quickstart.md. `body` is the same case
# object as the curl example; only the path segment changes per factor.
result = call("POST", "/api/recordability/treatment", body)
print(f"{result['factor']}: {result['leaning']} ({result['likelihood']:.2f}, certainty {result['certainty']:.2f})")
for item in result.get("items") or []:
print(f" {item['determination']:>6} {item['key']}")
Node.js (native fetch)
// Uses the call() helper from quickstart.md.
const result = await call("POST", "/api/recordability/exceptions", body);
const applied = (result.items ?? []).filter((i) => i.determination === "yes");
console.log(`Exceptions applying: ${applied.length ? applied.map((i) => i.key).join(", ") : "none"}`);
Intended use
- Answering one question well. Classify a treatment list against the first-aid line, rule the nine exceptions in or out, or test the new-case question alone — a single AI analysis, cheaper and faster than the full composite.
- Re-running one factor as facts arrive. A new witness statement or a clinic follow-up changes one factor's inputs; re-call that factor without recomputing the rest.
- Building a factor-by-factor review workflow where a recordkeeping
professional walks each factor's
items,reasoning, and citations before making the determination. - Harvesting
missing_informationto drive an intake follow-up: it lists, in priority order, the facts that would most reduce uncertainty on that factor.
Mistaken use
- Do not treat the response as the recordability determination. It is advisory decision support; a qualified recordkeeping professional makes the call on the OSHA log.
- Need the overall recordability answer? Call
/api/recordability/analyzeonce — it composes the factors into a single assessment — instead of stitching five factor calls together yourself. - Need work-relatedness settled in one call? Use
/api/work_relatedness/analyze, which combines the presumption and the nine exceptions. Callingwork_relatednessandexceptionsseparately gives you two halves you must reconcile yourself. - Do not read
likelihoodthe same way across factors. Onexceptions, a high value argues against recordability. Always interpret it against the proposition in the factors table. - Do not read
certaintyaslikelihood.certaintymeasures how much reliable information the analysis had;likelihoodmeasures the factor's direction.0.9certainty with0.5likelihood is a well-informed toss-up, not a confident answer. - Do not use these endpoints to look up regulation text. Use
/api/regulations/searchand/api/regulations/get— the citations'refvalues slot straight into those lookups.
Debugging
- Capture
request_id. Present in every response body and in theX-Request-IDresponse header. 400 DeIdentificationViolation— thefieldextra names the offending path. Precise dates belong only in the four named event-date fields; names and contact details belong nowhere. Strip the identifier client-side before retrying.400 ValidationErroron a field you didn't send — the body rejects unrecognized fields. Check for typos in field names and for fields at the wrong nesting level.404 NotFound— confirm the final path segment is exactly one of the five factor keys, snake_case included (work_relatedness, notwork-relatedness).502 UpstreamError— retry once. If it persists, file a ticket with therequest_id; the analysis backend or its outbound safety scan is failing on your case.- Low
certaintywith a longmissing_informationlist — the analysis is telling you the case is thin, not that the endpoint is misbehaving. Supply the listed facts and re-call. 429 RateLimited— respect theRetry-Afterresponse header; back off before retrying.
Rate limits
See rate-limits.md for the current per-consumer limits.
Related
- recordability-analyze.md — the full composite recordability assessment across all factors.
- work_relatedness-analyze.md — the presumption and the nine exceptions combined into one work-relatedness call.
- regulations-search.md — find the regulation and guidance text the citations point at.
- regulations-get.md — fetch the full text of a cited section.
- authentication.md, errors.md, rate-limits.md, conventions.md — cross-cutting contract rules.
Change history
| Date | Change |
|---|---|
| 2026-07-01 | Initial publication. |