Search Agent API
Search Agent API
This is for:
Developer|
|
Coveo Conversational Search is a paid product extension that enables the Coveo Search Agent. Contact Coveo Sales or your Account Manager to add Coveo Conversational Search to your organization license. |
You can use the Coveo Search Agent to add a conversational search experience to a Coveo-powered search interface. It uses agentic AI capabilities, such as reasoning and decision-making, to orchestrate multiple rounds of content retrieval and answer generation based on follow-up questions.
If you’re building your interface with one of the Coveo Search UI libraries (Atomic, Headless, Quantic), you don’t need to call the Search Agent API directly. These libraries handle the Agent API integration internally (see Configure a search interface for the Search Agent). This article is for developers who need to call the API at the HTTP level without relying on a Coveo Search UI library (for example, for native mobile applications or other non-JavaScript environments).
|
|
Migration from
/preview to /v1
The Search Agent API has moved from the Preview route to a stable, versioned GA route.
All production integrations must use For example:
The request and response schemas are unchanged. Only the route prefix changes. If you use Coveo UI components (Atomic, Headless, Quantic, or Coveo for Salesforce), upgrade to a version that targets |
Prerequisites
Before you begin, make sure you have:
-
An access token: Either an API key for the Search API that was created using the Anonymous search template or a search token that grants the Allowed access level on the Execute queries domain.
Pass the token as a bearer token in the
Authorizationheader of each request:Authorization: Bearer <MY_TOKEN> -
A Search Agent configured in your organization. You’ll need the agent’s ID for all API requests documented in this article.
To get the Search Agent ID, access the Agents (platform-ca | platform-eu | platform-au) page of the Coveo Administration Console, click the Search Agent, and then click View.
-
Incremental event parsing. The Search Agent API is streaming only. Responses are delivered as Server-Sent Events (SSE) and there’s no option to receive the full response in a single payload. Your client must support incremental event parsing.
Generate a head answer
To generate an initial answer, send a POST request to the following endpoint.
POST https://<ORG_ID>.org.coveo.com/api/v1/organizations/<ORG_ID>/agents/<AGENT_ID>/answer HTTP/1.1
Content-Type: application/json
Accept: text/event-stream, application/json
Authorization: Bearer <MY_TOKEN>
Where:
-
<ORG_ID>is your Coveo organization ID. -
<AGENT_ID>is the unique identifier of the agent you want to query.
Request body
The request body is a JSON object.
The only required field is q: the user’s question (maximum 300 characters).
For the full list of accepted fields, see the Knowledge Generative AI API reference.
Example request
curl -X POST \
'https://<ORG_ID>.org.coveo.com/api/v1/organizations/<ORG_ID>/agents/<AGENT_ID>/answer' \
-H 'Authorization: Bearer <MY_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream, application/json' \
-d '{
"q": "How do I reset my password?"
}'
Replace <ORG_ID> with your organization ID and <AGENT_ID> with your Search Agent’s ID. |
|
Replace <MY_TOKEN> with your access token (API key or search token).
See Prerequisites for details. |
|
The Accept header must include text/event-stream to receive the SSE stream.
Including application/json (as shown) is also important so that validation errors are returned as JSON rather than a 406 Not Acceptable response. |
|
| The user question (maximum 300 characters): the only required field. |
Response format
The response is a Server-Sent Events (SSE) stream. The server sends events incrementally as the answer is generated. The client must parse these events as they arrive rather than wait for the full response.
Each event follows the AG-UI protocol format:
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"To reset your password, "}
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"navigate to the login page..."}
Parse the event stream
The Agent API streams the response as a sequence of events following the AG-UI protocol.
Each event is delivered as a Server-Sent Event with a data: line containing a JSON object.
The type field identifies the event type.
For the full list of AG-UI event types, their properties, and flow diagrams, see the AG-UI Events documentation.
The Coveo Search Agent uses the following event types during answer generation:
| Event type | Description |
|---|---|
|
The agent run has begun.
Capture |
|
Generation steps (for example, |
|
Internal tool invocations (for example, content retrieval).
For |
|
A text fragment of the answer.
Accumulate all |
|
Metadata events identified by |
|
The run completed.
Check |
|
An error occurred. See Error handling. |
Event payload examples
The following examples show the JSON payload for the most important events.
RUN_STARTED
{"type": "RUN_STARTED", "runId": "conv_q2lp4shyb2w3nyanngvm6ukloi_0001", "threadId": "conv_q2lp4shyb2w3nyanngvm6ukloi"}
TEXT_MESSAGE_CHUNK
{"type": "TEXT_MESSAGE_CHUNK", "delta": "To reset your password, "}
CUSTOM (header)
{"type": "CUSTOM", "name": "header", "value": {"contentFormat": "text/markdown", "followUpEnabled": true, "conversationToken": "eyJ..."}}
CUSTOM (citations)
{"type": "CUSTOM", "name": "citations", "value": {"citations": [{"title": "Reset guide", "uri": "https://...", "text": "..."}]}}
RUN_FINISHED
{"type": "RUN_FINISHED", "result": {"completionReason": "ANSWERED"}}
RUN_ERROR
{"type": "RUN_ERROR", "code": "KNOWLEDGE:SSE_INTERNAL_ERROR", "message": "An internal error occurred"}
Completion reason
The RUN_FINISHED event carries a result.completionReason field that indicates the outcome of the generation:
-
ANSWERED: The agent successfully generated an answer. -
Any other value: The agent couldn’t produce an answer. This isn’t an error; the stream completed normally, but the agent determined it couldn’t answer the question given the available content.
Your client should check this field to decide whether to display the accumulated answer text or show a "no answer available" message.
|
|
Note
The AG-UI protocol defines the event envelope format and streaming semantics. For full protocol details beyond the Coveo-specific events documented here, see the AG-UI protocol. |
header custom event
The header custom event is sent early in the stream, before the answer text begins.
It provides metadata that your client needs to correctly render the response and enable follow-up conversations.
The value object contains the following fields:
| Field | Type | Description |
|---|---|---|
|
|
The format of the answer text: |
|
|
Whether the agent supports follow-up conversations.
If |
|
|
A cryptographic proof token that the client must include in follow-up requests to authenticate the continuation of the conversation. |
{
"type": "CUSTOM",
"name": "header",
"value": {
"contentFormat": "text/markdown",
"followUpEnabled": true,
"conversationToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
citations custom event
The citations custom event is sent toward the end of the stream, after all TEXT_MESSAGE_CHUNK events.
It contains the source documents the agent used to generate the answer.
The value object contains a citations array.
Each citation object has the following fields:
| Field | Type | Description |
|---|---|---|
|
|
A unique identifier for the citation. |
|
|
The title of the source document. |
|
|
The original URI of the source document in the index. |
|
|
The URI to open when the user clicks the citation link. |
|
|
A permanent identifier for the source document, stable across re-indexing. |
|
|
A relevant text snippet from the source document that supports the answer. |
|
|
The name of the source from which the document was indexed. |
{
"type": "CUSTOM",
"name": "citations",
"value": {
"citations": [
{
"id": "cite-1",
"title": "How to reset your password",
"uri": "https://example.com/kb/reset-password",
"clickUri": "https://example.com/kb/reset-password",
"permanentid": "abc123def456",
"text": "Navigate to the login page and click Forgot password...",
"source": "Knowledge Base"
}
]
}
}
Event sequence
The following ordered list shows the typical progression of events from request to completion.
-
RUN_STARTED: The run begins. CapturethreadIdas theconversationIdfor follow-up requests. -
CUSTOM (header): Metadata includingconversationToken,contentFormat, andfollowUpEnabled. CaptureconversationTokenfor follow-ups. -
STEP_STARTED (searching): The retrieval step begins. -
TOOL_CALL_START: The agent invokes an internal search tool. -
TOOL_CALL_ARGS: The tool call arguments (for example, the query the agent formulated). -
TOOL_CALL_END: The tool call completes. -
STEP_FINISHED (searching): The retrieval step ends. -
STEP_STARTED (answering): The answer generation step begins. -
TEXT_MESSAGE_CHUNK: A text fragment of the answer. Repeats to accumulate alldeltavalues. -
TEXT_MESSAGE_CHUNK: Continues until the full answer is streamed. -
CUSTOM (citations): The citations array referencing source documents. -
STEP_FINISHED (answering): The generation step ends. -
RUN_FINISHED: The run completes. CheckcompletionReasonto confirm the answer was generated (ANSWERED).
|
|
Notes
|
Stream-parsing algorithm
The following example shows a language-agnostic algorithm that parses the event stream, accumulates the answer text from TEXT_MESSAGE_CHUNK deltas, and extracts the values needed for follow-up requests.
fullText = ""
conversationId = null
conversationToken = null
contentFormat = null
citations = []
FOR EACH event IN stream:
SWITCH event.type:
CASE "RUN_STARTED":
conversationId = event.threadId
CASE "CUSTOM":
IF event.name == "header":
conversationToken = event.value.conversationToken
contentFormat = event.value.contentFormat
ELSE IF event.name == "citations":
citations = event.value.citations
CASE "TEXT_MESSAGE_CHUNK":
fullText += event.delta
CASE "RUN_FINISHED":
IF event.result.completionReason == "ANSWERED":
display(fullText, citations)
ELSE:
showNoAnswer()
CASE "RUN_ERROR":
handleError(event.code, event.message)
Hold the conversationID for follow-up requests. |
|
Hold the conversationToken for follow-up requests. |
|
Extract conversationId from the RUN_STARTED event’s threadId field. |
|
Extract conversationToken from the header custom event’s value.conversationToken field. |
|
Accumulate each text delta: the full answer is the concatenation of all TEXT_MESSAGE_CHUNK deltas. |
|
Once RUN_FINISHED confirms the answer was generated, display the full text along with citations. |
After the stream ends, use conversationId and conversationToken in the follow-up request body to continue the conversation.
Full event stream example
The following collapsible block shows a complete, annotated Server-Sent Events (SSE) stream from start to finish.
Each event is shown in its raw SSE wire format (event: and data: lines).
Complete event stream example
event: RUN_STARTED
data: {"type":"RUN_STARTED","runId":"conv_q2lp4shyb2w3nyanngvm6ukloi_0001","threadId":"conv_q2lp4shyb2w3nyanngvm6ukloi"}
event: CUSTOM
data: {"type":"CUSTOM","name":"header","value":{"contentFormat":"text/markdown","followUpEnabled":true,"conversationToken":"eyJhbGciOiJIUzI1NiIs..."}}
event: STEP_STARTED
data: {"type":"STEP_STARTED","stepName":"searching","startedAt":"2025-07-15T10:00:01Z"}
event: TOOL_CALL_START
data: {"type":"TOOL_CALL_START","toolCallId":"tc-001","toolCallName":"searchRetrieval","startedAt":"2025-07-15T10:00:01Z"}
event: TOOL_CALL_ARGS
data: {"type":"TOOL_CALL_ARGS","toolCallId":"tc-001","delta":"{\"q\":\"how to reset password\"}"}
event: TOOL_CALL_END
data: {"type":"TOOL_CALL_END","toolCallId":"tc-001","finishedAt":"2025-07-15T10:00:02Z"}
event: STEP_FINISHED
data: {"type":"STEP_FINISHED","stepName":"searching","finishedAt":"2025-07-15T10:00:02Z"}
event: STEP_STARTED
data: {"type":"STEP_STARTED","stepName":"answering","startedAt":"2025-07-15T10:00:03Z"}
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"To reset your "}
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"password, navigate to the "}
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"login page and click "}
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"**Forgot password**. "}
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"Enter your email address "}
event: TEXT_MESSAGE_CHUNK
data: {"type":"TEXT_MESSAGE_CHUNK","delta":"and follow the instructions sent to your inbox."}
event: CUSTOM
data: {"type":"CUSTOM","name":"citations","value":{"citations":[{"id":"cite-1","title":"How to reset your password","uri":"https://example.com/kb/reset-password","clickUri":"https://example.com/kb/reset-password","permanentid":"abc123def456","text":"Navigate to the login page and click Forgot password...","source":"Knowledge Base"}]}}
event: STEP_FINISHED
data: {"type":"STEP_FINISHED","stepName":"answering","finishedAt":"2025-07-15T10:00:05Z"}
event: RUN_FINISHED
data: {"type":"RUN_FINISHED","result":{"completionReason":"ANSWERED"}}

RUN_STARTED: The run begins.
Capture threadId (conv_q2lp4shyb2w3nyanngvm6ukloi) as conversationId. |
|
CUSTOM (header): Capture conversationToken for follow-ups.
Note contentFormat is text/markdown. |
|
Searching step: The agent retrieves relevant passages.
Includes a tool call showing the formulated search query. |
|
Answering step: The agent begins generating the answer text. |
|
TEXT_MESSAGE_CHUNK: Text deltas arrive incrementally.
Concatenate all delta values to build the full answer: "To reset your password, navigate to the login page and click Forgot password.
Enter your email address and follow the instructions sent to your inbox." |
|
CUSTOM (citations): The source documents the agent used to generate the answer. |
|
RUN_FINISHED: completionReason is ANSWERED, confirming the answer was successfully generated. |
Follow-up conversations
The Agent API supports multi-turn conversations through a follow-up mechanism. After generating a head answer, the client can send follow-up questions that share the same conversational context, allowing the agent to produce answers that build on prior turns.
|
|
|
Capture conversation identity from the head answer
To send a follow-up, your client must capture two values from the head answer stream:
| Value | Source | Purpose |
|---|---|---|
|
|
Identifies the conversation thread. |
|
|
A cryptographic proof token that authenticates the client as the originator of the conversation. |
Both values are required in every follow-up request. See the stream-parsing algorithm for an example showing how to extract these values during stream parsing.
Follow-up endpoint
Send a follow-up question using a POST request to the following endpoint:
POST https://<ORG_ID>.org.coveo.com/api/v1/organizations/<ORG_ID>/agents/<AGENT_ID>/follow-up HTTP/1.1
Content-Type: application/json
Accept: text/event-stream, application/json
Authorization: Bearer <MY_TOKEN>
Where:
-
<ORG_ID>is your Coveo organization ID. -
<AGENT_ID>is the unique identifier of the agent you want to query.
Request body
The request body requires the follow-up question and the conversation identifiers captured from the head answer stream. For the full schema, see the Knowledge Generative AI API reference.
Example request
The following curl command sends a follow-up question referencing a prior conversation:
curl -X POST \
'https://<ORG_ID>.org.coveo.com/api/v1/organizations/<ORG_ID>/agents/<AGENT_ID>/follow-up' \
-H 'Authorization: Bearer <MY_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream, application/json' \
-d '{
"q": "What about pricing?",
"conversationId": "conv_q2lp4shyb2w3nyanngvm6ukloi",
"conversationToken": "eyJhbGciOiJIUzI1NiIs..."
}'
Replace <ORG_ID> with your organization ID and <AGENT_ID> with your agent’s ID. |
|
| The follow-up question. | |
The conversationId captured from RUN_STARTED.threadId in the head answer stream. |
|
The conversationToken captured from the header custom event in the head answer stream. |
Response format
The follow-up response uses the same AG-UI event stream format as the head answer. Your client can reuse the same stream-parsing logic described in the Parse the event stream section. The events, their order, and their payloads are identical.
Follow-up not supported
When an agent has followUpEnabled set to false, follow-up requests are rejected.
The backend returns a RUN_ERROR event with the code KNOWLEDGE:SSE_FOLLOW_UP_NOT_SUPPORTED.
Your client should check the followUpEnabled field in the header custom event during the head answer stream.
If the value is false, don’t offer a follow-up input to the user and don’t send follow-up requests.
Analytics and event tracking
For analytics dashboards and machine learning models to work correctly, your client must log usage analytics events to the Usage Analytics Write API. The Search Agent doesn’t log these events on your behalf. Your client is responsible for sending them.
There are three types of events to log:
Search events
Log a search event when the user submits a query that triggers answer generation. This is the event that feeds Coveo ML models to improve relevance over time.
The search event must include the searchQueryUid that identifies the query, and should be sent before or alongside the answer generation request.
Click events
Log a click event when the user clicks a citation link in the head answer. Clicks on citations for the first answer are tracked as standard click events, which means they feed ML models.
|
|
Note
Clicks on citations for follow-up answers are tracked as custom events (not click events), so they don’t feed ML models directly. |
Custom events
Log custom events for answer lifecycle and user interactions. These events power reporting dashboards but don’t feed ML models.
The following table lists the key custom events to log:
| eventValue: | When to log |
|---|---|
|
After the answer finishes streaming.
Include |
|
User clicked thumbs up. |
|
User clicked thumbs down. |
|
User clicked a citation link (for follow-up answer citations). |
|
User hovered a citation. Include |
|
User copied the answer. |
All custom events use eventType: "generatedAnswer" and include generativeQuestionAnsweringId in customData.
For the full list of events and how to set up dashboards, see Search Agent reports and UA events.
About the analytics object in the request body
The head answer and follow-up endpoints accept an optional analytics object.
For production implementations, set capture to false and rely on client-side event logging as described in the previous section.
Nonetheless, even when capture is set to false, including user_id in the analytics object is recommended to unlock advanced reporting in Snowflake reports.
|
|
Note
The |
Error handling
Errors can occur at two levels when interacting with the Agent API: at the transport level (before streaming begins) or at the stream level (during an active SSE stream). Your client should handle both categories.
Transport-level errors
Transport-level errors occur before the SSE stream is established. These are standard HTTP failures, meaning that the server never begins streaming events.
Common transport-level errors include:
-
Network error: Connection refused, DNS resolution failure, or request timeout. The client never receives a response.
-
Non-2xx HTTP status: The server responds with an error status code before streaming begins:
-
401 Unauthorized: The access token is missing, expired, or invalid. -
403 Forbidden: The token lacks the required privileges. -
404 Not Found: The agent ID or endpoint path is incorrect.
-
For transport-level errors, your client should:
-
Check the HTTP status code (if available) or detect the network failure.
-
Display an appropriate error message to the user.
-
Allow the user to retry the request.
Stream-level errors
Stream-level errors occur during an active SSE stream.
The server sends a 200 RUN_ERROR event with a code and a human-readable message:
{"type": "RUN_ERROR", "code": "KNOWLEDGE:SSE_INTERNAL_ERROR", "message": "An internal error occurred"}
When your client receives a RUN_ERROR event, it should stop processing the stream, interpret the code to determine what happened, and take the appropriate action.
Error codes
The following table lists the most common error codes, their meanings, and the recommended client behavior.
| Code | Meaning | Recommended client behavior |
|---|---|---|
|
The SSE stream exceeded the maximum allowed duration. |
Show an error message and allow the user to retry. |
|
A follow-up was attempted on an agent that doesn’t support it. |
Hide the follow-up input.
This error shouldn’t normally appear if |
|
The conversation ID is invalid or expired. |
Show an error on the follow-up turn and suggest starting a new conversation. |
|
The underlying ML models are temporarily unavailable. |
Show an error message and allow the user to retry. |
|
An internal server error occurred. |
Show a generic error message and allow the user to retry. |
|
The maximum number of conversation turns has been reached. |
Show a message indicating the conversation limit is reached and disable the follow-up input. |
No answer generated
A stream that completes normally with a RUN_FINISHED event but where completionReason isn’t ANSWERED isn’t an error.
The agent couldn’t produce an answer for the given query.
This can happen when:
-
No relevant content exists in the index for the question.
-
Query filters (
aq,cq, or facets) exclude all matching results. -
User permissions hide the content that would have been relevant.
-
The query is empty or too ambiguous for the agent to formulate an answer.
-
The question is blocked by the agent guardrails (for example, questions about malicious or harmful topics).
In this scenario, your client should:
-
Check
result.completionReasonin theRUN_FINISHEDevent. -
If the value isn’t
ANSWERED, display a "no answer available" message instead of the (empty) accumulated text. -
Don’t show an error indicator. The request succeeded, but the agent chose not to answer.
{"type": "RUN_FINISHED", "result": {"completionReason": "NO_ANSWER"}}
API reference
For the full request and response schemas, see the Knowledge Generative AI API reference.