Regulations Tree
Status:
stableCategory:regulationsAdded:2026-07-01Last reviewed:2026-07-01
Summary
Returns one CFR part as a nested subpart/section tree, in document
order. Use it to build a browse or navigation surface over a part —
each leaf entry carries the section number, its official heading, and a
stable id, so your application can render the part's structure and
fetch individual sections selectively via
/api/regulations/get.
The tree carries headings only, never section text. It answers "what is in this part and in what order," not "what does this section say."
Endpoint
| Field | Value |
|---|---|
| Method | GET |
| Path | /api/regulations/tree |
| Full URL | https://engine.tripod-health.com/api/regulations/tree |
| Authentication | API key (required) |
| Request body | none |
| Response body | application/json |
| Idempotent | Yes |
| Rate-limited | Yes |
Authentication
Send a valid API key in the apikey header. See
authentication.md.
Request
Headers
| Header | Required | Description |
|---|---|---|
apikey | Yes | Consumer API key. |
X-Request-ID | No | Optional correlation ID, echoed in the response body's request_id and the X-Request-ID response header. |
Path parameters
None.
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
title | integer | Yes | — | CFR title number, 1–50. OSHA recordkeeping lives under title 29. |
part | string | Yes | — | CFR part number within the title, e.g. 1904. Treat as opaque text. |
Both parameters are required. Omitting or malforming either returns
400 ValidationError naming the offending parameter.
Body
None.
Response
Success — 200 OK
The response is the whole part as one nested object: the part's
heading, then one node per subpart in document order, each holding its
leaf entries in document order. Leaf entries are either regulation
sections or appendices, distinguished by type.
The example below is a real capture of 29 CFR part 1904,
abbreviated for readability: it shows the first eight leaf entries
(subparts A and B in full, the start of subpart C) and the last two
(the end of subpart F, and subpart G). The full response contains every
subpart (A through G) and every section of the part.
{
"cfr_title": 29,
"part": "1904",
"heading": "PART 1904—RECORDING AND REPORTING OCCUPATIONAL INJURIES AND ILLNESSES",
"children": [
{
"type": "subpart",
"subpart": "A",
"heading": "Subpart A—Purpose",
"children": [
{ "type": "regulation", "id": 102434, "section": "1904.0", "heading": "Purpose." }
]
},
{
"type": "subpart",
"subpart": "B",
"heading": "Subpart B—Scope",
"children": [
{ "type": "regulation", "id": 102436, "section": "1904.1", "heading": "Partial exemption for employers with 10 or fewer employees." },
{ "type": "regulation", "id": 102437, "section": "1904.2", "heading": "Partial exemption for establishments in certain industries." },
{ "type": "regulation", "id": 102438, "section": "1904.3", "heading": "Keeping records for more than one agency." },
{ "type": "appendix", "id": 102439, "section": "Non-Mandatory Appendix A to Subpart B of Part 1904", "heading": "Non-Mandatory Appendix A to Subpart B of Part 1904—Partially Exempt Industries" }
]
},
{
"type": "subpart",
"subpart": "C",
"heading": "Subpart C—Recordkeeping Forms and Recording Criteria",
"children": [
{ "type": "regulation", "id": 102441, "section": "1904.4", "heading": "Recording criteria." },
{ "type": "regulation", "id": 102442, "section": "1904.5", "heading": "Determination of work-relatedness." },
{ "type": "regulation", "id": 102443, "section": "1904.6", "heading": "Determination of new cases." }
]
},
{
"type": "subpart",
"subpart": "F",
"heading": "Subpart F—Transition From the Former Rule",
"children": [
{ "type": "regulation", "id": 102471, "section": "1904.45", "heading": "OMB control numbers under the Paperwork Reduction Act" }
]
},
{
"type": "subpart",
"subpart": "G",
"heading": "Subpart G—Definitions",
"children": [
{ "type": "regulation", "id": 102473, "section": "1904.46", "heading": "Definitions." }
]
}
],
"request_id": "ab861045-5af0-4878-b4ab-7b0a7565e7be",
"timestamp": "2026-07-02T00:42:48.636Z"
}
Response fields
| Field | Type | Always present | Description |
|---|---|---|---|
cfr_title | integer | Yes | Echoes the requested title. |
part | string | Yes | Echoes the requested part. |
heading | string | Yes | Official part heading as published in the CFR. |
children | array | Yes | Subpart nodes in document order. |
children[].type | string | Yes | Always subpart at this level. |
children[].subpart | string | Yes | Subpart letter, e.g. "C". |
children[].heading | string | Yes | Official subpart heading. |
children[].children | array | Yes | Leaf entries within the subpart, in document order. |
children[].children[].type | string | Yes | regulation or appendix. |
children[].children[].id | integer | Yes | Stable identifier for the entry. Use it to enumerate what to fetch via /api/regulations/get. |
children[].children[].section | string | Yes | Section number (e.g. "1904.5"), a reserved range (e.g. "1904.13-1904.28"), or the full appendix designation. |
children[].children[].heading | string | Yes | Official heading of the section or appendix. Headings only — the tree never carries section text. |
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 | Part found; tree returned. |
400 | ValidationError | title missing or not an integer 1–50, or part missing or malformed. field names the offender. |
401 | Unauthenticated | Missing or invalid API key. |
404 | NotFound | No part matches (title, part). |
429 | RateLimited | Per-consumer rate limit exceeded. |
500 | InternalError | Unexpected failure. Quote request_id in the ticket. |
Examples
curl
curl -sS "https://engine.tripod-health.com/api/regulations/tree?title=29&part=1904" \
-H "apikey: $ENGINE_API_KEY"
Python (requests)
# Uses the call() helper from quickstart.md.
tree = call("GET", "/api/regulations/tree?title=29&part=1904")
for subpart in tree["children"]:
print(subpart["heading"])
for entry in subpart["children"]:
print(f" {entry['section']}: {entry['heading']}")
Node.js (native fetch)
// Uses the call() helper from quickstart.md.
const tree = await call("GET", "/api/regulations/tree?title=29&part=1904");
const leaves = tree.children.flatMap((s) => s.children);
console.log(`${tree.part}: ${leaves.length} entries`);
Error scenarios
Missing parameter
Send:
curl -sS "https://engine.tripod-health.com/api/regulations/tree?title=29" \
-H "apikey: $ENGINE_API_KEY" \
-H "X-Request-ID: docs-regulations-tree-missing-part" \
-w "\nHTTP %{http_code}\n"
Receive (400):
{
"timestamp": "2026-07-01T17:32:10.812Z",
"error": "ValidationError",
"message": "Query parameter 'part' is required",
"request_id": "docs-regulations-tree-missing-part",
"field": "part"
}
Unknown part
Send:
curl -sS "https://engine.tripod-health.com/api/regulations/tree?title=29&part=9999" \
-H "apikey: $ENGINE_API_KEY" \
-H "X-Request-ID: docs-regulations-tree-unknown-part" \
-w "\nHTTP %{http_code}\n"
Receive (404):
{
"timestamp": "2026-07-01T17:32:10.812Z",
"error": "NotFound",
"message": "No part '9999' under title 29",
"request_id": "docs-regulations-tree-unknown-part"
}
Intended use
- Building a browse/navigation UI over a part — the tree is already in document order and grouped by subpart, so it renders directly as a sidebar or table of contents.
- Enumerating a part's sections to fetch selectively. Walk the
leaves, pick the sections you need, and pull their full text via
/api/regulations/get— one tree call replaces guessing at section numbers. - Detecting appendices. Appendices do not follow the
part.sectionnumbering scheme; the tree is the reliable way to discover they exist and what they are called.
Mistaken use
- Do not use the tree for text search. The tree matches nothing —
it returns structure. Use
/api/regulations/searchto find sections by content. - Do not expect section text. Leaf entries carry headings only. Use
/api/regulations/getto fetch the full text of a section or appendix. - Do not parse
sectionas a number. Values include reserved ranges ("1904.13-1904.28") and full appendix designations. Treat the field as opaque display text and useidfor lookups.
Debugging
- Capture
request_id. Present in every response body and in theX-Request-IDresponse header. 400 ValidationError— bothtitleandpartare required; thefieldextra names the missing or malformed one.titlemust be an integer1–50.404 NotFoundfor a part you're sure exists — confirm thetitleis right. The same part number can exist under one title and not another.- Fewer leaves than you expected — reserved section ranges are
collapsed into a single entry (e.g.
"1904.13-1904.28"), so the leaf count is lower than the raw section-number span.
Rate limits
See rate-limits.md for the current per-consumer limits.
Related
- regulations-search.md — find sections by text content.
- regulations-get.md — fetch the full text of one section or appendix.
- authentication.md, errors.md, rate-limits.md, conventions.md — cross-cutting contract rules.
Change history
| Date | Change |
|---|---|
| 2026-07-01 | Initial publication. |