Loading documentation

Access required

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

Sign out
Skip to main content

API conventions

Status: stable Category: reference Added: 2026-04-20 Last reviewed: 2026-09-16

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_id for 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's catalog_version changes; 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.

MethodUsed for
GETReads that have no side effects: health, version, catalog lookups. Idempotent.
POSTScoring 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:

HeaderRequiredPurpose
apikeyYesConsumer API key. See authentication.md.
Content-TypeOn requests with a bodyMust be application/json. Anything else returns 415 UnsupportedMediaType.

Body​

  • JSON only. No XML, no application/x-www-form-urlencoded, no multipart/form-data.
  • UTF-8.
  • Maximum size 512KB. A larger body returns 413 PayloadTooLarge. No endpoint needs a body anywhere near this ceiling.
  • Field names use snake_case. Example: age_bracket, not ageBracket.

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​

The engine assigns every request its own UUID and returns it as request_id in the body — in 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. You cannot choose the ID: an X-Request-ID header you send is not used. Keep your own correlation IDs on your side.

Every response also carries an X-Request-ID response header. That header is the gateway's trace ID for the same request, not the engine's ID, and it never matches request_id. A response the gateway generates itself — a 401 for a missing or invalid key, a 404 for a path that is not an API route, a 429 for a limit enforced at the gateway — never reached the engine: its body is the gateway's own shape, any request_id in it is 32 hex characters with no dashes, and the header is the only ID it carries.

Quote the body's request_id whenever you contact support about a specific call. Quoting the X-Request-ID header also works — support can find the call from either — and it is the only option for a gateway-generated error or a success body with no request_id.

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 (Z suffix), 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": 67.63, "band": "bad", "bank_version": "2026-09-15-standard-ladder" },
"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​

CategoryExamples
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 patternsSSN 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 date exceptions​

Four fields are allowed to carry a precise date, and they are named explicitly rather than matched by shape:

FieldWhere it is accepted
incident_dateOSHA recordability analysis
prior_incident_dateOSHA recordability analysis
reported_dateOSHA recordability analysis
symptom_onset_dateOSHA recordability analysis

A date-shaped value in any other field, at any depth, is rejected — including a field you might reasonably expect to take one. The CDRI surface takes no dates at all: on /api/cdri/compute the case history is sent as day counts — case.injury_days_ago and case.completed_visit_days_ago — so there is no date for the engine to convert or discard. A duration is not a Safe Harbor identifier; a date is.

:::caution The rejection message says "date of birth" Any unexpected date-shaped value is reported as appears to be a precise date of birth (send age_bracket instead), because that is the identifier the check exists to catch. If you send a date in a field that is not in the table above — an injury onset, for example — that is the message you get, and the field in the envelope is what tells you which value to replace. Send a day count instead. :::

What to send instead​

Identifier you'd naively sendSend this instead
Date of birthdemographics.age_bracket (for example "35-44")
Legal nameNothing — Trinity doesn't need a name to produce a score
SSN or MRNNothing — Trinity does not correlate across calls by subject
Email / phoneNothing — see above
Street addressdemographics.region (state/region granularity)
NAICS code + job titledemographics.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/get is canonical; /api/catalog/get/ returns 404.

Idempotency​

  • GET calls are idempotent and safe to retry on any retriable failure status.
  • POST calls (/score, /cdri/compute) are computationally repeatable but not idempotent — each call is treated as a new request server-side. Retrying a 5xx on POST is correct behavior. There is no client-supplied idempotency key today.

Request size guidance​

  • A /score body for the longest instrument in the catalog is a few hundred bytes.
  • A /cdri/compute body carrying the whole question bank answered, plus context and case history, 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.

Change history​

DateChange
2026-04-20Initial publication.
2026-06-03Removed the tpa audience value; documented the onset/as_of date exception for the acuity surface.
2026-09-16Replaced the onset/as_of date exception with the four recordability date fields, which are now the only ones accepted; the CDRI surface takes day counts and no dates at all.
2026-10-14The engine assigns every request ID; an X-Request-ID request header is not used. The X-Request-ID response header is the gateway's trace ID for the request and does not match request_id; quote request_id to support.