Marqo Conversational Agent API
Overview
The Conversational Agent API provides intelligent, conversational product discovery with query expansion, contextual recommendations, and real-time streaming responses. The system understands user intent, asks clarifying questions when needed, and organizes results into meaningful categories with natural language explanations.
Prerequisites
- A Marqo Cloud account
- Your
x-marqo-index-idvalue for shopper-facing endpoints (copy it from the Quick Start code snippets for your index in the Marqo Console) - Your Marqo API key for settings and management endpoints (find your API key)
- An existing ecommerce index with products (add products guide)
- Access to the Conversational Agent API (contact Marqo for access). Until it is enabled, conversational search, Upload Images, Conversation Starters, Query Conversation Starters, and PDP Query Suggestions return
403Agentic chat is not enabled for this index. The conversation endpoints (Get Conversation, Get Conversation Message, Submit Message Feedback, List Conversations, Delete Conversation) do not check this flag, so they answer normally for conversations that already exist
Authentication
Two credentials are used on this page:
x-marqo-index-id: {index_id}is the public credential for shopper-facing endpoints: conversational search, image upload, conversation starters, PDP query suggestions, and reading, listing, and giving feedback on conversations. It is safe to use from a browser or storefront.Authorization: Bearer {api_key}is required by the management endpoints: catalog context settings, deleting a conversation, Reviews, and Help Center Content. These never acceptx-marqo-index-id. Call them from your backend so the key is never exposed in client-side code.
A request with neither credential, or with an invalid one, returns 401 Unauthorized.
Requiring API keys on shopper-facing endpoints
On request, Marqo can configure your index so that the shopper-facing endpoints also require an API key. When that is on, each request must carry Authorization: Bearer {api_key} for the same index, and the key's scope must match the endpoint:
| Endpoints | Minimum key scope |
|---|---|
Conversational search (/converse), Upload Images, Submit Message Feedback | Read-Write |
| Conversation Starters, Query Conversation Starters, PDP Query Suggestions, Get Conversation, Get Conversation Message, List Conversations | Read |
A request with only x-marqo-index-id, or with a key that belongs to a different index, returns 401 Unauthorized. A key with insufficient scope returns 403 Insufficient API key scope. The management endpoints above always enforce these scopes: catalog context reads need a Read key, catalog context updates and deleting a conversation need a Read-Write key, and reindexing help-center content needs an Admin key. See Find Your API Key for the scopes.
Channels
Marqo can configure channel-specific conversational settings for your index (for example a mobile app with a different catalog context). The channel query parameter selects that configuration on conversational search, Upload Images, Conversation Starters, Query Conversation Starters, PDP Query Suggestions, the catalog context settings, and the Reviews endpoints. Use letters, numbers, hyphens, or underscores; maximum 64 characters. Omit it to use the default configuration. A channel inherits every setting it does not override from the default configuration, including the catalog context.
A channel that is malformed, or that Marqo has not configured, is rejected before the request is processed:
| Status | Message | When |
|---|---|---|
400 | Channel ID is required. | channel is present but empty |
400 | Channel ID must be at most 64 characters. | The value is longer than 64 characters |
400 | Channel ID must contain only alphanumerics, hyphens, and underscores. | The value contains any other character |
500 | Agentic config for channel "{channel}" is not configured. | The value is well-formed but Marqo has not configured that channel for your index |
The catalog context settings endpoints reject the same format errors with 422 rather than 400; see Catalog Context Settings.
Endpoints
Conversational Search
Stream conversational search results with categorized products and intelligent messaging. This is the main endpoint for conversational product discovery.
Endpoint: GET /indexes/{index_name}/agentic-search/converse
Headers:
x-marqo-index-id: {index_id}(required)x-marqo-shopify-customer-token: {customer_token}(optional) — the logged-in shopper's Shopify customer access token. When present, the agent can answer order-related questions (e.g. "where is my last order?") scoped to that shopper. See Order Lookup.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
payload | string | Yes | Base64-encoded JSON containing the request parameters (see below) |
channel | string | No | Optional channel identifier for routing the request through a channel-specific conversational experience. Use letters, numbers, hyphens, or underscores; maximum 64 characters. See Channels. |
Payload Parameters (JSON, then base64-encoded):
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
q | string | Yes | - | User query. Maximum 2048 characters by default |
extraContext | string | No | - | Additional context to pass to the agent before the user query (see Extra Context). Maximum 2048 characters by default |
sessionId | string | No | null | Session identifier |
userId | string | No | null | User identifier |
conversationId | string | No | - | Conversation identifier (returned from a previous response) for maintaining context |
categoryResultLimit | integer | No | 6 | Number of results per category. Must be 1 or greater |
maxCategories | integer | No | 4 | Maximum number of categories the agent will return. Must be 1 or greater |
filter | string | No | - | Query filter using Marqo's filter DSL (e.g., "availability:true") |
attributesToRetrieve | array | No | Default fields | Product fields to return (e.g., ["productTitle", "variantTitle", "price", "variantImageUrl", "_id"]) |
documentIds | array | No | - | Product document IDs to use as context (e.g., for PDP page questions) |
contextFields | array | No | All fields | Fields to include from the document context (e.g., ["material", "description"]) |
imageUrls | array | No | - | Publicly reachable image URLs to include in the query (must be valid URLs). imageUrls and imageIds together may hold at most 5 images |
imageIds | array | No | - | IDs of images previously uploaded with Upload Images. Use this for images the shopper supplies from their device. Counted together with imageUrls toward the limit of 5 |
categoriesAfterMessage | boolean | No | false | When true, category-hits events are always sent after the message events (see Response Event Ordering) |
divisionSeed | string | No | - | Hint which product division the user is browsing from (see Division Seed) |
The 2048-character limits on q and extraContext are defaults. Marqo can configure different limits for your index, and the 413 message reports the limit actually in force.
Marqo runs two generations of the conversational agent, and your index is on one of them. On indexes running the newer generation, q may be omitted when imageUrls or imageIds are supplied, so a shopper can send an image on its own; a turn with neither text nor images is rejected with 400 q: q or at least one image is required. On indexes running the earlier generation, q is always required. Send q whenever you have it and your integration works on both.
Example Request: With Product Context
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data-urlencode "payload=$(echo -n '{
"conversationId": "3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9",
"documentIds": ["1298739082732"],
"q": "I need outfits for a tropical vacation",
"sessionId": "session-xyz789",
"userId": "user-123",
"categoryResultLimit": 6,
"filter": "availability:true",
"attributesToRetrieve": [
"productTitle",
"variantTitle",
"price",
"variantImageUrl",
"_id"
]
}' | base64 | tr -d '\n')"
Example Request: Simple Query
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data-urlencode "payload=$(echo -n '{
"q": "red dress",
"sessionId": "session-001",
"userId": "user-456"
}' | base64 | tr -d '\n')"
Example Request: With Channel
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data-urlencode 'channel=mobile-app' \
--data-urlencode "payload=$(echo -n '{
"q": "red dress",
"sessionId": "session-001",
"userId": "user-456"
}' | base64 | tr -d '\n')"
Example Request: With Extra Context
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data-urlencode "payload=$(echo -n '{
"q": "show me matching bags",
"extraContext": "The customer is browsing vegan leather accessories.",
"sessionId": "session-001",
"userId": "user-456"
}' | base64 | tr -d '\n')"
Example Request: With Uploaded Images
Upload the shopper's images first with Upload Images, then pass the returned IDs:
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data-urlencode "payload=$(echo -n '{
"q": "find me a jacket that goes with these",
"imageIds": ["img_3f9c2a1b8d4e4c6f9a1b2c3d4e5f6a7b"],
"sessionId": "session-001",
"userId": "user-456"
}' | base64 | tr -d '\n')"
Error Responses
These errors are returned before the stream starts, with the standard error body {"error": "..."}:
403Agentic chat is not enabled for this index400Query parameter 'payload' is required400Invalid base64 JSON payload400with a validation message such asq: q is requiredwhen a payload field fails validation400Invalid filter syntaxwhenfilteris not valid filter DSL400Too many images provided. Maximum is 5, got {n}.400Invalid divisionSeed: "{value}". Allowed values: men, mens, women, womens, kids, kid, beauty400Conversation not found for conversationId: {conversation_id}when the conversation does not exist or has been deleted400This conversation has reached the maximum of 1000 user messages and cannot accept more. Start a new conversation to continue.(see Conversation Retention and Limits)413Query 'q' is too long (max 2048 characters).The number in the message is the limit configured for your index413extraContext is too long (max 2048 characters).The number in the message is the limit configured for your index400with one of the malformedchannelmessages, or500for an unconfigured channel; see Channels401Unauthorizedand403Insufficient API key scopeas described in Authentication
Once the stream has started the HTTP status is already 200; later failures arrive as an error event.
Response Events
The conversational search endpoint streams responses as Server-Sent Events (SSE). Each event has an event: field identifying its type and a data: field containing a JSON payload.
message
Text from the agent's conversational response. Depending on how your index is configured, the reply arrives either as a series of incremental deltas or as a single event containing the whole reply. Always concatenate every message event in order to build the full message; do not assume a fixed number of frames.
event: message
data: {"message":"I'd be happy to help you find the perfect jacket! "}
event: message
data: {"message":"To give you the best recommendations:\n\n- What type of jacket are you looking for?"}
Fields:
message(string): A text delta to append to the agent's response
suggestions
Follow-up query suggestions generated by the agent based on the current conversation context. Display these as clickable prompts to help users continue exploring.
event: suggestions
data: {"suggestions":["show me trending items for her","what are popular tech gifts?","something unique and trending"]}
Fields:
suggestions(array of strings): Suggested follow-up queries the user might want to ask
category-hits
Search results grouped by category.
event: category-hits
data: {"categoryHits":[{"category":"Summer Dresses","query":"summer floral dresses","confidence":0.9,"hits":[{"_id":"dress_001","productTitle":"Red Sequin Dress","price":89.99},{"_id":"dress_002","productTitle":"Black Silk Gown","price":129.99}]}]}
Fields:
categoryHits(array): Array of category groupscategory(string): Category name (e.g., "Summer Dresses", "Casual Jackets")query(string): The search query the agent used to retrieve products in this categoryconfidence(number): The agent's confidence score for this category (0–1)hits(array): Array of product documents matching this categoryappliedFilter(string, optional): The filter that was applied to this category's searchfilterDropped(boolean, optional):trueif the agent's filter was dropped due to producing zero resultsoriginalFilter(string, optional): The filter that was dropped, present whenfilterDroppedistrue
If the search for one category fails, its entry contains category, status (the HTTP status of the failed search), and error instead of hits. Other categories are unaffected.
auth-required
Emitted when the shopper asked an order question that needs a logged-in customer but no valid x-marqo-shopify-customer-token was supplied (see Order Lookup). No message text follows for this turn. The stream still finishes normally with conversation-id and stream-end, so keep the conversationId, prompt the shopper to log in, and continue the conversation with a fresh token.
event: auth-required
data: {"reason":"missing_token","loginUrl":"https://my-store.myshopify.com/account/login"}
Fields:
reason(string): Why authentication is needed. One ofmissing_token(no token was sent),unauthorized_401(the token was rejected, for example because it expired), ornull_customer(the token did not resolve to a customer)loginUrl(string): The store's login page
message-id
Emitted before conversation-id. Use this value when retrieving message details, submitting feedback for a specific agent reply, or correlating the reply in conversation history.
event: message-id
data: {"messageId":"9b4c4b6d-8bfe-4c66-b388-0d97d3ec1e2e"}
Fields:
messageId(string): Unique identifier for the agent message
conversation-id
Emitted near the end of the stream. Use this ID in subsequent requests to continue the conversation.
event: conversation-id
data: {"conversationId":"3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9"}
Fields:
conversationId(string): Unique identifier for this conversation
conversation-end
Emitted after conversation-id when the turn just completed was the last one the conversation can accept (see Conversation Retention and Limits). The reply for this turn is complete and has been saved, but the next request that reuses this conversationId returns 400. Start a new conversation by omitting conversationId.
event: conversation-end
data: {"reason":"This conversation has reached the maximum of 1000 user messages. Your next message in this conversation will return an error. Start a new conversation to continue."}
Fields:
reason(string): Human-readable explanation
error
Emitted when an error occurs after the stream has started. The HTTP status is already 200 at this point, so clients must watch for this event.
event: error
data: {"error":"Failed to retrieve document doc_123: not found","status":404}
Fields:
error(string): Error messagestatus(number, optional): HTTP status code of the underlying failure, when known
Some errors are recoverable and the stream continues, for example:
{"status": 404, "error": "Failed to retrieve document doc_123: ..."}when adocumentIdsentry cannot be fetched{"error": "Image not found or expired: img_..."},{"error": "Image has expired: img_..."}, or{"error": "Image cannot be used for this account: img_..."}when animageIdsentry is unusable. The turn continues without that image{"status": 500, "error": "Failed to save conversation. Please try again."}when the reply was generated but could not be stored. Nomessage-idorconversation-idfollows
Others end the stream, and no stream-end follows:
{"status": 400, "error": "user location not supported"}when the request comes from a region the model does not serve{"error": "Internal server error"}for any unexpected failure
stream-end
Marks the end of the SSE stream. No further events will be sent.
event: stream-end
data: {}
Response Event Ordering
By default, category-hits events are streamed as soon as search results are available, which means they may arrive before the message events. This allows clients to render product results immediately while the agent's text response is still being generated.
If you prefer to display category results after the agent's message (for example, to show the conversational text first), set categoriesAfterMessage to true in the payload. When enabled, category-hits events are buffered internally and sent after all message and suggestions events.
The stream emits message-id before conversation-id. conversation-end, when present, follows conversation-id. auth-required replaces the message events for that turn.
Upload Images
Upload images from the shopper's device so they can be attached to a conversational search turn. The response returns an ID per image; pass those IDs in the imageIds field of the /converse payload. Use imageUrls instead when the image is already hosted at a public URL.
Endpoint: POST /indexes/{index_name}/agentic-search/images
Headers:
Content-Type: application/jsonx-marqo-index-id: {index_id}(required)
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
channel | string | No | Channel identifier; see Channels |
Request Body:
| Parameter | Type | Required | Description |
|---|---|---|---|
images | array | Yes | 1 to 5 images to upload |
Each entry in images has the following fields:
| Parameter | Type | Required | Description |
|---|---|---|---|
dataUrl | string | Yes | The image as a base64 data URL: data:{mime_type};base64,{payload}. The MIME type must be an image/* type |
clientImageId | string | Yes | Your own identifier for the image. It is echoed back in the response and used in error messages so you can match results to inputs |
Limits:
- Up to 5 images per request.
- Supported formats: JPEG, PNG, WebP, BMP, TIFF, ICO, HEIC, and HEIF.
- Maximum size per image (decoded bytes): 5 MB for JPEG, PNG, WebP, BMP, TIFF, and ICO; 1 MB for HEIC and HEIF.
- Images are resized server-side before use. Uploading images larger than a few hundred pixels on the longest side does not improve results.
- Uploaded images are kept for 24 hours and then deleted. Use the IDs within that window; an expired ID produces an
errorevent on/converseand the turn continues without that image. - Image IDs are tied to your account and cannot be used with another account's index.
Example Request:
curl -X POST 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/images' \
--header 'Content-Type: application/json' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data '{
"images": [
{
"clientImageId": "camera-roll-1",
"dataUrl": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..."
}
]
}'
Example Response:
{
"images": [
{
"clientImageId": "camera-roll-1",
"imageId": "img_3f9c2a1b8d4e4c6f9a1b2c3d4e5f6a7b",
"mimeType": "image/jpeg",
"expiresAt": "2026-09-10T03:12:45.000Z"
}
]
}
Response Fields:
images(array): One entry per uploaded image, in request orderclientImageId(string): The identifier you suppliedimageId(string): The ID to pass inimageIdson/conversemimeType(string): The MIME type of the stored image, which may differ from the uploaded type after processingexpiresAt(string): ISO 8601 timestamp after which the image is no longer available
Error Responses:
403Agentic chat is not enabled for this index400Invalid JSON in request body400images: At least one image is required, or another validation message whenimageshas more than 5 entries or an entry is missingdataUrlorclientImageId400Invalid dataUrl format for {clientImageId}. Expected data:<mime>;base64,<payload>400Invalid dataUrl mimeType for {clientImageId}: {mime_type}. Expected an image/* MIME type.400Image payload is empty: {clientImageId}400Image too large for {clientImageId} ({size}KB > {limit}KB limit)400Invalid base64 payload in dataUrl for {clientImageId}400Image format {mime_type} is not supported: {clientImageId}. Supported formats: JPEG, PNG, WebP, BMP, TIFF, HEIC, HEIF.400Failed to process image: {clientImageId}when the bytes cannot be decoded as the declared format500Failed to upload images. Nothing from the request is stored
If any image in the request fails validation, no images from that request are stored.
Catalog Context Settings
Configure persistent catalog context for the conversational agent. Use this when you want the agent to consistently apply store, brand, merchandising, or catalog guidance without passing the same text in every /converse request.
These are server-side settings endpoints. They use your Marqo API key in the Authorization header instead of the x-marqo-index-id header used by shopper-facing conversational endpoints. Do not expose your API key in browser code.
Endpoints:
GET /indexes/{index_name}/agentic-search/settings/converse/catalog-contextPATCH /indexes/{index_name}/agentic-search/settings/converse/catalog-context
Headers:
Authorization: Bearer {api_key}(required)Content-Type: application/json(required forPATCH)
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
channel | string | No | Optional channel scope. Omit it to read or update the default catalog context. Include it to read or update a channel-specific context. Use letters, numbers, hyphens, or underscores; maximum 64 characters. |
Channel-specific catalog context is stored separately from the default scope. Use the same channel value that your clients pass to GET /indexes/{index_name}/agentic-search/converse?channel=... when you want a specific conversational experience to use that context. If no context is stored for the requested scope, catalogContext is null. At request time a channel without its own context uses the default context.
Reads need a Read key or higher; updates need a Read-Write key or higher. Updates can take up to about a minute to reach live conversational requests.
Get Catalog Context
Example Request: Default Scope
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/settings/converse/catalog-context' \
--header 'Authorization: Bearer {api_key}'
Example Response:
{
"scope": "default",
"channel": null,
"catalogContext": "Use concise product language and prioritize in-stock items."
}
Update Catalog Context
PATCH replaces the catalog context for the selected scope while preserving other agentic search settings. The request body must use the catalogContext field. Send null to clear the stored context.
Request Body:
| Parameter | Type | Required | Description |
|---|---|---|---|
catalogContext | string or null | Yes | Persistent context to provide to the conversational agent. Surrounding whitespace is trimmed before the value is stored. Send null, an empty string, or a whitespace-only string to remove the context for this scope. |
Example Request: Channel Scope
curl -X PATCH 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/settings/converse/catalog-context?channel=mobile-app' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"catalogContext": "Use mobile-specific merchandising copy and keep answers short."
}'
Example Response:
{
"scope": "channel",
"channel": "mobile-app",
"catalogContext": "Use mobile-specific merchandising copy and keep answers short."
}
Example Request: Clear Context
curl -X PATCH 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/settings/converse/catalog-context?channel=mobile-app' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data '{"catalogContext": null}'
Error Responses:
401: Missing or invalid API key.403This operation requires a read-scoped API key.onGET, orThis operation requires a read_write-scoped API key.onPATCH, when the key's scope is too low.404: The index does not exist under your account.422: Invalid request body, unsupported field name, or invalidchannelvalue.
Conversation Starters
Generate conversation starters based on your most popular queries from your analytics data.
Endpoint: GET /indexes/{index_name}/agentic-search/conversation-starters
Headers:
x-marqo-index-id: {index_id}(required)
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
maxStarters | integer | No | 4 | Number of conversation starters to return. Must be between 1 and 20 |
division | string | No | null | Product division filter to generate division-specific starters. Allowed values: men, mens, women, womens, kid, kids. Matched case-insensitively, and surrounding whitespace is ignored |
channel | string | No | - | Channel identifier; see Channels |
invalidateCache | string | No | false | Set to true to skip the cached starters and generate a fresh set. Case-insensitive |
invalidateCache=true regenerates the starters on every request it is sent with, which costs latency and AI usage. Use it for testing, never on live storefront traffic.
Example Request:
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/conversation-starters?maxStarters=4&division=women' \
--header 'x-marqo-index-id: abc123-my-ecom-store'
Example Response:
[
"Show me running shoes for a marathon",
"Help me find a gift for a birthday",
"I'm looking for the perfect backpack",
"What's trending in sneakers right now?"
]
The response is an array of conversation starter strings. It is empty when the index has no analytics data to draw popular queries from.
Error Responses:
403Agentic chat is not enabled for this index400maxStarters must be a number between 1 and 20400Invalid division: "{value}". Allowed values: men, mens, women, womens, kid, kids400with one of the malformedchannelmessages, or500for an unconfigured channel; see Channels
Query Conversation Starters
Generate follow-up prompts for a specific query, for example to show under a search box or beside search results as entry points into a conversation. Unlike Conversation Starters, the suggestions are specific to the query rather than to your popular searches.
This endpoint must be enabled for your index by Marqo. Until then it returns 403.
Endpoint: GET /indexes/{index_name}/agentic-search/query-conversation-starters
Headers:
x-marqo-index-id: {index_id}(required)
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
q | string | Yes | - | The query to generate starters for. Maximum 2048 characters by default |
maxStarters | integer | No | 4 | Number of starters to return. Must be between 1 and 20 |
channel | string | No | - | Channel identifier; see Channels |
This endpoint applies the same input limit as conversational search rather than a limit of its own, so a different limit configured for conversational search on your index also applies here.
Example Request:
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/query-conversation-starters' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data-urlencode 'q=running shoes' \
--data-urlencode 'maxStarters=3'
Example Response:
[
"Which running shoes are best for flat feet?",
"Show me lightweight running shoes for racing",
"What running shoes do you have under $100?"
]
The response is an array of starter strings. Results for the same query are cached, so repeated requests return quickly and may return the same starters.
Error Responses:
403Agentic chat is not enabled for this index403Query conversation starters are not enabled for this index400q is requiredwhenqis missing or blank400maxStarters must be a number between 1 and 20400with one of the malformedchannelmessages, or500for an unconfigured channel; see Channels413Query 'q' is too long (max 2048 characters).The number in the message is the limit configured for your index503Query conversation starters are temporarily unavailable. Retry later
PDP Query Suggestions
Generate contextual search query suggestions based on a specific product. This endpoint helps users discover related products or learn more about a product they're viewing.
Endpoint: POST /indexes/{index_name}/agentic-search/chat-suggestions
Headers:
Content-Type: application/jsonx-marqo-index-id: {index_id}(required)
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
channel | string | No | Channel identifier; see Channels |
Request Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
documentId | string | Yes | - | Product document identifier (_id field) |
sessionId | string | No | - | User session identifier |
userId | string | No | - | User identifier |
maxSuggestions | integer | No | 5 | Maximum number of suggestions to return. Must be between 1 and 10 |
minSuggestions | integer | No | 1 | Minimum number of suggestions to return. Must be between 1 and 10, and not greater than maxSuggestions |
contextFields | string[] | No | ["productTitle", "variantTitle"] | Document fields to include as context for suggestion generation. When provided, only matching fields (case-insensitive) are passed to the model. When omitted, defaults to productTitle and variantTitle. Image context (variantImageUrl/productMainImageUrl) is always included automatically when present. |
The request body must not contain fields other than those listed; unknown fields return 400.
Example Request:
curl -X POST 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/chat-suggestions' \
--header 'Content-Type: application/json' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data '{
"documentId": "273301208",
"sessionId": "session-abc123",
"userId": "user-28389290",
"maxSuggestions": 5,
"minSuggestions": 1,
"contextFields": ["material", "description"]
}'
Example Response:
[
"What heels would go well with this?",
"What is the material made out of?",
"What do people say about this product?"
]
The response is an array of suggested queries that users might want to ask about the product. Results are cached per document and parameter set, so repeated requests for the same product return the same suggestions.
Error Responses:
403Agentic chat is not enabled for this index400documentId is required400minSuggestions must be <= maxSuggestions400Document has no context fields or image to generate suggestions fromwhen none of the requestedcontextFieldsexist on the document and it has no image404Document not found500Failed to generate chat suggestions
Get Conversation
Retrieve the history of a previous conversation by its ID.
Endpoint: GET /indexes/{index_name}/agentic-search/conversations/{conversation_id}
Headers:
x-marqo-index-id: {index_id}(required)
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index_name | string | Yes | The name of your index |
conversation_id | string | Yes | The conversation ID returned from a previous converse response |
Example Request:
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/conversations/3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9' \
--header 'x-marqo-index-id: abc123-my-ecom-store'
Example Response:
{
"conversationId": "3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9",
"title": "Tropical Vacation Outfits",
"conversation": [
{
"role": "user",
"message": "I need outfits for a tropical vacation"
},
{
"role": "agent",
"messageId": "9b4c4b6d-8bfe-4c66-b388-0d97d3ec1e2e",
"message": "Here are some great options for a tropical getaway!",
"categories": [
{
"query": "tropical vacation dresses",
"category": "Summer Dresses",
"confidence": 0.9,
"hits": ["dress_001", "dress_002"]
}
]
}
]
}
Response Fields:
conversationId(string): The conversation identifiertitle(string, optional): A short title summarizing the conversation topicconversation(array): Array of messages in chronological orderrole(string): Either"user"or"agent"message(string): The message contentmessageId(string, optional, agent only): Unique identifier for the agent message. Use this with the get message and feedback endpoints.categories(array, optional, agent only): Search categories from the agent's responsequery(string): The search query usedcategory(string): The category nameconfidence(number): Confidence score (0–1)hits(array of strings): Product document IDs returned
Error Responses:
| Status | Message | When |
|---|---|---|
400 | Conversation not found for conversationId: {conversation_id} | The ID is unknown, malformed, or belongs to a conversation that has been deleted or has expired |
401 | Unauthorized | Missing or invalid credential |
:::warning Reading a missing conversation returns 400, not 404
This endpoint, Get Conversation Message, and Submit Message Feedback report an unknown conversation ID as 400. Delete Conversation reports the same condition as 404. Do not assume one status covers both: check for the Conversation not found for conversationId message rather than branching on 404 alone.
:::
Get Conversation Message
Retrieve a specific agent message with its category hit IDs hydrated into product documents.
Endpoint: GET /indexes/{index_name}/agentic-search/conversations/{conversation_id}/messages/{message_id}
Headers:
x-marqo-index-id: {index_id}(required)
Use the messageId returned by the message-id SSE event or from GET /indexes/{index_name}/agentic-search/conversations/{conversation_id}. This endpoint does not accept query parameters.
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index_name | string | Yes | The name of your index |
conversation_id | string | Yes | The conversation ID that contains the target message |
message_id | string | Yes | The agent message ID to retrieve |
Example Request:
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/conversations/3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9/messages/9b4c4b6d-8bfe-4c66-b388-0d97d3ec1e2e' \
--header 'x-marqo-index-id: abc123-my-ecom-store'
Example Response:
{
"messageId": "9b4c4b6d-8bfe-4c66-b388-0d97d3ec1e2e",
"user": "I need outfits for a tropical vacation",
"agent": "Here are some great options for a tropical getaway!",
"suggestions": [
"show me beach cover-ups",
"what shoes work with these outfits?",
"find accessories for this look"
],
"categories": [
{
"query": "tropical vacation dresses",
"category": "Summer Dresses",
"confidence": 0.9,
"hits": [
{
"_id": "dress_001",
"productTitle": "Red Floral Sundress",
"variantTitle": "Red / Small",
"price": 89.99,
"variantImageUrl": "https://example.com/images/dress_001.jpg"
},
{
"_id": "dress_002",
"productTitle": "Linen Midi Dress",
"variantTitle": "Natural / Medium",
"price": 129.99,
"variantImageUrl": "https://example.com/images/dress_002.jpg"
}
]
}
]
}
Response Fields:
messageId(string): Unique identifier for the returned agent messageuser(string): The user message immediately before the selected agent responseagent(string): The selected agent response textsuggestions(array, optional): Suggested follow-up queries extracted from the agent responsecategories(array, optional): Search categories from the selected agent responsequery(string): The search query usedcategory(string): The category nameconfidence(number): Confidence score (0–1)hits(array): Product documents returned for the category. Returned document fields follow theattributesToRetrieveused when the message was created. If a document cannot be retrieved, the hit entry contains_id,status, anderror.
Error Responses:
| Status | Message | When |
|---|---|---|
400 | Conversation not found for conversationId: {conversation_id} | The conversation ID is unknown, malformed, deleted, or expired |
400 | messageId is required | The message ID path segment is empty |
404 | Message not found for messageId: {message_id} | The conversation exists but holds no agent message with that ID |
401 | Unauthorized | Missing or invalid credential |
A missing conversation is a 400 here while a missing message is a 404; see the note under Get Conversation.
Submit Message Feedback
Record feedback for a specific agent message.
Endpoint: POST /indexes/{index_name}/agentic-search/conversations/{conversation_id}/feedback
Headers:
Content-Type: application/jsonx-marqo-index-id: {index_id}(required)
Use the messageId returned by the message-id SSE event or from GET /indexes/{index_name}/agentic-search/conversations/{conversation_id}.
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index_name | string | Yes | The name of your index |
conversation_id | string | Yes | The conversation ID that contains the target message |
Request Body:
| Parameter | Type | Required | Description |
|---|---|---|---|
messageId | string | Yes | The agent message to attach feedback to |
feedback | integer | Yes | Feedback value. Use 1 for positive feedback and 0 for negative feedback |
feedbackText | string | No | Optional feedback note, up to 500 characters |
Example Request:
curl -X POST 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/conversations/3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9/feedback' \
--header 'Content-Type: application/json' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data '{
"messageId": "9b4c4b6d-8bfe-4c66-b388-0d97d3ec1e2e",
"feedback": 1,
"feedbackText": "Helpful response"
}'
Example Response:
{
"status": "ok"
}
Error Responses:
| Status | Message | When |
|---|---|---|
400 | conversationId is required | The conversation ID path segment is empty |
400 | messageId is required | messageId is missing from the body |
400 | feedback must be 0 or 1 | feedback is absent or is any other value |
400 | feedbackText must be 500 characters or less | feedbackText is longer than 500 characters |
400 | Invalid feedback request | The body fails validation for any other reason |
400 | Conversation not found for conversationId: {conversation_id} | The conversation ID is malformed |
404 | Conversation not found for conversationId: {conversation_id} | The conversation ID is well-formed but no such conversation exists, including one that was deleted or has expired |
404 | Message not found for messageId: {message_id} | The conversation exists but holds no message with that ID |
400 | Feedback is not supported for this conversation | The conversation predates feedback support |
401 | Unauthorized | Missing or invalid credential |
The same Conversation not found message is returned as 400 for a malformed ID and 404 for a well-formed one that does not exist. Match on the message rather than the status if you need to treat both the same way.
List Conversations
List all conversations for a specific user, sorted by most recently updated first.
Endpoint: GET /indexes/{index_name}/agentic-search/conversations
Headers:
x-marqo-index-id: {index_id}(required)
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | The user identifier to list conversations for |
Example Request:
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/conversations?userId=user-123' \
--header 'x-marqo-index-id: abc123-my-ecom-store'
Example Response:
{
"conversations": [
{
"conversationId": "3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9",
"title": "Tropical Vacation Outfits",
"createdAt": 1717012800000,
"updatedAt": 1717016400000
},
{
"conversationId": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"title": "Running Shoes",
"createdAt": 1716926400000,
"updatedAt": 1716930000000
}
]
}
Response Fields:
conversations(array): Array of conversation summaries, sorted byupdatedAtdescending (most recent first)conversationId(string): Unique identifier for the conversationtitle(string): A short title summarizing the conversation topiccreatedAt(number): Timestamp (milliseconds since epoch) when the conversation was createdupdatedAt(number): Timestamp (milliseconds since epoch) when the conversation was last updated
Error Responses:
| Status | Message | When |
|---|---|---|
400 | accountId, indexName, and userId are required | userId is missing from the query string |
401 | Unauthorized | Missing or invalid credential |
A userId with no conversations is not an error: the response is 200 with an empty conversations array.
Delete Conversation
Permanently delete a stored conversation, including its message history, feedback, and its entry in the user's conversation list.
Endpoint: DELETE /indexes/{index_name}/agentic-search/conversations/{conversation_id}
This endpoint requires a Read-Write (or Admin) API key authenticated with the Authorization: Bearer {api_key} header and does not accept the x-marqo-index-id credential used by the other conversation endpoints. Call it from your backend so the key is never exposed in client-side code.
Headers:
Authorization: Bearer {api_key}(required)
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index_name | string | Yes | The name of your index |
conversation_id | string | Yes | The conversation ID returned from a previous converse response |
This endpoint does not accept query parameters or a request body.
Example Request:
curl -X DELETE 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/conversations/3fd8b45def95c8a98da1cfda23543699144cee5d9c89ae143040bca4ff0636e9' \
--header 'Authorization: Bearer {api_key}'
Example Response:
{
"status": "ok"
}
Error Responses:
401: Missing or invalid API key.403Insufficient API key scope: The key is a Read key.404: No conversation exists for the givenconversation_idunder this index. This also covers a malformed conversation ID, a conversation belonging to a different index or account, and a conversation that has already been deleted.
Deletion is permanent and cannot be undone. Because a second delete of the same conversation returns 404, treat that status as "already gone" rather than an error when you retry. Once deleted, the conversation_id is also rejected by the other conversation endpoints, and passing it to GET /indexes/{index_name}/agentic-search/converse returns 400.
Help Center Content
When customer support is enabled for your conversational agent, Marqo crawls your public help center and grounds support answers in that content. These endpoints let you see which pages are indexed, read what the agent sees, and request a refresh. Marqo configures which site and locales are crawled; contact Marqo to set this up or change it.
All four endpoints require Authorization: Bearer {api_key}. The three GET endpoints accept any valid key; POST .../reindex requires an Admin key. The channel query parameter does not apply to them.
List Locales
Endpoint: GET /indexes/{index_name}/help-center/locales
Lists every locale configured for the index with the time its content was last indexed.
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/help-center/locales' \
--header 'Authorization: Bearer {api_key}'
Example Response:
[
{"locale": "en", "indexedAt": "2026-09-08T02:15:41+00:00"},
{"locale": "fr", "indexedAt": null}
]
locale(string): A configured localeindexedAt(string or null): ISO 8601 timestamp of the last successful index, ornullif the locale has not been indexed yet
The list is empty when no locale is configured.
Get Corpus
Endpoint: GET /indexes/{index_name}/help-center/corpus
Returns the list of indexed pages and freshness for one locale.
| Parameter | Type | Required | Description |
|---|---|---|---|
locale | string | Yes | The locale to read, as returned by List Locales |
include | string | No | Set to text to include the full corpus text in the response. Only applies when mode is inline |
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/help-center/corpus?locale=en&include=text' \
--header 'Authorization: Bearer {api_key}'
Example Response:
{
"indexName": "my-ecom-store",
"locale": "en",
"mode": "manifest",
"indexedAt": "2026-09-08T02:15:41+00:00",
"totalTokens": 48213,
"version": "1",
"citations": {
"shipping-times": "https://help.example.com/articles/shipping-times",
"returns-policy": "https://help.example.com/articles/returns-policy"
},
"articles": [
{
"articleId": "shipping-times",
"title": "Shipping times",
"summary": "Delivery estimates by region and carrier.",
"citationUrl": "https://help.example.com/articles/shipping-times"
}
]
}
indexName(string): Your index namelocale(string): The requested localemode(string):inlinewhen the corpus is stored as one text,manifestwhen it is stored as individual articlesindexedAt(string): ISO 8601 timestamp of the last successful indextotalTokens(number): Approximate size of the corpus in tokensversion(string): Corpus format versioncitations(object): Map of article ID to the public URL the agent cites for that articlearticles(array,manifestmode only): One entry per indexed article witharticleId,title,summary, andcitationUrl. Fetch an article's full text with Get Articlecorpus(string,inlinemode withinclude=textonly): The full corpus text
Error Responses:
404No help-center corpus for index '{index_name}' locale '{locale}'. It may not have been indexed yet.422:localeis missing or invalid, orincludeis nottext
Get Article
Endpoint: GET /indexes/{index_name}/help-center/corpus/pages/{article_id}
Returns the full text of one article from a manifest mode corpus.
| Parameter | Type | Required | Description |
|---|---|---|---|
article_id | string (path) | Yes | An articleId from Get Corpus |
locale | string | Yes | The locale the article belongs to |
curl 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/help-center/corpus/pages/shipping-times?locale=en' \
--header 'Authorization: Bearer {api_key}'
Example Response:
{
"articleId": "shipping-times",
"title": "Shipping times",
"body": "Standard shipping takes 3 to 5 business days within the US...",
"sourceUrl": "https://help.example.com/articles/shipping-times",
"citationUrl": "https://help.example.com/articles/shipping-times",
"indexedAt": "2026-09-08T02:15:41+00:00"
}
articleId,title,body(string): The article as indexedsourceUrl(string): The URL the article was crawled fromcitationUrl(string): The public URL the agent citesindexedAt(string): ISO 8601 timestamp the article was indexed
Error Responses:
404Article '{article_id}' not found in the corpus for index '{index_name}' locale '{locale}'.This is also returned when the locale has no corpus422:localeis missing or invalid
Trigger Reindex
Endpoint: POST /indexes/{index_name}/help-center/reindex
Queues a fresh crawl of the help center for one locale. The crawl runs in the background; poll List Locales or Get Corpus and watch indexedAt to see when it lands. Requires an Admin key. No request body.
| Parameter | Type | Required | Description |
|---|---|---|---|
locale | string | Yes | The locale to reindex |
curl -X POST 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/help-center/reindex?locale=en' \
--header 'Authorization: Bearer {api_key}'
Example Response:
{
"indexName": "my-ecom-store",
"locale": "en",
"status": "queued",
"indexedAt": "2026-09-08T02:15:41+00:00"
}
status(string): Alwaysqueuedon successindexedAt(string or null): The corpus freshness before this run, ornullif the locale has never been indexed
Reindexing is limited to one request per index every 60 seconds, across all locales. A second request inside that window returns 429 Reindex was triggered too recently; retry after {seconds}s. A request that returns 429 or 503 did not queue a crawl.
Error Responses:
403This operation requires an admin-scoped API key.422:localeis missing or invalid429Reindex was triggered too recently; retry after {seconds}s.503Help-center reindexing is temporarily unavailable.
Agentic Conversation Behavior Overview
Context Management
The system maintains conversation context across interactions:
- Context Continuity: Previous conversation context is used to inform current responses
- Product Context: When
documentIdsare provided, the system fetches those products and uses their information to provide more relevant responses. UsecontextFieldsto limit which product fields are included. - Extra Context: When
extraContextis provided, the system passes it to the agent as additional context for the current request before the user's query. - Session Tracking:
sessionIdanduserIdhelp maintain context throughout a user's session
Extra Context
Use extraContext to provide request-specific context that should help the agent respond to the user's query, such as the current page, campaign, or browsing context. The value must be a string. Empty or whitespace-only values are ignored.
extraContext is used internally for the agent prompt and is not returned by GET /indexes/{index_name}/agentic-search/conversations/{conversation_id}. Treat it as public, user-controlled input like q: do not include secrets, sensitive personal information, or privileged assumptions that should not influence the model.
Conversation Retention and Limits
- Retention. A conversation is kept for 30 days after its last activity (a new turn or a read), then deleted. Reads and feedback for a deleted conversation fail, and passing its ID to
/conversereturns400. - Stored history. The 100 most recent turns of a conversation are stored and returned by Get Conversation. Older turns are dropped. The agent itself only considers the most recent turns when composing a reply, so very long conversations do not keep growing the context the model sees.
- Hard limit. A conversation accepts at most 1000 user messages. The turn that reaches the limit is answered normally and is followed by a
conversation-endevent. Any later/converserequest with thatconversationIdreturns400This conversation has reached the maximum of 1000 user messages and cannot accept more. Start a new conversation to continue. - User and anonymous conversations. A conversation started with a
userIdin its first turn belongs to that user: it is given a title and appears in List Conversations for thatuserId. A conversation started withoutuserIdis anonymous: it is stored and can be continued and read by ID, but it never appears in a user's list, even if later turns include auserId. Decide on the first turn. - Input limits.
qandextraContextare each limited to 2048 characters by default; longer values return413, and the message reports the limit configured for your index. Both are scanned for payment card numbers, email addresses, and US phone numbers before use, and any found are removed.
Question Triggering - Ambiguous Queries
The system automatically asks clarifying questions when user queries are ambiguous or too broad.
Examples:
- Query:
"jacket"→ System asks: "What type of jacket are you looking for (winter coat, rain jacket, casual jacket, work blazer)?" - Query:
"shoes"→ System asks clarifying questions about style, occasion, or gender - Query:
"dress"→ System asks about occasion, style, or fit preferences
When Questions Are Triggered:
- Queries that are too broad (single word product categories)
- Queries missing important context (style, occasion, gender, features)
- Queries that could match many different product types
Division Seed
The divisionSeed parameter tells the agent which product division (e.g. Women's, Men's, Kids) the user is currently browsing. The agent uses this as a default filter for searches without the user needing to state it explicitly.
Allowed values: men, mens, women, womens, kids, kid, beauty
Values are matched case-insensitively and surrounding whitespace is ignored. Any other value returns 400 Invalid divisionSeed: "{value}". Allowed values: men, mens, women, womens, kids, kid, beauty.
The division parameter on Conversation Starters accepts a different set: it has no beauty value.
Example Request:
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--data-urlencode "payload=$(echo -n '{
"q": "show me summer dresses",
"divisionSeed": "womens",
"sessionId": "session-001",
"userId": "user-456"
}' | base64 | tr -d '\n')"
Behavior:
- The agent defaults searches to the specified division (e.g. applying a division filter for Women's products)
- If the user explicitly requests a different division (e.g. "show me men's jackets"), the agent switches to the appropriate division
- Once switched, the agent stays on the new division for subsequent messages until the conversation context indicates otherwise
- The division is not mentioned to the user unless they ask about it
Interaction with filter:
If you also pass a filter that includes a division constraint (e.g. productDivision:(WOMENS)), the server-side filter takes priority. The agent will not add a conflicting division filter from the seed. In general, prefer using divisionSeed over hardcoded division filters — it allows the agent to adapt when the user's intent changes, rather than being locked to a fixed filter.
Order Lookup
When a logged-in shopper asks about their orders (e.g. "where is my last order?", "what's the status of order #1234?"), the agent can answer directly from the shopper's order history instead of handing them off to a help page. This is enabled by forwarding the shopper's customer access token on the x-marqo-shopify-customer-token header.
Forwarding the token:
- Your trusted, server-side backend (e.g. Shopify Hydrogen, Next.js, Remix) owns the shopper's authenticated storefront session and obtains a per-session customer access token from your commerce platform.
- Forward that token on the
x-marqo-shopify-customer-tokenheader with each/converserequest you make on the shopper's behalf. Send the token value exactly as issued by the platform. - Forward the token from your backend over your server-to-server channel. Because the order token must be sent as a header, browser clients using
EventSource(which cannot set custom headers) are not suitable for authenticated order lookups — use the fetch +ReadableStreamapproach instead.
What the agent can answer:
- Recent orders — "where is my last order?", "what did I order recently?". The agent returns the shopper's most recent order(s).
- A specific order by number — "where is order #1234?", including branded or custom order numbers (e.g.
ORD-2024-001). Lookups by order number are not limited to recent orders — they can reach orders of any age. - Disambiguation — when the shopper has multiple recent orders and the request is ambiguous, the agent asks which one they mean (e.g. "Which order — #1234 (delivered), #1230 (in transit), or #1228 (processing)?") rather than guessing.
Answers can include order status, fulfillment and tracking details, line items, and totals.
Scope and security:
- The token is scoped to a single shopper. The agent can only read that shopper's own orders — it cannot read another customer's orders, and there is no parameter (such as a customer ID) that the model can set to widen the scope.
- Order lookups are strictly read-only. The agent never creates, cancels, or modifies orders.
- Retrieved order data is used only to compose the answer for the current request.
Missing or expired token:
- No token passed. If a shopper asks an order question without the
x-marqo-shopify-customer-tokenheader (e.g. a logged-out visitor), the agent does not attempt a lookup and instead invites the shopper to log in. - Expired or invalid token. Customer access tokens are short-lived. If the forwarded token has expired or is rejected mid-conversation, the agent stops attempting the lookup and asks the shopper to log in again. There is no server-side retry — the token is per-shopper and short-lived by design.
- The
/converserequest still succeeds in both cases. A missing or expired token does not produce an HTTP error to your backend — the response is a normal SSE stream, and the prompt to log in is delivered in-band as part of the agent's reply. - Non-order questions are unaffected. Product discovery and other non-order questions continue to work normally, with or without a token and without re-authentication.
- Recovering. Once the shopper (re-)authenticates, forward a fresh token on the next
/converserequest and order lookups resume.
Supported platforms:
- Shopify — forward the shopper's Shopify customer access token.
Support for additional commerce platforms is coming. Each platform will use its own header (e.g. x-marqo-{platform}-customer-token).
Example Request:
curl -G 'https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse' \
--header 'x-marqo-index-id: abc123-my-ecom-store' \
--header 'x-marqo-shopify-customer-token: {shopper_customer_access_token}' \
--data-urlencode "payload=$(echo -n '{
"q": "where is my last order?",
"sessionId": "session-001",
"userId": "user-456"
}' | base64 | tr -d '\n')"
Implementation Notes
Streaming Response Handling
When using the converse endpoint, you must handle Server-Sent Events (SSE). The response will stream multiple events, each containing different parts of the response.
Example: Using EventSource
EventSource does not support custom headers natively. If your setup allows the
x-marqo-index-id header to be set via other means (e.g., cookies or a proxy), you
can use EventSource with named event listeners:
const requestData = {
q: "red dress",
sessionId: "session-001",
userId: "user-456",
};
const payload = btoa(JSON.stringify(requestData));
const url = `https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse?payload=${encodeURIComponent(payload)}`;
const eventSource = new EventSource(url);
let agentMessage = "";
let latestMessageId = null;
let conversationId = null;
eventSource.addEventListener("message", (event) => {
const data = JSON.parse(event.data);
agentMessage += data.message;
});
eventSource.addEventListener("category-hits", (event) => {
const data = JSON.parse(event.data);
console.log("Categories:", data.categoryHits);
});
eventSource.addEventListener("suggestions", (event) => {
const data = JSON.parse(event.data);
console.log("Suggestions:", data.suggestions);
});
eventSource.addEventListener("message-id", (event) => {
const data = JSON.parse(event.data);
latestMessageId = data.messageId;
});
eventSource.addEventListener("conversation-id", (event) => {
const data = JSON.parse(event.data);
conversationId = data.conversationId;
console.log("Conversation ID:", conversationId);
});
eventSource.addEventListener("auth-required", (event) => {
const data = JSON.parse(event.data);
console.log("Login needed:", data.reason, data.loginUrl);
});
eventSource.addEventListener("conversation-end", (event) => {
const data = JSON.parse(event.data);
console.log("Conversation full, start a new one:", data.reason);
});
eventSource.addEventListener("error", (event) => {
if (!event.data) return; // EventSource also fires "error" on connection failures
const data = JSON.parse(event.data);
console.error("Server error:", data.error, data.status);
});
eventSource.addEventListener("stream-end", () => {
console.log("Full message:", agentMessage);
console.log("Latest agent message ID:", latestMessageId);
eventSource.close();
});
Example: Using fetch with ReadableStream (supports custom headers)
const requestData = {
q: "red dress",
sessionId: "session-001",
userId: "user-456",
};
const payload = btoa(JSON.stringify(requestData));
const url = `https://ecom.marqo-ep.ai/api/v1/indexes/my-ecom-store/agentic-search/converse?payload=${encodeURIComponent(payload)}`;
const response = await fetch(url, {
method: "GET",
headers: {
"x-marqo-index-id": "abc123-my-ecom-store",
},
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const parts = buffer.split("\n\n");
buffer = parts.pop();
for (const part of parts) {
const eventMatch = part.match(/^event: (.+)$/m);
const dataMatch = part.match(/^data: (.+)$/m);
if (!eventMatch || !dataMatch) continue;
const eventType = eventMatch[1];
const data = JSON.parse(dataMatch[1]);
switch (eventType) {
case "message":
console.log("Text delta:", data.message);
break;
case "category-hits":
console.log("Categories:", data.categoryHits);
break;
case "suggestions":
console.log("Suggestions:", data.suggestions);
break;
case "message-id":
console.log("Latest agent message ID:", data.messageId);
break;
case "conversation-id":
console.log("Conversation ID:", data.conversationId);
break;
case "auth-required":
console.log("Login needed:", data.reason, data.loginUrl);
break;
case "conversation-end":
console.log("Conversation full, start a new one:", data.reason);
break;
case "error":
console.error("Error:", data.error, data.status);
break;
case "stream-end":
console.log("Stream complete");
break;
}
}
}