Embedded Page¶
Working example
A single-file MIS simulator implements the full integration flow below. Open demo · View source
Prerequisites¶
- Obtain your API key from the Tayra team during onboarding. Send as
Authorization: <api_key>. Store server-side only. - Allow outbound HTTPS and WSS (port 443) from clinician machines to
api.tayra.healthandtayra.health. All connections are outbound. If an L7 proxy inspects traffic, ensure it permits theUpgrade: websocketheader for these hosts.
Integration flow¶
Tayra runs as an embedded view inside your MIS. For each encounter you create a session over HTTPS, supplying the note structure you need. You receive a URL to embed. The clinician records and reviews the consultation inside that view. You long-poll the session status until it reaches completed, then write the note into the patient chart.
Everything is ordinary outbound HTTPS. No inbound access, no open ports, no persistent protocol.
- Create a session — one POST returns a
session_idand a launch URL (the ID is a cryptographically random token — safe to pass to the workstation, no other credentials needed there) - Embed the view — load the URL in an iframe or WebView with microphone permission
- Poll for the note —
GETreturns the session status:in_progress,interrupted(with partial content if the connection dropped), orcompletedwith the final note
sequenceDiagram
autonumber
participant E as Your MIS
participant T as Tayra API
participant V as Embedded View
E->>T: POST /sessions
T-->>E: session_id, launch_url
E->>V: load launch_url in iframe / WebView
Note over V: Clinician records, reviews, submits
loop until status = completed or 404
E->>T: GET /sessions/{id} (held up to 25 s)
T-->>E: 200 {status}
end
E->>E: write result into the chart
0. Create a doctor (optional)¶
Register the doctor once and reuse the returned id as doctor_id in subsequent sessions. Linking sessions to a doctor lets you track usage per clinician and view per-doctor analytics in the admin panel.
201 Created
| Field | Requirement |
|---|---|
last_name |
Required. |
first_name |
Required. |
patronymic |
Optional. |
1. Create a session¶
Call when the clinician opens the encounter, not in advance. The launch URL is short-lived.
Option A — you define the note structure (see Custom Template):
{ "title": "Follow-up",
"locale": "en",
"template": {
"type": "object",
"properties": {
"symptoms": {
"type": "array",
"description": "List of reported symptoms",
"items": { "type": "string" }
},
"diagnosis": {
"type": ["string", "null"],
"description": "Clinical diagnosis"
}
},
"required": ["symptoms", "diagnosis"]
}
}
Option B — use a pre-configured template:
201 Created
{
"session_id": "8h4f-...",
"launch_url": "tayra.health/session?t=...",
"expires_at": "2026-08-12T09:15:02Z"
}
Request fields¶
| Field | Requirement |
|---|---|
template |
Required unless you send template_id. The note structure: field identifiers, labels, and optional hints. Up to 5000 total properties; aggregate string budget 120000 characters. |
template_id |
Alternative to template. Names a pre-configured note structure held for your tenant. Send exactly one of template or template_id. |
title |
Optional. Shown in the view header so the clinician can confirm the encounter. No patient identifiers. |
locale |
Required. Transcription and interface language. Two-letter ISO 639-1 code: en, uk, pl, fr, lv. |
doctor_id |
Optional. UUID of the doctor for this encounter. Create via POST /doctors. |
patient_metadata |
Optional. Object with the patient's age (integer, 0–150) and gender (single-line string, up to 32 characters). Used as context to improve the accuracy of the note, e.g. grammatical gender and age-appropriate wording. Both fields are optional. Send only these values, no names or other patient identifiers. |
2. Embed the view¶
Load launch_url in an iframe or native WebView. It occupies the space it is given.
<iframe
src="{launch_url}"
allow="microphone"
style="width:100%;min-width:640px;height:100%;min-height:720px;border:0">
</iframe>
| Requirement | Detail |
|---|---|
| Load promptly | Single-use token, valid 60 seconds. Expired → launch_token_expired. Already used → launch_token_consumed. |
| Do not reload | Reloading or unmounting invalidates the view. Use relaunch for a fresh URL. |
| Microphone | iframe: allow="microphone". Desktop WebView: grant via host permission API. |
| Size | Recommended 640 × 720 CSS pixels or above (half of a 1280×720 HD screen, split side-by-side). Responsive to any container size. |
Relaunch¶
200 OK
The consultation continues where it left off.
3. Poll for the note¶
Long-poll the session. The response always includes a status field indicating the current state.
Recording in progress — 200 OK
Connection interrupted, partial result available — 200 OK
If the clinician reconnects, status returns to in_progress.
Clinician submitted — 200 OK
Session cancelled or expired — 404 Not Found
Loop mechanics¶
- Send
GETwithwait. Server holds the connection until a status transition or timeout. status: "in_progress"— no change yet; re-poll.status: "interrupted"— connection dropped;contentcontains the partial result. Decide whether to wait for reconnection or persist as-is.status: "completed"— final note ready; persist to chart and stop polling.404— session cancelled or expired; stop polling.
Practical notes¶
| Concern | What to do |
|---|---|
| Client timeout | Set above the wait — e.g. 25 + 15 s. |
| Short-polling fallback | If long-polling is not feasible, omit wait and poll every 5 s. Long-polling is recommended. |
| Failures | Reissue on drop or timeout. Back off on repeated 5xx. |
| Concurrency | One loop per session. |
| Note retention | Finalized notes are retained for 1 hour as a recovery backstop. |
4. postMessage (optional)¶
When the clinician submits the note, the embedded view posts a message event to the parent window. This is an optional alternative to detecting completion via polling.
window.addEventListener('message', (event) => {
if (event.data?.type === 'tayra:completed') {
// event.data contains the final note
}
});
Event payload
| Field | Description |
|---|---|
type |
Always "tayra:completed". |
content |
Structured note matching the template schema. |
transcript |
Full transcript text. |
duration_seconds |
Session recording duration in seconds. |
Note
The event fires once, on submission. It is a convenience signal — polling remains the authoritative delivery mechanism and must not be removed. If the iframe is loaded cross-origin or inside a native WebView without a JS bridge, the event may not be receivable.
Errors¶
{ "error": {
"code": "session_not_found",
"message": "Unknown or expired session.",
"retryable": false,
"trace_id": "trc_9f2a..." } }
| Code | HTTP | Action |
|---|---|---|
launch_token_expired |
410 | URL older than 60 s. Call relaunch. |
launch_token_consumed |
410 | URL already used. Call relaunch. |
session_not_found |
404 | Session expired or cancelled. Stop polling. |
rate_limited |
429 | Retry after retry_after_ms. |
internal |
5xx | Retry with backoff. Quote trace_id in support requests. |
Standard errors (400, 401, 409) use the same envelope and are self-explanatory during development.
Limits¶
| Limit | Value |
|---|---|
| Total properties per schema | 5000 |
| Aggregate string budget | 120000 characters |
| Launch URL validity | 60 s, single use |
| Note retention | 1 hour |
| Max long-poll wait | 25 s |
| Min short-poll interval | 5 s |