Ask a Question
Ask a Knowledge Agent a question without waiting on the response — submit it, then check back when it's ready.
A Knowledge Agent does real work to answer a question. Depending on what you ask, it may search across your cards and connected Sources, pull in results from the web, or call external tools before composing a response. Straightforward lookups come back right away; deeper questions take longer, because the agent is doing more to get the answer right.
The endpoints below let you take advantage of that without holding a connection open. You submit a question, and Guru either answers immediately or hands you a receipt you can check back on.
Two endpoints work together:
| Endpoint | Purpose |
|---|---|
POST /api/v1/chat/ask-async | Submit a question |
GET /api/v1/chat/ask-async/{threadId}/{turnId} | Check whether the answer is ready |
Step 1: Submit the question
Send the question to POST /api/v1/chat/ask-async. Only question is required.
curl -X POST https://api.getguru.com/api/v1/chat/ask-async \
-u $GURU_USER:$GURU_TOKEN \
-H "Content-Type: application/json" \
-d '{"question": "What is our refund policy?"}'Request fields
| Field | Type | Description |
|---|---|---|
question | string | The question to ask. Required. |
agentId | string | The Knowledge Agent that should answer. If omitted, Guru routes to the default agent. |
chatThreadId | string | Continues an existing conversation instead of starting a new one. See Multi-turn conversations. |
minimalAnswerOnly | boolean | Returns the answer text without supporting detail. |
bypassQuestionDetection | boolean | Skips the check that determines whether the input is a question. |
context | object | Identifies an external conversation the question originated from (Slack or Microsoft Teams). |
Two possible outcomes
The response body is the same shape either way. What differs is the status code and how much of it is populated.
200 — the answer is ready. Guru finished within the request. status is COMPLETE (or ERRORED), and answer, sources, and the rest are populated. You're done; no polling needed. Short questions against a fast agent usually land here.
{
"answer": "Refunds are processed within 30 days of purchase...",
"chatThreadId": "205aa72d-e884-4142-b977-884980354a15",
"turnId": "9bdcf221-7b40-455d-94dd-f3903ea1c551",
"answerId": "4b085b98-0fd3-453f-90bf-fd50dcb90e08",
"sources": [ ... ],
"knowledgeAgent": { ... },
"classification": "ANSWERED",
"status": "COMPLETE"
}202 — the answer is still being generated. You get chatThreadId, turnId, and status: "PENDING". Everything else is omitted.
{
"chatThreadId": "205aa72d-e884-4142-b977-884980354a15",
"turnId": "9bdcf221-7b40-455d-94dd-f3903ea1c551",
"status": "PENDING"
}
You always get the coordinates you needBoth responses include
chatThreadIdandturnId. You never have to generate or supply them yourself in order to poll.
Always branch on status rather than assuming a fast answer. A question that returns 200 today may return 202 tomorrow if the agent's configuration changes.
Step 2: Poll for the answer
When you receive a 202, use the chatThreadId and turnId from that response to check on the answer.
curl https://api.getguru.com/api/v1/chat/ask-async/205aa72d-e884-4142-b977-884980354a15/9bdcf221-7b40-455d-94dd-f3903ea1c551 \
-u $GURU_USER:$GURU_TOKENThe GET follows the same pattern as the POST:
202— stillPENDING. Wait and try again.200— the turn reached a final state. Readstatusto find out which.
A200does not mean the question was answered successfully
200means the turn is finished, which includes finishing badly. Check thatstatusisCOMPLETEbefore usinganswer. A client that branches on the HTTP status alone will present a failed turn as though it were a real answer.
Polling
The Guru API is not rate limited, so you're free to poll as often as your integration needs. A fixed interval of a second or two is usually plenty — polling faster won't return the answer any sooner.
Set an upper bound on total wait time so a stalled turn doesn't hang your integration indefinitely. How long to wait depends on your agent and use case; it's a client-side decision, not something the API enforces.
Status values
status is the lifecycle of the turn, and it's the field to branch on:
| Value | Meaning |
|---|---|
PENDING | Guru is still working. Keep polling. |
COMPLETE | Finished successfully. answer and sources are populated. |
ERRORED | Finished unsuccessfully. Do not use answer. |
Don't confuse status with classification. status tells you whether the request finished; classification describes the outcome — for example, ANSWERED. A turn can be COMPLETE without Guru having found a useful answer, so check both if your integration needs to distinguish "here's your answer" from "Guru couldn't find one."
Response fields
| Field | Description |
|---|---|
answer | The generated answer text. Present when status is COMPLETE. |
chatThreadId | The conversation this turn belongs to. Pass it back to ask a follow-up. |
turnId | This specific question-and-answer exchange. |
answerId | Identifies the answer for use with the Answers endpoints. |
sources | The documents the answer drew on. See below. |
knowledgeAgent | The agent that produced the answer. |
classification | The outcome of the question, such as ANSWERED. |
status | Turn lifecycle. See Status values. |
Fields with no value are omitted from the response rather than returned as null. Check for a field's presence before reading it.
Sources
sources contains the documents behind the answer. Each has a documentType that determines its shape:
documentType | What it is |
|---|---|
GURU | A Guru card |
SOURCE | A record from a connected Source |
GURU_ATTACHMENT | A file attached to a card |
WEB | A page retrieved via web search |
MCP | A result returned by an MCP tool |
Every source includes id, title, and url, which is enough to render citations. Guru cards also include verificationState (TRUSTED, NEEDS_VERIFICATION, STALE, and others) — useful if you want to signal the trustworthiness of a citation to your users.
Sources return metadata, not document content. To retrieve the body of a cited card, call GET /api/v1/cards/{cardId}/extended with the source's id.
Multi-turn conversations
Each call to POST /chat/ask-async without a chatThreadId starts a fresh conversation. To ask a follow-up that carries the earlier context, pass the chatThreadId you received back:
{
"question": "Does that apply to annual plans too?",
"chatThreadId": "205aa72d-e884-4142-b977-884980354a15"
}Each question adds a new turn to the thread, with its own turnId. Poll each turn using the thread ID plus that turn's ID.
Updated 4 days ago

