RAG assistant
Grounded answers with citations, a stateful conversation assistant with streaming, and the engine-native conversation endpoint.
The platform offers three ways to turn search results into prose, all grounded in the tenant's own index:
| Endpoint | Shape | State |
|---|---|---|
POST /indexes/{slug}/answer | One question in, one answer plus citations out | Stateless |
POST /indexes/{slug}/assistant | Conversation turn, history persisted | Stateful, conversation_id |
POST /indexes/{slug}/assistant/native | Engine conversation model, platform-managed history | Stateful, engine-managed |
All three retrieve with the same tenant-scoped search, so the model never sees another organization's documents — retrieval has already been filtered before the prompt is built.
One-shot answers
curl -sS -X POST "$AAC_API/indexes/documentation/answer" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{ "question": "How much does the pro plan cost and what is included?", "per_page": 4 }'{
"index": "documentation",
"answer": "Pro costs 29 USD per month and includes 500,000 searches [1].",
"citations": [
{
"index": 1,
"id": "pricing",
"title": "Pricing",
"url": "https://example.com/pricing",
"snippet": "Pro is 29 USD per month with 500,000 searches."
}
],
"found": 4,
"model": "gpt-4o-mini",
"tokens": 218,
"took_ms": 640,
"request_id": "req_2c4e6a8b0d2f4a6c8e0b2d4f"
}citations[].index matches the [n] markers in the answer text, so a UI can render numbered footnotes without parsing the prose.
Behaviour worth knowing:
- Retrieval runs
per_pageof at most 6 documents (the request'sper_pageis capped at 10, the context at 6). - No matches is not an error. You get
found: 0, empty citations and the sentenceNo documents matched this question, so there is nothing to summarise.— a truthful empty answer beats a hallucinated one. answerrequires an AI provider: withoutOPENAI_API_KEYit is503 AI_NOT_CONFIGURED.- It needs the
aiAnswerplan feature, which every plan includes — Free too, with a small hard-capped allowance of 10,000 AI tokens per month (no overage: past it you get429 QUOTA_EXCEEDED). - AI tokens are metered against the
ai_tokensallowance.
Any search parameter can accompany question — filter_by and query_by are the useful ones:
{
"question": "what changed in 1.8?",
"filter_by": "tags:=[changelog]",
"query_by": "title,content"
}Conversation assistant
curl -sS -X POST "$AAC_API/indexes/documentation/assistant" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{ "question": "How much does pro cost?", "conversation_id": "conv_01HZYB" }'{
"conversation_id": "conv_01HZYB",
"answer": "Pro is 29 USD per month with 500,000 searches [1].",
"mode": "llm",
"model": "gpt-4o-mini",
"citations": [
{
"n": 1,
"id": "pricing",
"title": "Pricing",
"url": "https://example.com/pricing",
"snippet": "Pro is 29 USD per month with 500,000 searches."
}
],
"found": 4,
"tokens": 231,
"latency_ms": 702,
"request_id": "req_8b0d2f4a6c8e0b2d4f6a8c0e"
}Omit conversation_id to start a thread; the response's id goes into the next call. The id is also returned in the x-aac-conversation header. Messages are persisted and metered as ai_tokens.
mode tells you how the answer was produced:
llm— a model answered from the retrieved sources (requiresOPENAI_API_KEY).extractive— no model available, so the assistant returned a structured summary of the matching snippets with a source list. The endpoint still works; the prose is just assembled rather than generated.
Read the history back:
curl -sS "$AAC_API/indexes/documentation/assistant" -H "Authorization: Bearer $AAC_KEY"{
"conversations": [
{
"id": "conv_01HZYB",
"title": "How much does pro cost?",
"messageCount": 2,
"createdAt": "2026-10-23T10:12:00.000Z",
"updatedAt": "2026-10-23T10:12:04.000Z"
}
]
}curl -sS "$AAC_API/indexes/documentation/assistant/conv_01HZYB" \
-H "Authorization: Bearer $AAC_KEY"{
"conversation": {
"id": "conv_01HZYB",
"title": "How much does pro cost?",
"created_at": "2026-10-23T10:12:00.000Z",
"messages": [
{
"id": "msg_1",
"role": "user",
"content": "How much does pro cost?",
"citations": [],
"created_at": "2026-10-23T10:12:00.000Z"
},
{
"id": "msg_2",
"role": "assistant",
"content": "Pro is 29 USD per month…",
"citations": [{ "n": 1, "id": "pricing", "title": "Pricing" }],
"created_at": "2026-10-23T10:12:04.000Z"
}
]
}
}A conversation id that belongs to another index is 404 CONVERSATION_NOT_FOUND — the lookup is scoped by index, not just by tenant.
Streaming
curl -N -sS -X POST "$AAC_API/indexes/documentation/assistant/stream" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{ "question": "And what are the limits?" }'data: {"type":"citations","payload":{"citations":[...],"conversation_id":"conv_01HZYB"}}
data: {"type":"delta","payload":{"text":"Each API key allows "}}
data: {"type":"delta","payload":{"text":"600 requests per minute."}}
data: {"type":"done","payload":{"conversation_id":"conv_01HZYB","tokens":204,"latency_ms":688}}Four event types: citations first (so a UI can show sources immediately), delta fragments, then done; a failure mid-stream arrives as error with a message. The stream is text/event-stream with cache-control: no-cache, no-transform. Each frame is a single data: line followed by a blank line — the SDKs parse exactly that.
Engine-native conversations
curl -sS -X POST "$AAC_API/indexes/documentation/assistant/native" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{ "question": "Summarise the billing model", "conversation_id": "conv_engine_1" }'{
"index": "documentation",
"answer": "Billing is metered by searches, documents, AI tokens and reindex runs…",
"conversation_id": "conv_engine_1",
"mode": "native",
"request_id": "req_4f6a8c0e2b4d6f8a0c2e4b6d"
}This path uses the engine's own conversation model (a configured model or the request's model_id) and keeps history on the engine side. It answers questions but returns no citation array — use /answer or /assistant when you need to show sources.
List the models the node offers (scope settings:write):
curl -sS "$AAC_API/indexes/documentation/conversation-models" \
-H "Authorization: Bearer $AAC_KEY"Choosing
- Marketing FAQ, docs Q&A →
/answer. Simple, stateless, citations for free. - Support chat that must remember the thread →
/assistantwith streaming. Persisted history, citations, extractive fallback. - You already run a conversation on the engine and only need a proxy →
/assistant/native. - You want to control prompting entirely → call
/search, take the hits, and build your own prompt. The platform's contract then stops at retrieval, which is often the right boundary.