Documentation
Conversational Triage API
Quickstart

Quickstart

Conversational Triage API lets you build a free-text triage experience: the patient describes their problem in their own words, the assistant asks follow-up questions, and the API maintains a structured assessment behind the conversation — evidence, differential conditions, and a triage recommendation.

Unlike Engine API, which is stateless and expects you to send the full evidence list with every request, Conversational Triage API is stateful: the server owns the conversation, so each request carries only the new user message.

Setup

The API is available at:

https://api.infermedica.com/api/ct/v2

All HTTP paths on this page are relative to that base URL. The realtime WebSocket transport lives at wss://api.infermedica.com/api/ct/v2/realtime.

Authentication

Every request carries a bearer token in the Authorization header. Tokens are issued and validated by the Infermedica API gateway, and the token must carry the ct:conversations scope.

Authorization: Bearer <token>

See Authentication for details.

Choose a transport

The same conversation model is exposed through three interchangeable flavors — pick the one that matches your client:

TransportUse when
Blocking HTTPPOST /turnsServer-to-server integrations, or any client that can wait for the full reply.
HTTP streamingPOST /turns/streamWeb and mobile clients that want to render text as it is generated.
WebSocket/realtimeVoice channels and long-lived sessions with interruption support.

Building an orchestrator on the A2A protocol (opens in a new tab) instead? See A2A integration — it exposes the same conversation model as a remote A2A agent.

The rest of this page walks through a conversation over blocking HTTP.

1. Create a conversation

Start by creating a conversation. Both settings and assessment are required objects, but every field inside them is optional — send empty objects to accept the defaults configured for your instance.

cURL
curl "https://api.infermedica.com/api/ct/v2/conversations" \
  -X "POST" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "language": "en",
      "channel": "text",
      "ui_widgets_enabled": false,
      "opening_message_enabled": false
    },
    "assessment": {
      "age": { "value": 35, "unit": "year" },
      "sex": "female",
      "relationship": "self",
      "evidence": []
    }
  }'

The response is 201 Created with the full conversation snapshot. Keep the id — every subsequent call is scoped to it.

JSON
{
  "id": "68d9a84d-711c-4a55-a9dd-da6363e58e38",
  "status": "in_progress",
  "settings": {
    "language": "en",
    "channel": "text",
    "ui_widgets_enabled": false,
    "opening_message_enabled": false,
    "risk_factors_enabled": true,
    "geographic_risk_factors_enabled": false,
    "handoff_policies": [],
    "max_message_length": 1500
  },
  "assessment": {
    "age": { "value": 35, "unit": "year" },
    "sex": "female",
    "relationship": "self",
    "evidence": [],
    "unmatched_evidence": [],
    "conditions": [],
    "recommendation": null
  },
  "messages": [],
  "is_terminated": false
}
ℹ️

assessment is required, but everything inside it is optional. Send "assessment": {} when you have nothing to seed — omitting the object fails validation. Seed it whenever your host application already knows the patient: demographics supplied up front are questions the assistant does not have to ask.

2. Send a message

One user message produces one turn: the accepted input plus all assistant output it generated.

cURL
curl "https://api.infermedica.com/api/ct/v2/conversations/68d9a84d-711c-4a55-a9dd-da6363e58e38/turns" \
  -X "POST" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "user_message": {
      "content": "I have a headache"
    }
  }'

The call blocks until the turn finishes and returns 201 Created:

JSON
{
  "id": "a1b2c3d4-0000-0000-0000-000000000000",
  "status": "completed",
  "conversation_status": "in_progress",
  "user_message": {
    "id": "68d9a84d-711c-4a55-a9dd-da6363e58e38",
    "content": "I have a headache"
  },
  "assistant_messages": [
    {
      "id": "6f605890-ff28-4b6d-a17f-d410f9faf558",
      "turn_id": "a1b2c3d4-0000-0000-0000-000000000000",
      "kind": "follow_up",
      "can_interrupt": true,
      "content_parts": [
        { "type": "text", "text": "How long have you had it?" }
      ]
    }
  ],
  "handoff": null,
  "conversation_terminated": false
}

Assistant text lives in content_parts, not in a top-level content field. To render plain text or feed a TTS engine, concatenate the text parts in order. See Messages and content parts.

3. Read the assessment

A blocking turn response deliberately omits the assessment. Refresh it with a separate read:

cURL
curl "https://api.infermedica.com/api/ct/v2/conversations/68d9a84d-711c-4a55-a9dd-da6363e58e38/assessment" \
  -H "Authorization: Bearer <token>"
JSON
{
  "age": { "value": 35, "unit": "year" },
  "sex": "female",
  "relationship": "self",
  "evidence": [
    {
      "id": "s_21",
      "name": "Headache",
      "common_name": "Headache",
      "state": "present",
      "origin": "asked",
      "seriousness": "normal",
      "parent_id": null
    }
  ],
  "unmatched_evidence": [],
  "conditions": [],
  "recommendation": null
}
ℹ️

On the streaming transports you do not need this call — the assessment arrives on every turn as a conversation.assessment.updated event.

4. Continue until a recommendation

Repeat step 2 for each user message. Because the server owns the state, you never resend earlier evidence.

The conversation ends when assessment.recommendation becomes non-null and conversation_status flips to completed. At that point the recommendation carries the triage level, the specialist and channel suggestion, and the ranked differential conditions.

JSON
{
  "recommendation": {
    "triage_level": "consultation_24",
    "conditions": [
      {
        "id": "c_49",
        "probability": 0.4532,
        "name": "Migraine",
        "common_name": "Migraine",
        "condition_details": { "...": "..." }
      }
    ],
    "has_emergency_evidence": false,
    "serious_evidence": [],
    "specialist": { "id": "sp_1", "name": "General practitioner" },
    "channel": "personal_visit",
    "stop_reason": "..."
  }
}
⚠️

A conversation can also end without a recommendation — an underage patient or a confirmed change of age or sex set is_terminated to true, and further messages are rejected with conversation_terminated. The explanation for the user arrives in that turn's assistant messages. Handle both endings.

Next steps

Was this page helpful?