Skip to main content

Messages API

Realtime chat channels that hang off a live Meeting. Every Meeting starts with one default thread (main) and - just like a Slack channel - any client can spin up extra threads on‑the‑fly (/v1/meetings/{meeting_id}/threads). Use them to separate side‑bars (qa‑chat), action‑items, or Assistant‑only command lanes.

Looking for auth, base URLs, pagination, or error formats? Head over to API Foundations.

Endpoints at a glance

Method & PathPurpose
GET /v1/meetings/{meeting_id}/threads/{id}/messagesList messages (cursor‑paginated)
POST /v1/meetings/{meeting_id}/threads/{id}/messagesPost a user chat message / inline tool call
GET /v1/meetings/{meeting_id}/threads/{id}/streamStream live messages (SSE, WebSocket upgrade)

Real‑world hook: Daily Scrum Assistant auto‑flags new blockers in the blockers thread while the team is still in the stand‑up.

Message object schema

FieldTypeDescription
idstringIdentifier (prefix msg_).
thread_idstringOwning thread.
created_attimestampISO‑8601 UTC.
rolestringuser, assistant, system, transcript_segment, tool_call_started, tool_call_finished.
typestringanswer (default) tool_suggestion or tool_response.
contentstring|objectMessage text or tool payload/result JSON.
segment_offsetinteger?Milliseconds offset inside the meeting for transcript segments.
tool_namestring?Populated for tool call rows.
tool_paramsobject?Arguments passed to the tool.
tool_resultobject?Result blob once tool_call_finished arrives.

Pagination follows the shared cursor model (cursor, limit, has_more). Newest‑first ordering by default; set order=asc to replay chronologically.

List messages

GET /v1/meetings/{meeting_id}/threads/{id}/messages

Query params

NameTypeDefaultDescription
cursorstring?Pointer from previous page.
limitinteger?501–200 messages per page.
orderstring?descasc or desc.

Post a message / inline tool call

POST /v1/meetings/{meeting_id}/threads/{id}/messages

Body parameters

FieldTypeRequiredNotes
rolestringyesuser only - assistant/system messages are server‑generated.
contentstringyes*Natural‑language prompt. If it requests an action, the platform automatically infers and executes the proper tool.

🛠️ Tool call flow: Simply phrase the request (e.g. "Post 🚨 New blocker: login API 500s to #general on Slack"). The Assistant will reply with a tool_suggestion. Confirm by posting confirmToolUse: true to execute the action, or false to decline.

Example – ask a question

curl -X POST https://api.voilo.io/meetings/meeting_123/threads/th_abc/messages \
-H "Authorization: Bearer $VOILO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"role": "user",
"content": "Summarize the last 30 seconds"
}'

Execution Flow for Tool Calls

When a user request may require running an external tool (Slack, Jira, calendar, etc.) the exchange now follows the five‑step handshake below:

To enable the tool, you need to configure it and the assistant. See Tool Calling for more details.

  1. User request - The client posts a normal chat message to POST /v1/meetings/{meeting_id}/threads/{id}/messages. No special fields are needed to ask for the action.
curl -X POST https://api.voilo.io/meetings/meeting_123/threads/th_abc/messages \
-H "Authorization: Bearer $VOILO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"role": "user",
"content": "Post 🚨 New blocker: login API 500s to #general on Slack"
}'
  1. Assistant suggestion - If the Assistant decides the request should be handled by a tool, it replies with a message whose type is "tool_suggestion". The payload includes tool_name and tool_params.
{
"type": "tool_suggestion",
"tool_name": "slack_post_message",
"tool_description": "Post a message to a Slack channel",
"tool_params": {
"channel": "general",
"text": "🚨 New blocker: login API 500s"
},
"expected_outcome": "The message is posted to the Slack channel",
"tool_calls": [
{
"name": "slack_post_message",
"args": {
"channel": "general",
"text": "🚨 New blocker: login API 500s"
},
"type": "tool_call",
"id": "call_123"
}
]
}
  1. Client decision - Your UI (or server logic) chooses whether to execute the suggested tool. You can surface the details to the end‑user or auto‑approve.
  2. Confirmation - Post a follow‑up chat message in the same thread containing the top‑level field "confirmToolUse": true (or false).
curl -X POST https://api.voilo.io/meetings/meeting_123/threads/th_abc/messages \
-H "Authorization: Bearer $VOILO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"role": "user",
"content": "",
"confirmToolUse": true
}'
  1. Tool execution - When confirmToolUse is true the platform runs the tool and streams back a type = "tool_response" message carrying the result in tool_result.
{
"type": "tool_response",
"answer": "The message is posted to the Slack channel",
"tool_results": [{
"tool": "slack_post_message",
"args": {
"channel": "general",
"text": "🚨 New blocker: login API 500s"
},
"result": "The message is posted to the Slack channel"
}]
}

Stream live messages

GET /v1/meetings/{meeting_id}/threads/{id}/stream

The server only supports WebSocket for realtime delivery. Include the typical handshake headers (Connection: Upgrade, Upgrade: websocket) plus any subprotocol you need.

  • Each inbound text frame is a JSON‑encoded Message delivered the moment it is created.
  • Heartbeat ping frames are emitted every 30 seconds; respond with pong (most WS libraries do this automatically).
// sample text frame payload
{
"thread_id": "th_blockers",
"created_at": "2025-06-17T11:50:03Z",
"role": "user",
"content": "List all blockers",
"segment_offset": 271200
}

See also