hub / content /engineering /api.html
amirmh's picture
Deploy Travi Company Hub β€” rebuilt from monolithic hub.html (79 docs, lazy-loaded partials, static space)
5a79992 verified
Raw
History Blame Contribute Delete
21.1 kB
<article class="doc" id="doc-engineering-api" aria-hidden="true">
<div class="strip">
<span class="tag">Engineering</span>
<span class="sep">&middot;</span>
<span>API Reference</span>
</div>
<h1>API Reference &amp; Integration Spec</h1>
<p style="font-size:15px;color:var(--slate);margin-bottom:28px;max-width:700px;line-height:1.6;">
A lightweight integration specification covering the FHIR R4 endpoints Travi consumes and exposes, authentication flow, webhook contracts, and a self-assessment checklist for hospital IT teams evaluating integration complexity.
</p>
<!-- ── AUTH FLOW ─────────────────────────────────────────────────────────── -->
<h2 id="api-auth">Authentication</h2>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:18px 20px;margin:16px 0 24px;">
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;">
<div>
<div style="font-size:11px;font-weight:700;color:#38bdf8;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;">SMART on FHIR 2.0</div>
<p style="font-size:13px;color:var(--slate);line-height:1.6;margin:0;">Travi registers as a SMART on FHIR app in the hospital's EHR App Gallery. OAuth 2.0 authorization code flow with PKCE. Scopes requested: <code>patient/*.read</code>, <code>user/Task.write</code>, <code>user/Communication.write</code>, <code>user/ClinicalImpression.write</code>.</p>
</div>
<div>
<div style="font-size:11px;font-weight:700;color:#38bdf8;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;">JWT Bearer Tokens</div>
<p style="font-size:13px;color:var(--slate);line-height:1.6;margin:0;">All Travi API endpoints require a signed JWT bearer token. Tokens are scoped per hospital, expire in 1 hour, and are rotated automatically. Hospital IT teams receive a client ID and client secret during onboarding β€” no API keys are shared with clinical staff.</p>
</div>
<div>
<div style="font-size:11px;font-weight:700;color:#38bdf8;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;">Mutual TLS (mTLS)</div>
<p style="font-size:13px;color:var(--slate);line-height:1.6;margin:0;">For hospitals that require it, Travi supports mTLS on the FHIR ingest endpoint. The hospital provides a client certificate during onboarding; Travi pins the certificate and rejects connections from any other source.</p>
</div>
</div>
</div>
<!-- ── INBOUND ENDPOINTS (what Travi reads from the EHR) ─────────────────── -->
<h2 id="api-inbound">Inbound: FHIR Resources Travi Reads</h2>
<div style="overflow-x:auto;-webkit-overflow-scrolling:touch;margin:16px 0 28px;">
<table style="width:100%;border-collapse:collapse;min-width:580px;">
<thead>
<tr style="background:var(--surface);border-bottom:2px solid var(--border);">
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">FHIR Resource</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Trigger</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Key Fields Used</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Required</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid var(--border);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Encounter</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">status = <code>finished</code>, class = <code>IMP</code> (inpatient)</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">period.end, subject, hospitalization.dischargeDisposition</td>
<td style="padding:10px 14px;"><span style="background:rgba(239,68,68,0.12);color:#f87171;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;">YES</span></td>
</tr>
<tr style="border-bottom:1px solid var(--border);background:rgba(255,255,255,0.015);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Patient</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Pulled on discharge event</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">name, telecom (phone), birthDate, address, language</td>
<td style="padding:10px 14px;"><span style="background:rgba(239,68,68,0.12);color:#f87171;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;">YES</span></td>
</tr>
<tr style="border-bottom:1px solid var(--border);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">MedicationRequest</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">status = <code>active</code>, linked to Encounter</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">medication.code, dosageInstruction, dispenseRequest.numberOfRepeatsAllowed</td>
<td style="padding:10px 14px;"><span style="background:rgba(239,68,68,0.12);color:#f87171;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;">YES</span></td>
</tr>
<tr style="border-bottom:1px solid var(--border);background:rgba(255,255,255,0.015);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">DocumentReference</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">type = discharge summary (LOINC 18842-5)</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">content.attachment (base64 or URL), context.encounter</td>
<td style="padding:10px 14px;"><span style="background:rgba(239,68,68,0.12);color:#f87171;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;">YES</span></td>
</tr>
<tr style="border-bottom:1px solid var(--border);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Appointment</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">status = <code>booked</code>, linked to Patient</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">start, end, participant, serviceType</td>
<td style="padding:10px 14px;"><span style="background:rgba(245,158,11,0.12);color:#fbbf24;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;">OPTIONAL</span></td>
</tr>
<tr style="border-bottom:1px solid var(--border);background:rgba(255,255,255,0.015);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Observation</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">category = vital-signs or laboratory, last 30 days</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">code (LOINC), valueQuantity, effectiveDateTime</td>
<td style="padding:10px 14px;"><span style="background:rgba(245,158,11,0.12);color:#fbbf24;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;">OPTIONAL</span></td>
</tr>
<tr>
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Condition</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">clinicalStatus = <code>active</code>, linked to Patient</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">code (ICD-10), severity, onsetDateTime</td>
<td style="padding:10px 14px;"><span style="background:rgba(245,158,11,0.12);color:#fbbf24;font-size:11px;font-weight:700;padding:2px 8px;border-radius:4px;">OPTIONAL</span></td>
</tr>
</tbody>
</table>
</div>
<!-- ── OUTBOUND ENDPOINTS (what Travi writes back) ────────────────────────── -->
<h2 id="api-outbound">Outbound: FHIR Resources Travi Writes</h2>
<div style="overflow-x:auto;-webkit-overflow-scrolling:touch;margin:16px 0 28px;">
<table style="width:100%;border-collapse:collapse;min-width:580px;">
<thead>
<tr style="background:var(--surface);border-bottom:2px solid var(--border);">
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">FHIR Resource</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Purpose</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Trigger</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Frequency</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid var(--border);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Task</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Care plan task status (completed, in-progress, failed)</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Patient SMS response or daily batch</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Daily or on event</td>
</tr>
<tr style="border-bottom:1px solid var(--border);background:rgba(255,255,255,0.015);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Communication</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Log of all patient-Travi SMS interactions</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Each SMS sent or received</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Real-time</td>
</tr>
<tr style="border-bottom:1px solid var(--border);">
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">ClinicalImpression</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">AI-generated risk assessment with score and rationale</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Risk score threshold crossed or daily</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Daily + on escalation</td>
</tr>
<tr>
<td style="padding:10px 14px;font-size:13px;font-weight:600;color:var(--ink);font-family:monospace;">Flag</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">High-risk patient alert visible in EHR patient banner</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Escalation engine fires</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">On escalation only</td>
</tr>
</tbody>
</table>
</div>
<!-- ── WEBHOOK CONTRACT ────────────────────────────────────────────────────── -->
<h2 id="api-webhooks">Webhook Contract</h2>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:18px 20px;margin:16px 0 24px;">
<p style="font-size:13px;color:var(--slate);line-height:1.6;margin:0 0 14px;">Travi exposes a single inbound webhook endpoint for EHR systems that push rather than allow polling:</p>
<pre style="background:rgba(0,0,0,0.3);border:1px solid var(--border);border-radius:8px;padding:14px 16px;overflow-x:auto;margin:0 0 14px;"><code style="font-size:12px;color:#e2e8f0;font-family:'JetBrains Mono',monospace;">POST https://api.travi.ai/v1/fhir/webhook
Authorization: Bearer &lt;hospital_jwt&gt;
Content-Type: application/fhir+json
X-Travi-Signature: sha256=&lt;hmac_signature&gt;
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{ "resource": { "resourceType": "Encounter", ... } },
{ "resource": { "resourceType": "Patient", ... } }
]
}</code></pre>
<p style="font-size:13px;color:var(--slate);line-height:1.6;margin:0;">All webhook payloads are HMAC-SHA256 signed using the hospital's shared secret. Travi validates the signature before processing. Idempotency key: <code>Encounter.id</code> β€” duplicate discharge events for the same encounter are deduplicated automatically. Response: <code>202 Accepted</code> within 200ms; processing is async.</p>
</div>
<!-- ── ANALYTICS API ───────────────────────────────────────────────────────── -->
<h2 id="api-analytics">Analytics API (Hospital Read-Only)</h2>
<div style="overflow-x:auto;-webkit-overflow-scrolling:touch;margin:16px 0 28px;">
<table style="width:100%;border-collapse:collapse;min-width:540px;">
<thead>
<tr style="background:var(--surface);border-bottom:2px solid var(--border);">
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Endpoint</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Returns</th>
<th style="padding:10px 14px;text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--slate);">Auth</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid var(--border);">
<td style="padding:10px 14px;font-size:12.5px;font-family:monospace;color:var(--ink);">GET /v1/analytics/readmissions</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">30-day readmission rate by cohort, DRG, and month</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Hospital admin JWT</td>
</tr>
<tr style="border-bottom:1px solid var(--border);background:rgba(255,255,255,0.015);">
<td style="padding:10px 14px;font-size:12.5px;font-family:monospace;color:var(--ink);">GET /v1/analytics/adherence</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Medication fill rate, appointment attendance, task completion by cohort</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Hospital admin JWT</td>
</tr>
<tr style="border-bottom:1px solid var(--border);">
<td style="padding:10px 14px;font-size:12.5px;font-family:monospace;color:var(--ink);">GET /v1/analytics/hrrp</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Projected HRRP penalty reduction and estimated dollar savings</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Hospital admin JWT</td>
</tr>
<tr style="border-bottom:1px solid var(--border);background:rgba(255,255,255,0.015);">
<td style="padding:10px 14px;font-size:12.5px;font-family:monospace;color:var(--ink);">GET /v1/analytics/tcm-billing</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">TCM-eligible encounters, billed vs. unbilled, CPT code breakdown</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Hospital admin JWT</td>
</tr>
<tr>
<td style="padding:10px 14px;font-size:12.5px;font-family:monospace;color:var(--ink);">GET /v1/patients/{id}/timeline</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Full 30-day task and interaction timeline for a single patient</td>
<td style="padding:10px 14px;font-size:13px;color:var(--slate);">Care coordinator JWT (own cohort only)</td>
</tr>
</tbody>
</table>
</div>
<!-- ── IT SELF-ASSESSMENT CHECKLIST ───────────────────────────────────────── -->
<h2 id="api-checklist">Hospital IT Self-Assessment Checklist</h2>
<p style="font-size:13.5px;color:var(--slate);margin:8px 0 16px;line-height:1.6;">Use this checklist to estimate integration complexity before the first call with Travi's integration team. All "YES" answers indicate a standard 6–8 week integration. Each "NO" adds 1–3 weeks.</p>
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:10px;margin-bottom:28px;">
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">FHIR R4 endpoint available</div><div style="font-size:12px;color:var(--slate);">EHR exposes a FHIR R4 API (Epic FHIR, Oracle FHIR, or equivalent)</div></div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">SMART on FHIR app registration supported</div><div style="font-size:12px;color:var(--slate);">IT team can register a third-party SMART app in the EHR app gallery</div></div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">ADT events accessible via FHIR Subscription or webhook</div><div style="font-size:12px;color:var(--slate);">Discharge events can be pushed or polled in near-real-time</div></div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">Discharge summaries in DocumentReference</div><div style="font-size:12px;color:var(--slate);">Discharge summary text accessible as FHIR DocumentReference (LOINC 18842-5)</div></div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">Task resource write-back permitted</div><div style="font-size:12px;color:var(--slate);">Third-party apps can write Task, Communication, and ClinicalImpression resources back to the EHR</div></div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">Patient phone numbers in telecom field</div><div style="font-size:12px;color:var(--slate);">Patient.telecom contains a mobile phone number for SMS delivery</div></div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">BAA can be executed with Travi</div><div style="font-size:12px;color:var(--slate);">Legal and compliance team can sign a Business Associate Agreement before go-live</div></div>
</div>
<div style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 14px;display:flex;align-items:flex-start;gap:10px;">
<span style="font-size:16px;flex-shrink:0;">☐</span>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);margin-bottom:2px;">Sandbox / test environment available</div><div style="font-size:12px;color:var(--slate);">EHR has a non-production sandbox with synthetic patient data for integration testing</div></div>
</div>
</div>
<div style="background:color-mix(in srgb,#0ea5e9 8%,var(--surface));border:1px solid color-mix(in srgb,#0ea5e9 30%,var(--border));border-radius:10px;padding:16px 20px;">
<div style="font-size:11px;font-weight:700;color:#38bdf8;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;">Integration Support</div>
<p style="font-size:13px;color:var(--slate);line-height:1.6;margin:0;">Travi provides a dedicated integration engineer for every pilot and contract customer. The integration engineer handles SMART app registration, data mapping, UAT coordination, and go-live monitoring. A sandbox environment with synthetic patient data is available for testing before any production data is accessed. Full integration documentation is available under NDA.</p>
</div>
<p style="margin-top:20px;font-size:13px;color:var(--slate);">
<strong>See also:</strong>
<a href="#doc-engineering-dataflow" style="color:var(--accent);text-decoration:none;" onclick="showDoc('doc-engineering-dataflow')">FHIR R4 Data Flow</a> β€” architecture diagram showing how these resources move through the pipeline, and
<a href="#doc-engineering-security" style="color:var(--accent);text-decoration:none;" onclick="showDoc('doc-engineering-security')">Security Architecture</a> β€” for PHI handling and compliance details.
</p>
</article>