Conversations Tutorial
This tutorial demonstrates how to have multi-turn conversations with Knowledge Discovery agents. Unlike single-shot questions, conversations maintain context across multiple questions and answers, allowing for more natural dialogue.
Prerequisites
- Valid access token (see Authentication)
- At least one configured agent with data sources
- Discovery User role or higher
If your HTTP client doesn't add the User-Agent header by default, make sure to include it in all requests below (e.g., User-Agent: PostmanRuntime/7.28.4) to avoid receiving a 403 Forbidden response.
Tutorial Overview
In this tutorial, you'll learn to:
- Start a new conversation with an agent
- Ask follow-up questions within a conversation
- Use streaming endpoints to receive incremental responses via SSE
- List and retrieve conversation messages
- Provide feedback on conversation messages
- Manage conversations (list, update metadata)
Conversation API calls are synchronous and may take up to 480 seconds for complex GraphRAG queries. Ensure your HTTP client is configured with a timeout of at least 480 seconds to avoid premature disconnects.
Part 1: Start a Conversation
To start a new conversation, send the first question using the conversation endpoint. The response returns both the new conversationId and the first message (question and answer).
- HTTP
- curl
POST {{baseUrl}}/qna/agents/{{agentId}}/conversations
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
{
"question": "What is the status of the project?",
"contextObjectIds": null,
"dynamicFilter": null
}
AGENT_ID="project-agent-id"
curl -X POST "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"question": "What is the status of the project?",
"contextObjectIds": null,
"dynamicFilter": null
}'
Start Conversation Request Parameters
| Field | Required | Description |
|---|---|---|
question | Yes | Initial natural language question for the conversation |
dynamicFilter | No | Runtime filter expression to narrow search results |
enableCitations | No | When true, the agent includes inline citation references in the answer text. Defaults to false when not provided. |
Response Structure
{
"conversation": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"lastModified": "2025-12-02T10:30:00Z"
},
"message": {
"id": "f1e2d3c4-b5a6-7890-1234-567890abcdef",
"answer": "Based on the documents, the project is currently in progress.",
"documentReferences": [
{
"documentId": "doc001",
"references": [
{
"referenceId": "ref001",
"rankScore": 0.95,
"rank": 1
}
]
}
],
"question": "What is the status of the project?",
"feedback": null,
"staticFilter": null,
"dynamicFilter": null,
"dateCreated": "2025-12-02T10:30:00Z",
"dateAnswered": "2025-12-02T10:30:05Z",
"agentVersion": 1,
"status": "Answered"
}
}
The response structure shown above applies to standard RAG agents. If you're using a Graph RAG agent (see Managing Agents Tutorial - Graph RAG), the response will include graphReferences instead of (or in addition to) documentReferences. Graph references provide entity and relationship information from the knowledge graph domain.
Understanding the Response
| Field | Type | Description |
|---|---|---|
conversation.id | UUID | Unique identifier for the conversation |
conversation.lastModified | datetime | Timestamp of the last update |
message.id | UUID | Unique identifier for this message |
message.question | string | The question text |
message.answer | string | The generated answer |
message.documentReferences | array | Source documents and chunks used for the answer |
message.dateCreated | datetime | When the question was submitted |
message.dateAnswered | datetime | When the answer was generated |
message.agentVersion | integer | Version of the agent used |
message.status | enum | Message status: Unspecified, Submitted, Answered, Error, Blocked |
Starting a Conversation with Dynamic Filter
You can apply dynamic filters when starting a conversation to narrow the search scope for all messages in that conversation. This is useful when you want to focus the conversation on specific documents, date ranges, or categories.
- HTTP
- curl
POST {{baseUrl}}/qna/agents/{{agentId}}/conversations
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
{
"question": "What are the latest security policies?",
"dynamicFilter": {
"logicalOperator": "And",
"conditions": [
{
"field": "DocumentType",
"type": "Text",
"operator": "Equals",
"value": "Policy"
},
{
"field": "Department",
"type": "Text",
"operator": "Equals",
"value": "Security"
},
{
"field": "LastModified",
"type": "Date",
"operator": "GreaterThan",
"value": "2024-01-01"
}
]
}
}
curl -X POST "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"question": "What are the latest security policies?",
"dynamicFilter": {
"logicalOperator": "And",
"conditions": [
{
"field": "DocumentType",
"type": "Text",
"operator": "Equals",
"value": "Policy"
},
{
"field": "Department",
"type": "Text",
"operator": "Equals",
"value": "Security"
},
{
"field": "LastModified",
"type": "Date",
"operator": "GreaterThan",
"value": "2024-01-01"
}
]
}
}'
Dynamic filters can also be provided when asking follow-up questions (see Part 2). Each message can have its own filter, or you can omit it to use the agent's default behavior. For more details on filter syntax, see the Single-Shot Questions Tutorial.
Dynamic Filter Template Matching
The dynamicFilter you provide at runtime must match the structure defined in the agent's dynamicFilterTemplate, which is configured during agent creation or update. The template defines which fields can be filtered and their types, while the runtime filter provides the actual values.
Example: Agent Configuration with Dynamic Filter Template
When creating or updating an agent, you define which fields users can filter by:
{
"name": "Document Search Agent",
"description": "Agent that allows users to filter by department, document type, and date",
"modelName": "amazon.nova-micro-v1:0",
"sourceIds": ["your-source-id"],
"dynamicFilterTemplate": {
"logicalOperator": "And",
"conditions": [
{
"field": "Department",
"type": "Text"
},
{
"field": "DocumentType",
"type": "Text"
},
{
"field": "LastModified",
"type": "Date"
}
]
}
}
Corresponding Runtime Dynamic Filter
When starting a conversation or asking questions, provide the operator and value for the fields defined in the template:
{
"dynamicFilter": {
"logicalOperator": "And",
"conditions": [
{
"field": "Department",
"type": "Text",
"operator": "Equals",
"value": "Security"
},
{
"field": "DocumentType",
"type": "Text",
"operator": "Equals",
"value": "Policy"
},
{
"field": "LastModified",
"type": "Date",
"operator": "GreaterThan",
"value": "2024-01-01"
}
]
}
}
The fields in your runtime dynamicFilter must match those defined in the agent's dynamicFilterTemplate. You can provide values for all or a subset of the template fields. For more information on configuring agents with dynamic filter templates, see the Managing Agents Tutorial.
Starting a Conversation with Citations
Set enableCitations to true to receive inline citation markers in the answer text, indicating which source documents support each part of the response.
- HTTP
- curl
POST {{baseUrl}}/qna/agents/{{agentId}}/conversations
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
{
"question": "What is the company's leave policy?",
"enableCitations": true
}
curl -X POST "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"question": "What is the company'\''s leave policy?",
"enableCitations": true
}'
enableCitations can also be combined with dynamicFilter and contextObjectIds in the same request. When omitted or set to false, citation markers are not included in the answer.
Part 2: Ask Follow-Up Questions
Once you have a conversationId, you can continue the conversation by sending follow-up questions. The agent will use previous messages in the conversation as context.
- HTTP
- curl
POST {{baseUrl}}/qna/agents/{{agentId}}/conversations/{{conversationId}}/messages
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
{
"question": "Can you provide more details about the timeline?",
"contextObjectIds": null,
"dynamicFilter": null
}
AGENT_ID="project-agent-id"
CONVERSATION_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
curl -X POST "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/${CONVERSATION_ID}/messages" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"question": "Can you provide more details about the timeline?",
"contextObjectIds": null,
"dynamicFilter": null
}'
Response
The response contains a ConversationMessageDto with the new question and answer, similar to the initial message:
{
"id": "b2c3d4e5-f6a7-8901-2345-678901abcdef",
"answer": "The latest update shows the project timeline is on track with an expected completion date of Q2 2026.",
"documentReferences": [
{
"documentId": "doc002",
"references": [
{
"referenceId": "ref002",
"rankScore": 0.92,
"rank": 1
}
]
}
],
"question": "Can you provide more details about the timeline?",
"feedback": null,
"staticFilter": null,
"dynamicFilter": null,
"dateCreated": "2025-12-02T10:35:00Z",
"dateAnswered": "2025-12-02T10:35:03Z",
"agentVersion": 1,
"status": "Answered"
}
The agent understands that "the timeline" refers to the project mentioned in the first question, demonstrating context retention across the conversation.
Follow-up messages also accept enableCitations (optional boolean). Set it to true on individual messages to request citations for that specific response, independently of whether citations were enabled for the conversation's first message.
Part 3: Streaming Conversations
Instead of waiting for a complete answer, you can observe the agents progress as a Server-Sent Events (SSE) stream. This reduces perceived latency and allows you to follow the models reasoning.
Start a Conversation with Streaming
- HTTP
- curl
POST {{baseUrl}}/qna/agents/{{agentId}}/conversations/stream
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
Accept: text/event-stream
{
"question": "What is the status of the project?"
}
AGENT_ID="project-agent-id"
curl -X POST "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/stream" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
--no-buffer \
-d '{"question": "What is the status of the project?"}'
Continue a Conversation with Streaming
- HTTP
- curl
POST {{baseUrl}}/qna/agents/{{agentId}}/conversations/{{conversationId}}/messages/stream
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
Accept: text/event-stream
{
"question": "When is the new deadline?"
}
AGENT_ID="project-agent-id"
CONVERSATION_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
curl -X POST "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/${CONVERSATION_ID}/messages/stream" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
--no-buffer \
-d '{"question": "When is the new deadline?"}'
SSE Event Reference
Each line of the stream is an SSE event with the format data: <json>\n\n. The event field in the JSON payload identifies the event type:
| Event type | Description |
|---|---|
stream.started | Emitted once at the start of the stream. Contains conversationId and messageId. |
stream.delta | Consecutive steps of Chain of Thought. This can be either Model reasoning or tool call results. |
stream.tool_call | Emitted when the agent invokes an internal tool (e.g., search). Contains name and optional arguments. |
stream.completed | Final event when the full answer is ready. Contains the complete message object and, for new conversations, a conversation object. |
response.incomplete | Emitted instead of stream.completed when the answer is blocked by a guardrail (content filter). Contains the message object with status: "Blocked" and a null answer, and, for new conversations, a conversation object. |
stream.error | Emitted if an error occurs. Contains error (description) and optional code. |
Example stream output:
data: {"event":"stream.started","conversationId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","messageId":"f1e2d3c4-b5a6-7890-1234-567890abcdef"}
data: {"name":"search_vector_db","arguments":"{\u0022natural_language_question\u0022: \u0022What are the procedures and steps to request or obtain PTO (Paid Time Off)?\u0022}","event":"stream.tool_call"}
data: {"delta":"{\n \u0022results\u0022: [],\n \u0022_total_results\u0022: 0\n}","event":"stream.delta"}
data: {"event":"stream.completed","message":{"id":"f1e2d3c4-b5a6-7890-1234-567890abcdef","answer":"Based on the documents, the project is currently in progress.","documentReferences":[...],"question":"What is the status of the project?","status":"Answered",...},"conversation":{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","lastModified":"2025-12-02T10:30:05Z"}}
Use the stream.completed event to retrieve the full structured response including documentReferences. The conversation field in stream.completed is only populated for the first message (start conversation); it is null for follow-up messages.
If the agent's guardrail blocks the response, you will receive response.incomplete instead of stream.completed. In that case, message.status is "Blocked" and message.answer is null.
Part 4: List Conversation Messages
You can retrieve the history of messages within a conversation using cursor-based pagination.
- HTTP
- curl
GET {{baseUrl}}/qna/agents/{{agentId}}/conversations/{{conversationId}}/messages?pageSize=50&maxContentLength=500
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
AGENT_ID="project-agent-id"
CONVERSATION_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
curl -X GET "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/${CONVERSATION_ID}/messages?pageSize=50&maxContentLength=500" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}"
Query Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
cursor | No | null | Cursor token for fetching the next page |
pageSize | No | 100 | Number of items per page (1-100) |
maxContentLength | No | null | Maximum length of question/answer content (10-1000) |
fields | No | null | Comma-separated list of fields to include in response |
Response Structure
{
"data": [
{
"id": "f1e2d3c4-b5a6-7890-1234-567890abcdef",
"answer": "Based on the documents, the project is currently in progress.",
"documentReferences": [
{
"documentId": "doc001",
"references": [
{
"referenceId": "ref001",
"rankScore": 0.95,
"rank": 1
}
]
}
],
"question": "What is the status of the project?",
"feedback": "Good",
"staticFilter": null,
"dynamicFilter": null,
"dateCreated": "2025-12-02T10:30:00Z",
"dateAnswered": "2025-12-02T10:30:05Z",
"agentVersion": 1,
"status": "Answered"
},
{
"id": "b2c3d4e5-f6a7-8901-2345-678901abcdef",
"answer": "The latest update shows the project timeline is on track...",
"documentReferences": [
{
"documentId": "doc002",
"references": [
{
"referenceId": "ref002",
"rankScore": 0.92,
"rank": 1
}
]
}
],
"question": "Can you provide more details about the timeline?",
"feedback": null,
"staticFilter": null,
"dynamicFilter": null,
"dateCreated": "2025-12-02T10:35:00Z",
"dateAnswered": "2025-12-02T10:35:03Z",
"agentVersion": 1,
"status": "Answered"
}
],
"pagination": {
"nextCursor": null,
"hasMore": false
}
}
Understanding Cursor Pagination
Unlike page-based pagination (used in single-shot question history), conversation messages use cursor-based pagination:
| Field | Type | Description |
|---|---|---|
pagination.nextCursor | string (nullable) | Token to fetch the next page; null if no more pages |
pagination.hasMore | boolean | Indicates whether more messages are available |
To fetch the next page, pass the nextCursor value as the cursor query parameter.
Part 5: Get a Single Message
To retrieve a specific message (for example, when a user opens a detailed view), use the message ID.
- HTTP
- curl
GET {{baseUrl}}/qna/agents/{{agentId}}/conversations/{{conversationId}}/messages/{{messageId}}
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
AGENT_ID="project-agent-id"
CONVERSATION_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
MESSAGE_ID="f1e2d3c4-b5a6-7890-1234-567890abcdef"
curl -X GET "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/${CONVERSATION_ID}/messages/${MESSAGE_ID}" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}"
The response is a single ConversationMessageDto object with full details about the message.
Part 6: Provide Feedback on Messages
Feedback on conversation answers is recorded per message, similar to single-shot questions.
- HTTP
- curl
POST {{baseUrl}}/qna/agents/{{agentId}}/conversations/{{conversationId}}/messages/{{messageId}}/feedback
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
{
"feedback": "Good"
}
AGENT_ID="project-agent-id"
CONVERSATION_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
MESSAGE_ID="f1e2d3c4-b5a6-7890-1234-567890abcdef"
curl -X POST "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/${CONVERSATION_ID}/messages/${MESSAGE_ID}/feedback" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{"feedback": "Good"}'
feedback (enum)
| Value | Meaning |
|---|---|
Good | Answer was helpful and accurate |
Bad | Answer was not helpful or contained errors |
Retry | Request to regenerate the answer |
Part 7: Manage Conversations
List All Conversations for an Agent
Retrieve all conversations associated with an agent.
- HTTP
- curl
GET {{baseUrl}}/qna/agents/{{agentId}}/conversations?pageSize=20
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
AGENT_ID="project-agent-id"
curl -X GET "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations?pageSize=20" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}"
Query Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
cursor | No | null | Cursor token for fetching the next page |
pageSize | No | 20 | Number of items per page (1-100) |
Response
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Project Status Discussion",
"description": "Conversation about current project status",
"lastModified": "2025-12-02T10:35:00Z"
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": null,
"description": null,
"lastModified": "2025-12-01T15:20:00Z"
}
],
"pagination": {
"nextCursor": "eyJsYXN0SWQiOiJiMmMzZDRlNS1mNmE3LTg5MDEtYmNkZS1mMTIzNDU2Nzg5MDEiLCJsYXN0TW9kaWZpZWQiOiIyMDI1LTEyLTAxVDE1OjIwOjAwWiJ9",
"hasMore": true
}
}
Get Conversation Details
Retrieve metadata for a specific conversation.
- HTTP
- curl
GET {{baseUrl}}/qna/agents/{{agentId}}/conversations/{{conversationId}}
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
AGENT_ID="project-agent-id"
CONVERSATION_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
curl -X GET "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/${CONVERSATION_ID}" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}"
Update Conversation Metadata
Update the name and description of a conversation for better organization.
- HTTP
- curl
PUT {{baseUrl}}/qna/agents/{{agentId}}/conversations/{{conversationId}}
Authorization: Bearer {{accessToken}}
Hxp-Environment: {{environmentKey}}
Hxp-App: {{appKey}}
Content-Type: application/json
{
"name": "Project Status Q2 2025",
"description": "Discussion about project timeline and deliverables for Q2"
}
AGENT_ID="project-agent-id"
CONVERSATION_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
curl -X PUT "${DISCOVERY_API_BASE_URL}/qna/agents/${AGENT_ID}/conversations/${CONVERSATION_ID}" \
-H "Authorization: Bearer ${DISCOVERY_ACCESS_TOKEN}" \
-H "Hxp-Environment: ${ENVIRONMENT_KEY}" \
-H "Hxp-App: ${APP_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "Project Status Q2 2025",
"description": "Discussion about project timeline and deliverables for Q2"
}'
Update Request Parameters
| Field | Required | Description |
|---|---|---|
name | Yes | Conversation name (for display/organization) |
description | No | Optional longer description of the conversation purpose |
Summary
In this tutorial, you learned how to:
✅ Start multi-turn conversations with agents
✅ Ask follow-up questions that use previous context
✅ Receive incremental responses using SSE streaming endpoints
✅ List and retrieve conversation messages
✅ Provide feedback on conversation messages
✅ Manage conversation metadata (list, view, update)
Next Steps
- Learn about Single-Shot Questions for one-off queries
- Review the API Reference for complete endpoint documentation