Loading documentation

Access required

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

Sign out
Skip to main content

Transcript Trim

Status: stable Category: trinityassist Added: 2026-05-26 Last reviewed: 2026-05-26

Summary

Condense one window of raw diarized transcript into a tight clinical-content extract. The engine drops conversational filler -- pleasantries, scheduling chatter, repeated content -- and keeps the medically-relevant material (symptoms, findings, assessment, plan, patient-reported barriers). Intended to run periodically during a live visit so the downstream SOAP draft can read condensed text instead of re-processing every byte of raw transcript on each refresh.

This is the periodic half of TrinityAssist's transcript pipeline: the consumer accumulates raw diarized turns from a live capture, ships one window per tick to this endpoint, and then feeds the trimmed_transcript it gets back into /api/encounters/soap/draft at call_mode: "refresh" (mid-call) or call_mode: "final" (end-of-call wrap-up).

Endpoint

FieldValue
MethodPOST
Path/api/encounters/transcript/trim
Full URLhttps://engine.tripod-health.com/api/encounters/transcript/trim
AuthenticationAPI key (required)
Request bodyapplication/json
Response bodyapplication/json
IdempotentNo (each call may produce a slightly different rewrite)
Rate-limitedYes

Generated text passes the engine's outbound de-identification scan before returning; identifier-shaped output fails with 502 UpstreamError rather than scrubbing silently.

Purpose

Long clinical visits produce long transcripts. Re-sending the entire raw transcript to the SOAP-draft endpoint every few minutes would be expensive, slow, and would push the model to re-read content it already saw. The trim endpoint is a small, cheap condensation pass that runs on the engine's Haiku tier and emits a clinical-content extract one window at a time. The consumer is expected to accumulate the returned extracts (or replace its rolling extract each tick) and hand the result to SOAP draft when a refresh is due.

The engine selects the model. The trim pass always runs on Haiku -- the cost profile is what makes a 5-minute cadence viable. There is no consumer-controlled tier escalation on this endpoint.

Authentication

Standard API-key. Set apikey: $ENGINE_API_KEY on the request. See authentication.md.

Request

Headers

HeaderRequiredDescription
apikeyYesConsumer API key.
Content-TypeYesapplication/json.
X-Request-IDNoOptional client correlation ID. Echoed back in the response.

Body

{
"window_text": "provider: What brings you in today?\npatient: My lower back has been bad for about three weeks since helping my brother move...\nprovider: Numbness or weakness anywhere?\npatient: No, but it tingles down my right calf sometimes.\nprovider: Pain scale?\npatient: Five sitting here, eight driving over.\n...",
"encounter_meta": {
"visit_type": "initial",
"encounter_type": "in_person"
}
}

Body fields

FieldTypeRequiredConstraintsDescription
window_textstringYesnon-empty, ≤ 64,000 charsRaw diarized transcript for one window. Newline-joined speaker: text lines is the expected shape, but any prose with attributable turns is accepted.
encounter_metaobjectYesEncounter context the engine uses to calibrate the trim aggressiveness.
encounter_meta.visit_typeenumYesone of initial, follow_up, touchbase, amendment, finalLets the engine drop more baseline context on follow-ups and keep more on initial visits.
encounter_meta.encounter_typeenumYesone of in_person, telemedicine, telephonicModality of the visit.

The body MUST NOT contain identifier-shaped values. The engine's inbound de-identification posture rejects any payload that carries names, dates of birth, SSN-shaped values, phone numbers, email addresses, MRNs, or similar identifiers. Strip identifiers from the transcript on the consumer side before calling this endpoint. See conventions.md.

A model_tier_hint field is reserved for forward compatibility but is currently ignored. Send it or don't -- the trim pass runs on Haiku regardless.

Response

Success — 200 OK

{
"trimmed_text": "Chief Complaint: Right lower back pain for three weeks, onset after lifting during a move.\n\nHistory of Present Illness: The patient describes a deep ache in the right lower back with sharp, knife-like pain on bending or twisting. Tingling radiates down the right leg, occasionally reaching the calf. Current pain is 5/10 sitting at rest; pain was 8/10 while driving despite ibuprofen use. Sitting exacerbates symptoms; walking is tolerable; lying flat with pillow under knees provides relief. Patient has been on light duty at work (warehouse picker) for one week due to pain. Denies numbness, weakness, or bowel/bladder changes.\n\nPhysical Examination: Forward flexion limited to 30 degrees. Straight leg raise positive on right at 50 degrees. Reflexes 2+ and symmetric. Sensation intact. Motor strength 5/5 throughout.\n\nAssessment: Lumbar strain with right L5 nerve root irritation. No red flags.\n\nPlan: Naproxen 500 mg twice daily with food for two weeks; ice four times daily; physical therapy twice weekly for four weeks focused on McKenzie extension exercises and core stabilization. Continue light duty with no lifting over 20 pounds for two more weeks. Re-evaluation in three weeks. The clinician will have the front desk verify insurance benefits prior to discharge and offered financial-assistance application given patient's concern about PT costs and reduced work hours with high-deductible plan.",
"model_tier": "haiku",
"request_id": "abc-123",
"timestamp": "2026-05-26T03:53:19.504Z"
}

The example above is a real production response from a synthetic initial in-person visit covering three weeks of right lower-back pain with mild radicular signs. The trim pass discarded scheduling chatter and pleasantries, preserved the financial-barrier mention in the Plan section because it changes the care plan, and produced prose in clinical voice with one paragraph per clinical topic.

Response fields

FieldTypeAlways presentDescription
trimmed_textstringYesCondensed clinical-content rewrite of the input window, in clinical voice. Max 8,000 characters. May be short when the window is thin -- the engine does not pad.
model_tierstringYesAlways "haiku" for this endpoint. Returned so callers can confirm tier selection and budget accordingly.
request_idstring | nullYesEcho of the inbound X-Request-ID if present, otherwise null.
timestampstringYesISO 8601 UTC server timestamp at response emission.

Status codes

CodeMeaningWhen
200OKTrim generated.
400ValidationErrorMissing or empty window_text, missing encounter_meta, or wrong type. field names the offender when known.
400DeIdentificationViolationBody carries an identifier-shaped value.
401UnauthenticatedMissing or invalid API key.
413PayloadTooLargeBody exceeds 512 KB.
415UnsupportedMediaTypeContent-Type is not application/json.
422UnprocessableEntityBody parsed but a downstream validation rule rejected it (e.g. unknown encounter_type enum value).
429RateLimitedPer-consumer rate limit exceeded.
500InternalErrorUnexpected failure.
502UpstreamErrorUpstream model call failed, or generated text failed the outbound de-id pass.
503ServiceUnavailableService temporarily unhealthy.
504UpstreamTimeoutUpstream model call exceeded the engine's trim budget.

Error envelope

{
"timestamp": "2026-05-26T03:56:27.691Z",
"error": "ValidationError",
"message": "Field 'window_text' must be a non-empty string",
"field": "window_text",
"request_id": "abc-123"
}

See errors.md for the full error code list.

Examples

curl — success

curl -sS -X POST "https://engine.tripod-health.com/api/encounters/transcript/trim" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"window_text": "provider: What brings you in today?\npatient: My lower back has been bad for about three weeks since helping my brother move. Deep ache, sharp on bending, sometimes tingles to the right calf.\nprovider: Numbness or weakness? Bowel or bladder issues?\npatient: No.\nprovider: Pain scale right now?\npatient: Five sitting, eight driving over.\nprovider: Better or worse with what?\npatient: Sitting kills it. Walking is fine. Lying flat with a pillow under my knees helps.\nprovider: Exam shows forward flexion to thirty degrees, right SLR positive at fifty, reflexes two plus symmetric, sensation intact, motor five over five.\nprovider: Lumbar strain with right L5 irritation. Naproxen five hundred twice daily for two weeks, ice four times a day, PT twice a week for four weeks on McKenzie extension and core stabilization. Light duty two more weeks, no lifting over twenty pounds. Back in three weeks.",
"encounter_meta": { "visit_type": "initial", "encounter_type": "in_person" }
}'

curl — validation error

curl -sS -X POST "https://engine.tripod-health.com/api/encounters/transcript/trim" \
-H "apikey: $ENGINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"window_text":"","encounter_meta":{"visit_type":"initial","encounter_type":"in_person"}}'

Expected response (400):

{
"timestamp": "2026-05-26T03:56:27.691Z",
"error": "ValidationError",
"message": "Field 'window_text' must be a non-empty string",
"field": "window_text",
"request_id": "abc-123"
}

Python — requests

import os, requests

resp = requests.post(
"https://engine.tripod-health.com/api/encounters/transcript/trim",
headers={"apikey": os.environ["ENGINE_API_KEY"]},
json={
"window_text": window_text,
"encounter_meta": {"visit_type": "initial", "encounter_type": "in_person"},
},
timeout=60,
)
resp.raise_for_status()
trimmed = resp.json()["trimmed_text"]

Node — fetch

const resp = await fetch(
"https://engine.tripod-health.com/api/encounters/transcript/trim",
{
method: "POST",
headers: {
apikey: process.env.ENGINE_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
window_text: windowText,
encounter_meta: { visit_type: "initial", encounter_type: "in_person" },
}),
},
);
if (!resp.ok) throw new Error(`Engine ${resp.status}: ${await resp.text()}`);
const { trimmed_text } = await resp.json();

Intended use

  • Periodic condensation during a live capture. The expected pattern is one call per visit per tick on a roughly 5-minute cadence, shipping whatever transcript has accumulated since the last tick.
  • Feeding the result into /api/encounters/soap/draft as trimmed_transcript when calling with call_mode: "refresh" or call_mode: "final".
  • Bounded keepalive of clinical content. Because the engine never persists transcripts beyond the call, this endpoint is also the consumer's mechanism to compact a long visit's transcript into a size the SOAP-draft prompt can carry.

Mistaken use

  • Calling this endpoint with the full encounter transcript at the end of the visit instead of using SOAP draft's call_mode: "final" with the accumulated trim text. The trim endpoint emits a tight extract, not a SOAP note -- it has no Subjective / Objective / Assessment / Plan structure and no ICD-10 candidates.
  • Persisting trimmed_text as the clinical record. The trim pass is an intermediate condensation step, not a signed note. The signed artifact is the clinician-edited SOAP draft.
  • Sending raw patient identifiers in window_text. The transcript must be de-identified before it reaches this endpoint.
  • Trying to escalate the model with model_tier_hint. The engine picks the tier; the hint is accepted for forward-compatibility but ignored. Trim always runs on Haiku.
  • Treating the output as deterministic. The model produces a fresh rewrite each call; even identical input may differ on rerun.

Debugging

  1. Capture the request_id from the response (success or error) or the X-Request-ID response header. Every support ticket starts here.
  2. 400 ValidationError -- the field extra names the offender. Most often window_text is empty or encounter_meta is missing.
  3. 422 UnprocessableEntity -- a downstream enum or shape constraint was violated (e.g. encounter_type: "clinic" is not a valid value). The engine surfaces this as a generic UnprocessableEntity envelope today; check the request body against the schema in this doc before retrying.
  4. 502 UpstreamError -- either the model call failed, or the generated text failed the outbound de-id scan. The latter indicates the upstream model produced an identifier-shaped string from a window that already contained one. Strip identifiers from the input transcript and retry.
  5. 504 UpstreamTimeout -- the trim took too long. Most likely the window is too large; trim shorter windows. The engine's internal budget for this endpoint is 25 s.

Rate limits

Per-consumer limits are enforced at the gateway. See rate-limits.md.

  • POST /api/encounters/soap/draft -- consumes the trimmed_text from this endpoint as trimmed_transcript during periodic refresh and end-of-call final-draft passes.
  • conventions.md -- de-identification contract, request IDs, timestamp shape.
  • errors.md -- error envelope and code list.

Change history

DateChange
2026-05-26Initial publication.