Schemas
Canonical reference for the wire shapes used by both transports — HTTP v2 and WebSocket. This is the only place where field-level shapes are described; the guides link here rather than repeating them.
Read models and write (input) models are listed separately. Input models always omit server-owned fields.
Enum values are in Enums and primitives; streaming event payloads in Streaming events.
Conversation
Conversation
Full read snapshot returned by both transports.
| Field | Type | Description |
|---|---|---|
id | UUID | Conversation identifier. |
status | "in_progress" or "completed" | Whether the conversation still accepts user messages. completed when the interview finished or the conversation was permanently terminated. Use is_terminated to distinguish those two cases. |
settings | ConversationSettings | Effective conversation settings. |
assessment | Assessment | Current assessment snapshot. |
messages | UserConversationMessage | AssistantConversationMessage[] | Persisted message history, discriminated by role. Empty for a new conversation. |
is_terminated | boolean | true once the conversation permanently rejects further user messages for a non-recommendation reason — for example an underage patient or a confirmed demographics change. Further turn calls fail with conversation_terminated. Default false. |
ConversationInput
Write model used to create a conversation.
| Field | Type | Required | Description |
|---|---|---|---|
settings | ConversationSettingsInput | yes | Initial settings; missing fields use the instance default, then the platform built-in. |
assessment | AssessmentInput | yes | Pre-seeded assessment; all fields optional. |
Settings
ConversationSettings
Server view of conversation settings; all fields are always present.
For create, an omitted field resolves to the default configured for your instance, then to the platform built-in listed below. An explicit request field always wins. Existing conversations keep the settings persisted at creation, so changing instance defaults does not affect conversations in flight. max_message_length is server-owned and not accepted on input.
| Field | Type | Platform default | Description |
|---|---|---|---|
language | string | "en" | BCP-47 language tag. |
channel | "text" or "voice" | "text" | Response mode optimized for text or TTS. |
ui_widgets_enabled | boolean | false | Emit UI widgets in assistant content_parts and transient widget events. |
opening_message_enabled | boolean | false | Emit an opening assistant message automatically. |
risk_factors_enabled | boolean | true | Allow the assistant to collect risk-factor evidence. |
geographic_risk_factors_enabled | boolean | false | Allow the assistant to collect geographic risk-factor evidence. |
handoff_policies | HandoffPolicy[] | [] | Enabled handoff detectors; empty disables all handoff detection. |
max_message_length | integer (> 0) | 1500 | Maximum accepted length of a user message, counted in Unicode code points, measured before PII redaction. Server-owned; per-instance. |
ConversationSettingsInput
Write model used on create and on partial update. All fields are optional; omitted fields keep their current value (update) or fall back to the instance default, then the platform built-in (create). max_message_length is not accepted.
| Field | Type | Required | Description |
|---|---|---|---|
language | string | no | Conversation language. |
channel | "text" or "voice" | no | Response mode optimized for text or TTS. |
ui_widgets_enabled | boolean | no | Emit UI widgets in assistant content_parts and transient widget events. |
opening_message_enabled | boolean | no | Emit an opening assistant message automatically. |
risk_factors_enabled | boolean | no | Allow the assistant to collect risk-factor evidence. |
geographic_risk_factors_enabled | boolean | no | Allow the assistant to collect geographic risk-factor evidence. |
handoff_policies | HandoffPolicy[] | no | Enabled handoff detectors. Values must be known HandoffPolicy members. |
Assessment
Assessment
Conversation-level assessment snapshot.
| Field | Type | Description |
|---|---|---|
age | Age or null | Patient age. |
sex | "male", "female", or null | Patient sex. |
relationship | PatientRelationship or null | Patient relationship to the user. |
evidence | Evidence[] | Matched evidence items. |
unmatched_evidence | UnmatchedEvidence[] | Evidence not yet linked to a knowledge concept. |
conditions | Condition[] | Knowledge-enriched condition rows when a recommendation exists. |
recommendation | Recommendation or null | Set once the conversation reaches a recommendation. |
Top-level conditions and recommendation.conditions carry the same Condition[] once a recommendation exists.
AssessmentInput
Write model used as the assessment seed on create. Read-only fields (recommendation, unmatched_evidence, conditions) are not accepted.
| Field | Type | Required | Description |
|---|---|---|---|
age | Age | no | Initial patient age. |
sex | "male" or "female" | no | Initial patient sex. |
relationship | PatientRelationship | no | Patient relationship to the user. |
evidence | EvidenceInput[] | no | Pre-seeded evidence items. |
Recommendation
| Field | Type | Description |
|---|---|---|
triage_level | TriageLevel | Triage band for the user. |
conditions | Condition[] | Same enriched rows as Assessment.conditions. |
has_emergency_evidence | boolean | Whether emergency-level evidence was observed. |
serious_evidence | SeriousEvidence[] | Highlighted serious evidence. |
specialist | Specialist | Suggested specialist. |
channel | RecommendationChannel | Suggested contact channel. |
stop_reason | string | Why the diagnostic flow stopped. |
Evidence
Read model for one evidence item in the assessment.
| Field | Type | Description |
|---|---|---|
id | string | Stable medical evidence identifier. |
name | string | Evidence label used in the conversation. |
common_name | string or null | Normalized common name when available. |
snomed_ct | SnomedCt | SNOMED CT mapping. Omitted from the response when unavailable. |
state | EvidenceState | Current evidence state. |
origin | EvidenceOrigin | How the evidence entered the conversation. |
seriousness | EvidenceSeriousness | Evidence seriousness from the knowledge base; defaults to normal when not classifiable. |
parent_id | string or null | Set when an immediate parent concept is also in evidence. |
EvidenceInput
Write model accepted on create; the server fills name and common_name from the knowledge base.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Stable medical evidence identifier. |
state | EvidenceState | yes | Declared evidence state. |
UnmatchedEvidence
Free-text or extracted evidence not linked to a knowledge concept.
| Field | Type | Description |
|---|---|---|
name | string | Label or extracted text. |
state | EvidenceState | Answer state. |
Condition
Knowledge-enriched differential condition row used in Assessment.conditions and Recommendation.conditions.
| Field | Type | Description |
|---|---|---|
id | string | Stable condition identifier. |
probability | number | Posterior probability for this condition. |
name | string | Display name. |
common_name | string | Normalized common name. |
condition_details | ConditionDetails | Knowledge-base metadata for this condition. |
ConditionDetails
| Field | Type | Description |
|---|---|---|
icd10_code | string | ICD-10 code. |
snomed_ct | SnomedCt | SNOMED CT mapping. Omitted when unavailable. |
category | ConditionCategory | Condition category. |
prevalence | ConditionPrevalence | Prevalence band. |
severity | ConditionSeverity | Severity band. |
acuteness | ConditionAcuteness | Acuteness band. |
hint | string or null | Optional advisory hint. |
has_patient_education | boolean | Whether patient-education content exists. |
triage_level | TriageLevel | Recommended triage level for this condition. |
ConditionCategory
| Field | Type | Description |
|---|---|---|
id | string | Stable category identifier. |
name | string | Display name. |
SnomedCt
| Field | Type | Description |
|---|---|---|
code | string | SNOMED CT code. |
SeriousEvidence
| Field | Type | Description |
|---|---|---|
id | string | Stable medical evidence identifier. |
name | string | Evidence label. |
common_name | string or null | Normalized common name when available. |
Specialist
| Field | Type | Description |
|---|---|---|
id | string | Stable specialist identifier. |
name | string | Display name. |
Messages
Which message type applies where:
| Type | Used in |
|---|---|
UserConversationMessage / AssistantConversationMessage | Conversation.messages only — persisted snapshot history. |
UserMessage / UserMessageInput | Blocking Turn.user_message, the streaming conversation.user_message.created event, and client turn input. Includes voice fields such as is_interrupted and played_content. |
AssistantMessage / AssistantMessageDelta | Completed stream segments and blocking Turn.assistant_messages. Includes can_interrupt; no top-level role in JSON. |
UserMessage
Read model for a user-authored message on turns and streaming events. Not used in Conversation.messages.
| Field | Type | Description |
|---|---|---|
id | UUID | Stable user message identifier. |
content | string | User message text after server-side PII redaction (non-empty). |
is_interrupted | boolean | Whether the user cut off an in-flight assistant message. Default false. |
played_content | string or null | Assistant text actually played out before the interruption, PII-redacted when present. |
traceparent | string or null | W3C Trace Context value sent by the client when starting the turn. |
UserMessageInput
Write model for a new user message.
| Field | Type | Required | Description |
|---|---|---|---|
id | UUID | no | Client-generated identifier for stable local correlation. The server assigns one when omitted. |
content | string | yes | Full user-authored content (non-empty). The server may redact PII before persistence and engine processing. |
is_interrupted | boolean | no | When true, the user cut off an in-flight assistant message — typical in voice channels. Default false. |
played_content | string | no | Assistant text played before interruption; omit when not interrupting. |
traceparent | string | no | W3C Trace Context traceparent linking this turn to the caller's trace. |
AssistantMessage
Canonical full representation of an assistant message — the completed form on streaming and on blocking turns.
| Field | Type | Description |
|---|---|---|
id | UUID | Stable assistant message identifier. |
turn_id | UUID | Turn that produced this message. |
kind | MessageKind | Assistant message role within the turn. |
can_interrupt | boolean | Whether the client may interrupt this assistant segment. Derived from kind alone — see MessageKind. |
content_parts | ContentPart[] | Ordered content parts. Always present on completed assistant messages. |
There is no top-level content or ui_widgets field. Concatenate text parts for plain-text rendering.
AssistantMessageDelta
In-progress chunk of an assistant message; the payload of the conversation.assistant_message.delta event.
| Field | Type | Description |
|---|---|---|
id | UUID | Stable assistant message identifier. |
turn_id | UUID | Turn that is producing this message. |
kind | MessageKind | Assistant message role within the turn. |
can_interrupt | boolean | Whether the client may interrupt the message while streaming. Derived from kind alone — see MessageKind. |
delta | string | Text chunk to append to the message. |
Deltas carry text only. Widgets arrive on conversation.assistant_message.ui_widget and in content_parts on the completed event, never in a delta. Never treat concatenated deltas as the complete message.
UserConversationMessage
Persisted user row in Conversation.messages. Discriminator value "user".
| Field | Type | Description |
|---|---|---|
role | "user" | Discriminator. |
content | string | User message text after server-side PII redaction. |
id | UUID or null | Stable message identifier when assigned. |
turn_id | UUID or null | Owning turn when known. |
AssistantConversationMessage
Persisted assistant row in Conversation.messages. Discriminator value "assistant".
| Field | Type | Description |
|---|---|---|
role | "assistant" | Discriminator. |
id | UUID | Stable assistant message identifier (required). |
kind | MessageKind | Assistant message role within the turn. |
content_parts | ContentPart[] | Ordered content parts. |
turn_id | UUID or null | Owning turn when known. |
Client rule: branch on role. Do not read content on assistant rows or content_parts on user rows. In OpenAPI this is a oneOf with role as the discriminator.
Turn
Blocking-turn read model returned by POST /turns.
| Field | Type | Description |
|---|---|---|
id | UUID | Stable turn identifier. |
status | "completed", "interrupted", or "failed" | Turn outcome. |
conversation_status | "in_progress" or "completed" | Conversation lifecycle after this turn. Same meaning as Conversation.status. Use conversation_terminated to tell a finished interview from a permanent termination. |
user_message | UserMessage | User input that started the turn. |
assistant_messages | AssistantMessage[] | Assistant output produced by the turn. |
handoff | Handoff or null | Set when a handoff was requested during the turn, subject to enabled handoff_policies. |
conversation_terminated | boolean | true when the turn left the conversation permanently unable to accept further messages. The explanation for the user is in assistant_messages. Default false. |
Handoff
Recorded when the assistant detects that control should pass back to the orchestrator. Emitted on conversation.handoff.requested and included on Turn for blocking turns.
| Field | Type | Description |
|---|---|---|
reason | HandoffReason | Why control should be handed off. |
user_request | string | The user request that triggered the handoff, verbatim or paraphrased. |
Content parts
ContentPart
Tagged union with type as the discriminator.
| Variant | type value | Payload |
|---|---|---|
TextContentPart | "text" | text: string — plain text segment. |
UiWidgetContentPart | "ui_widget" | widget: UIWidget — structured UI payload. |
{ "type": "text", "text": "How long have you had it?" }{
"type": "ui_widget",
"widget": { "type": "hints", "hints": ["Yes", "No", "Not sure"] }
}UIWidget
Tagged union nested inside ui_widget content parts and transient widget events. Discriminator is type.
| Variant | type value | Payload |
|---|---|---|
UIWidgetHints | "hints" | hints: string[] — short suggestion chips. |
UIWidgetForm | "form" | items: UIWidgetFormItem[] — structured multi-item answer form. |
UIWidgetRecommendationTriage | "recommendation_triage" | triage_level, specialist, channel — triage summary card. |
UIWidgetRecommendationConditions | "recommendation_conditions" | conditions, has_emergency_findings, serious_findings — differential conditions card. |
UIWidgetSummary | "summary" | patient, groups, unmatched — cumulative card of everything recorded so far. |
UIWidgetReadBack | "read_back" | groups — confirmation card for findings the user just volunteered. |
UIWidgetFormItem
| Field | Type | Description |
|---|---|---|
name | string | Human-readable item label. |
choices | string[] | Allowed answer labels in display order. |
UIWidgetRecommendationCondition
Condition row embedded in recommendation_conditions widgets; same enrichment shape as Condition.
| Field | Type | Description |
|---|---|---|
id | string | Stable condition identifier. |
probability | number | Posterior probability. |
name | string | Display name. |
common_name | string or null | Normalized common name when available. |
condition_details | ConditionDetails | Knowledge-base metadata. |
UIWidgetSummary
Emitted with the assistant message that asks the user to confirm what has been recorded so far. See UI widgets for when it appears and how to render it.
| Field | Type | Description |
|---|---|---|
type | "summary" | Discriminator. |
patient | string or null | Demographics heading, e.g. "Female, 30 years old" or "Female, 30 years old (someone else)". null when sex, age and patient relationship are all absent. |
groups | UIWidgetSummaryGroup[] | Findings grouped by state. Fixed order: present, absent, unknown. Empty groups are omitted. |
unmatched | UIWidgetSummaryUnmatched or null | Reported findings that matched no knowledge concept. null when there are none. |
UIWidgetSummaryGroup
| Field | Type | Description |
|---|---|---|
state | string | Stable machine key: present, absent, or unknown. |
title | string | Localized group heading, e.g. "Present", "Absent", "Not sure". |
items | UIWidgetSummaryItem[] | Findings in this group, in display order. |
UIWidgetSummaryItem
| Field | Type | Description |
|---|---|---|
name | string | Localized common name of the matched concept. |
highlighted | boolean | Whether this finding was added in the turn that produced this render. Default false. |
children | UIWidgetSummaryItem[] | Findings whose parent concept is this item. At most one level deep — a child's children is always []. |
UIWidgetSummaryUnmatched
| Field | Type | Description |
|---|---|---|
title | string | Localized section heading, e.g. "Findings I can't use". |
note | string | Localized explanation of why these findings are listed separately. |
items | UIWidgetSummaryUnmatchedItem[] | The unmatched findings, deduplicated, in the order the user raised them. |
UIWidgetSummaryUnmatchedItem
| Field | Type | Description |
|---|---|---|
name | string | The finding as the user expressed it — not a knowledge-base name. |
highlighted | boolean | Whether this finding was added in the turn that produced this render. Default false. |
UIWidgetReadBack
Emitted with a read_back assistant message. See Read-backs.
| Field | Type | Description |
|---|---|---|
type | "read_back" | Discriminator. |
groups | UIWidgetReadBackGroup[] | Findings grouped by state. Fixed order: present, absent, unknown. Empty groups are omitted. |
UIWidgetReadBackGroup
| Field | Type | Description |
|---|---|---|
state | string | Stable machine key: present, absent, or unknown. |
title | string | Localized group heading, e.g. "Present", "Absent", "Not sure". |
items | UIWidgetReadBackItem[] | Findings in this group, in display order. |
UIWidgetReadBackItem
| Field | Type | Description |
|---|---|---|
name | string | Localized common name of the matched concept. |
highlighted | boolean | Always false on read-backs. Present for shape parity with the summary card. |
Read-back items are deliberately flat: unlike UIWidgetSummaryItem they carry no children, because a single message cannot produce a parent-and-child pair to nest.
QuestionFinding
Serious-finding row inside UIWidgetRecommendationConditions.serious_findings.
| Field | Type | Description |
|---|---|---|
id | string | Stable medical evidence identifier. |
name | string | Evidence label. |
common_name | string or null | Normalized common name when available. |
parent_ids | string[] | Parent concept identifiers. |
Reasoning
Reasoning
Read model for the reasoning behind one completed turn.
| Field | Type | Description |
|---|---|---|
turn_id | UUID | Turn this reasoning describes. |
conditions | ReasoningCondition[] | Knowledge-enriched conditions considered at the turn boundary. |
evidence | ReasoningEvidence[] | Evidence, matched or unmatched, visible at the turn boundary. |
summary | string or null | Free-text rationale summary when available. |
ReasoningCondition
| Field | Type | Description |
|---|---|---|
id | string | Stable condition identifier. |
probability | number | Probability at the turn boundary. |
name | string | Display name. |
common_name | string | Normalized common name. |
ReasoningEvidence
| Field | Type | Description |
|---|---|---|
name | string | Evidence label. |
state | EvidenceState | State at the turn boundary. |
Condition drill-down
ConditionExplanation
| Field | Type | Description |
|---|---|---|
condition_id | string | The condition this explanation refers to. |
supporting_evidence | ExplainEvidence[] | Evidence supporting the condition. |
conflicting_evidence | ExplainEvidence[] | Evidence conflicting with the condition. |
unconfirmed_evidence | ExplainEvidence[] | Relevant evidence that was not confirmed. |
When an explanation is unavailable — no recommendation, the condition is not in the recommendation set, or demographics are incomplete — all three lists are empty and the response is still 200 OK.
ExplainEvidence
| Field | Type | Description |
|---|---|---|
id | string | Stable medical evidence identifier. |
name | string | Evidence label. |
common_name | string or null | Normalized common name when available. |
ConditionPatientEducation
| Field | Type | Description |
|---|---|---|
condition_id | string | The condition this content refers to. |
title | RichTextNode | Article title. |
sections | PatientEducationSection[] | Ordered patient-education sections. |
PatientEducationSection
| Field | Type | Description |
|---|---|---|
name | PatientEducationSectionType | Section identifier. |
title | RichTextNode | Section heading rendered as rich text. |
content | RichTextNode[] | Section body rendered as a list of rich-text nodes. |
RichTextNode
Recursive rich-text node.
| Field | Type | Description |
|---|---|---|
node_type | RichTextNodeType | Kind of node. |
content | RichTextNode[] | Child nodes; empty for leaf text nodes. |
value | string or null | Text payload for text nodes. |
target | string or null | Link target for link nodes. |
Errors
Error
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable identifier; see Error codes. |
message | string | Human-readable explanation. |
details | ValidationErrorDetail[] | Optional. Present whenever the failure was field validation — on HTTP 422 (invalid_request) and on the WebSocket invalid_payload error. The two transports use different codes for the same class of failure. |
ValidationErrorDetail
One entry per request field that failed validation.
| Field | Type | Description |
|---|---|---|
location | string | Path to the offending field, e.g. body.settings.language or body.assessment.evidence[0].id. |
message | string | Why the value was rejected, e.g. Input should be a valid string. |
type | string | Stable validation-failure identifier for programmatic branching, e.g. string_type, missing. |
location is formatted differently on each transport — do not parse it with one rule. On HTTP its first segment names the part of the request that failed (body, query, path, header, or cookie) and array positions are bracketed: body.assessment.evidence[0].id. On WebSocket it is a dot-joined path prefixed with the frame's event type, and array positions are plain segments: conversation.create.conversation.assessment, conversation.create.conversation.assessment.evidence.0.id. For an HTTP body that is not valid JSON, location is body and message names the syntax problem.
Error.message summarizes at most the first five entries and appends ; and N more beyond that; details always lists every failure. Submitted values are never echoed back in either field.
InfoDTO
Returned by GET /info.
| Field | Type | Description |
|---|---|---|
snomed_ct_version | string or null | SNOMED CT version URI used by the instance's medical model mappings, per the SNOMED URI Standard. |