API conventions
Status:
stableCategory:referenceAdded:2026-04-20Last reviewed:2026-06-03
Summary
Cross-cutting rules that hold across every endpoint in this directory. An integrator who reads this once can skim individual endpoint docs without re-learning the same conventions on each page.
- Base URL is
https://engine.tripod-health.com. - JSON only, UTF-8, over HTTPS.
- Paths are unversioned (no
/v1/, no/v2/). - Every response carries a
request_idfor correlation. - Timestamps are ISO 8601 UTC with millisecond precision.
- Success responses have no envelope — the payload is at the top level. Errors use a uniform envelope (see errors.md).
- Request bodies must be de-identified. Trinity rejects identifiers at the boundary; they do not pass into business logic.
Base URL
Every public endpoint is reached at https://engine.tripod-health.com.
HTTP is redirected to HTTPS with a 301. Your client should call the
https:// URL directly.
No other hostname belongs in your integration. If you see a different URL in a document or a log line, treat it as an internal detail that is not part of the public contract.
Paths
- All consumer-facing routes live under
/api/. Example:/api/cdri/compute. - The one exception is
/health, the unauthenticated container liveness probe — not reachable via the public hostname. - Paths are not versioned. It is
/api/cdri/compute, never/api/v1/cdri/compute. When a contract changes, the catalog'scatalog_versionchanges; the URL does not. - Path segments are lowercase. Multi-word resources use snake_case
(
depression_screen,quick_dash).
HTTP methods
Only GET and POST are in use today. The catalog names which
method each endpoint uses — see
GET /api/catalog/operations.
| Method | Used for |
|---|---|
GET | Reads that have no side effects: health, version, catalog lookups, /questions listings. Idempotent. |
POST | Scoring and composite computation: /score, /cdri/compute, /debug/echo. Each call is an independent computation with a persistent audit record. |
PUT, PATCH, and DELETE are not used. A request with one of
these methods against a known path returns 404 NotFound.
Request format
Headers
Every authenticated request needs:
| Header | Required | Purpose |
|---|---|---|
apikey | Yes | Consumer API key. See authentication.md. |
Content-Type | On requests with a body | Must be application/json. Anything else returns 415 UnsupportedMediaType. |
X-Request-ID | No | Client-supplied correlation ID (up to 128 characters). If omitted, Trinity generates a UUID. |
Body
- JSON only. No XML, no
application/x-www-form-urlencoded, nomultipart/form-data. - UTF-8.
- Maximum size
512KB. A larger body returns413 PayloadTooLarge. No endpoint needs a body anywhere near this ceiling. - Field names use
snake_case. Example:age_bracket, notageBracket.
Query parameters
Case-sensitive. Where an endpoint enumerates allowed values (for
example ?depth=basic|contextual|full or
?audience=employee|employer|provider), the values are all
lowercase and must match exactly.
Response format
Content type
Every response is application/json; charset=utf-8.
401 bodies use a smaller envelope — see
authentication.md.
Success shape
No envelope. The payload is at the top level:
{
"score": 72,
"band": "high",
"confidence": { "overall": 0.84, "tier": "high" },
"catalog_version": "0.13.0",
"request_id": "8f2c3b1d-5e4a-4a9c-9b1e-2a7f1d3e4c5b",
"timestamp": "2026-04-20T17:32:10.812Z"
}
Three fields appear on most (not all) successful business responses:
catalog_version, request_id, and timestamp. Health and version
responses structure those fields differently; see the individual
endpoint docs. Purely informational reads such as catalog lookups may
omit them when they add no value.
Error shape
Uniform envelope:
{
"timestamp": "2026-04-20T17:32:10.812Z",
"error": "ValidationError",
"message": "Field 'depth' must be one of basic, contextual, full",
"request_id": "...",
"field": "depth"
}
Every non-2xx response Trinity emits follows this shape. See
errors.md for the full taxonomy.
Request IDs
Every /api/* response carries a request ID. It is always present in
the X-Request-ID response header. It is also echoed in the response
body on most endpoints — every error envelope, and every computation
response (/score, /cdri/compute, /health/check, /debug/echo).
Pure-lookup endpoints such as /catalog/* and /version/get return
only the business payload in the body; use the response header there.
Rules:
- If you send an
X-Request-IDrequest header, up to 128 characters, Trinity uses it verbatim. - If you do not, or yours is empty or longer than 128 characters, the Trinity generates a UUID v4.
- IDs are echoed but not otherwise interpreted. Client-supplied IDs are opaque strings; Trinity does not require UUID format from clients.
- Every request gets a distinct ID. Do not reuse IDs across calls.
Quote the request ID whenever you contact support about a specific call. It is the fastest path from a symptom to a server-side trace.
Timestamps
All timestamps — in response bodies and in log lines — are ISO 8601 UTC with millisecond precision. Example:
2026-04-20T17:32:10.812Z
- Always UTC (
Zsuffix), never a local offset. - Always millisecond-resolution (
.812). - Never a Unix epoch integer in Trinity's public contract.
Parse with any standard ISO 8601 parser:
from datetime import datetime
ts = datetime.fromisoformat("2026-04-20T17:32:10.812Z".replace("Z", "+00:00"))
const ts = new Date("2026-04-20T17:32:10.812Z");
Null vs. missing
When a field is documented as "always present," it is present on every
response — explicit null when the value is unknown, never omitted.
When a field is documented as optional, it may be omitted entirely
from the response.
Example — a composite that could not classify a phenotype:
{
"cdri_score": 68,
"phenotype": null,
"confidence": { "overall": 0.54, "tier": "medium" }
}
phenotype: null is correct; phenotype being absent from the
response entirely is a bug worth reporting.
Client-side defensive reading: for an always-present field, read it
directly and handle null explicitly. Do not test for presence on
fields the doc says are always present.
De-identification boundary
Trinity is a de-identification contract. Request bodies are
scanned on the way in; anything that looks like an identifier is
rejected with 400 DeIdentificationViolation before business logic
runs.
What Trinity rejects
| Category | Examples |
|---|---|
| Blocked field names (any depth in the body) | name, first_name, last_name, full_name, ssn, dob, date_of_birth, email, phone, mrn, patient_id, member_id |
| Value patterns | SSN shape (\d{3}-\d{2}-\d{4}), email shape, phone-number shape, precise past-dated ISO date of birth |
The first hit wins. Trinity returns field naming the offending
path; the value itself is never echoed.
The one date exception: onset / as_of
The acuity inputs onset and (optionally) as_of — accepted by
/api/acuity/compute and by
/api/cdri/compute when you request the
embedded acuity view — are the only fields allowed to carry a date.
The engine converts them into a duration (weeks-since-onset) at the
request boundary and discards the raw date: it is never persisted or
logged. A duration is not a Safe Harbor identifier; a date is. A
date-shaped value anywhere other than onset/as_of is still
rejected.
What to send instead
| Identifier you'd naively send | Send this instead |
|---|---|
| Date of birth | demographics.age_bracket (for example "35-44") |
| Legal name | Nothing — Trinity doesn't need a name to produce a score |
| SSN or MRN | Nothing — Trinity does not correlate across calls by subject |
| Email / phone | Nothing — see above |
| Street address | demographics.region (state/region granularity) |
| NAICS code + job title | demographics.occupation_class |
If your workflow requires correlating multiple calls back to one subject, generate an opaque token on your side, pass it through whatever channel your pipeline uses, and re-associate it after the request. Trinity never sees the subject identity.
URL patterns
- One resource per path segment. No query-string-only endpoints.
- Verb-like paths at the tail (
/questions,/score,/compute,/check,/get) clarify the operation even when the HTTP method alone would. - No trailing slashes.
/api/catalog/getis canonical;/api/catalog/get/returns404.
Idempotency
GETcalls are idempotent and safe to retry on any retriable failure status.POSTcalls (/score,/cdri/compute) are computationally repeatable but not idempotent — each call is treated as a new request server-side. Retrying a5xxonPOSTis correct behavior. There is no client-supplied idempotency key today.
Request size guidance
- A
/scorebody for the longest instrument in the catalog is a few hundred bytes. - A
/cdri/computebody carrying every instrument's responses and demographics is typically well under 10 KB. - If your request approaches 100 KB, review what you are sending — no endpoint requires bodies that large, and you may be accidentally including free-form content that will trip the de-identification filter.
Related
- authentication.md — the
apikeyheader and401behavior. - errors.md — full error taxonomy and envelope shape.
- rate-limits.md — headers and backoff.
Change history
| Date | Change |
|---|---|
| 2026-04-20 | Initial publication. |
| 2026-06-03 | Removed the tpa audience value; documented the onset/as_of date exception for the acuity surface. |