Documentation
Conversational Triage API
FAQ

FAQ: Conversational Triage API

If what you're looking for isn't answered here, check out our general FAQ.

Choosing an API

  • Engine API is stateless and evidence-driven: you send the full evidence list with every /diagnosis call, and you own the conversation flow — question rendering, answer mapping, when to stop. Conversational Triage API is stateful and conversation-driven: the patient writes free text, the server owns the transcript and the assessment, and each request carries only the new message.

    Choose Engine API when you want full control of the interview and are building your own UI logic on top of a medical reasoning engine. Choose Conversational Triage API when you want a natural-language triage experience without implementing the conversation layer yourself.

  • Platform API manages users, patients and surveys, and drives structured question flows through an interview. Conversational Triage API is a free-text conversation surface with its own base URL and its own conversation model. The two are complementary rather than alternatives — see also Platform API vs Engine API.

  • Use blocking POST /turns for server-to-server integrations. Use HTTP NDJSON streaming for chat surfaces that render text as it is generated. Use the WebSocket transport for voice channels, interruption handling, and long-lived sessions with reconnect-and-resume.

    All three expose the same conversation model, so a conversation created on one can be read and continued on another.

Building a client

  • Almost always because the client builds message text from streaming deltas. Deltas carry text only — widgets never appear in them. Build message content from content_parts on conversation.assistant_message.completed. See Messages and content parts.

  • Conversation history is a union discriminated by role: user rows expose content only, assistant rows expose content_parts only. Branch on role.

  • On the turn's terminal event — conversation.turn.completed, conversation.turn.interrupted, or conversation.turn.failed. On HTTP NDJSON that is also when the stream closes; on blocking POST /turns it is when the response arrives. Gate your input control on that signal rather than on the last assistant message you received.

  • On HTTP, a conversation processes one turn at a time, so a second send while a turn is in flight is rejected until that turn reaches a terminal state. Gate your send control on the in-flight request; if you do retry, reuse the same user-message id — that call is idempotent and returns the existing turn. The WebSocket transport does not raise this error at all: a message sent mid-turn preempts the active turn instead.

Assessment and results

  • By design. On the blocking transport, refresh state with GET /conversations/{conversation_id}/assessment after a successful turn — that is the canonical way to pick up the updated assessment. On the streaming transports the assessment arrives on every turn as a conversation.assessment.updated event.

  • status (or conversation_status on a turn) becomes completed. Two situations produce that, and is_terminated distinguishes them: a finished interview with a recommendation, or a permanent termination with recommendation still null. Handle both — see Lifecycle and terminal states.

  • Yes. Pass age, sex, relationship and an evidence list in assessment when creating the conversation. Seeded evidence is recorded with origin: "seeded" so you can tell it apart from what the assistant collected. Ids must exist in the knowledge base, otherwise the create fails with unknown_evidence.

  • That is the documented response — 200 OK with three empty lists — when an explanation is unavailable: there is no recommendation yet, the condition is not in the recommendation set, or demographics are incomplete. Check for emptiness rather than for an error status.

Operations

  • Rate-limited responses carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers, and successful responses on rate-limited routes carry them too — read your current limits from there rather than hard-coding a number. Exceeding the limit produces rate_limit_exceeded (429), with Retry-After when applicable.

  • No. The server removes personally identifiable information from content — and from played_content on interruptions — before the message reaches the engine, and read models return only the redacted text.

  • Yes. Send a W3C Trace Context value in user_message.traceparent when starting a turn, and keep conversation.id alongside your own identifiers.

  • Call GET /info — it returns snomed_ct_version as a SNOMED URI for the instance you are authenticated against. Record it alongside stored assessments so you know which terminology version produced them.

Was this page helpful?