Turns
A turn is one accepted user message plus the assistant output it produced. POST /turns is the blocking variant: it returns once the turn is finished.
POST /conversations/{conversation_id}/turnsSending a message
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": {
"id": "68d9a84d-711c-4a55-a9dd-da6363e58e38",
"content": "I have a headache"
}
}'user_message is a UserMessageInput:
| Field | Type | Required | Description |
|---|---|---|---|
id | UUID | no | Client-generated id for local correlation. The server assigns one when omitted. |
content | string | yes | The user's text. Non-empty, and at most max_message_length Unicode code points, measured before PII redaction. |
is_interrupted | boolean | no | The user cut off an in-flight assistant message — typical in voice channels. Default false. |
played_content | string | no | The assistant text actually played before the interruption. Omit when not interrupting. |
traceparent | string | no | W3C Trace Context value linking this turn to your own trace. |
Before the message reaches the engine, the server removes personally identifiable information from content (and from played_content). Read models return only the redacted text, so treat content in responses as the only safe form of the message.
The response
Response: 201 Created, body is a Turn.
{
"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
}| Field | Type | Description |
|---|---|---|
id | UUID | The turn created by this request. |
status | completed, interrupted, or failed | Turn outcome. |
conversation_status | in_progress or completed | Whether the conversation still accepts messages. |
user_message | UserMessage | The accepted input, post-redaction. |
assistant_messages | AssistantMessage[] | Assistant output, in order. |
handoff | Handoff or null | Set when a handoff was requested during the turn. |
conversation_terminated | boolean | true when the turn left the conversation permanently unable to continue. |
Two things the response deliberately does not contain:
- The assessment. Refresh it with
GET /conversations/{conversation_id}/assessmentafter a successful turn. That call is the canonical way to pick up the updated state on this transport. - Transient widget events. Blocking turns return the final
content_partson each assistant message; the progressiveconversation.assistant_message.ui_widgetevent exists only on the streaming transports.
One turn at a time
On this transport a conversation processes one turn at a time: sending a message while another turn is active fails with turn_already_active (409). Gate your input control on the in-flight request. Retrying is safe when you reuse the same user-message id — that call is idempotent and returns the existing turn rather than a second one — but a retry under a new id fails again until the active turn reaches a terminal state.
The WebSocket transport behaves differently: a message sent mid-turn preempts the active turn instead of being rejected. See Realtime.
Once the conversation stops accepting messages, further calls fail with conversation_completed or conversation_terminated (both 409). See Lifecycle and terminal states.
Reading assistant output
Assistant messages have no top-level content. Build the rendered message from content_parts, in order:
const text = message.content_parts
.filter((part) => part.type === 'text')
.map((part) => part.text)
.join('')kind tells you the message's role within the turn — acknowledgement, bridge, filler, intro, or follow_up — which is useful for styling. can_interrupt tells a voice client whether the segment may be cut off, and is derived from kind alone. See Messages and content parts.