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:

EndpointPurpose
POST /api/v1/chat/ask-asyncSubmit 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

FieldTypeDescription
questionstringThe question to ask. Required.
agentIdstringThe Knowledge Agent that should answer. If omitted, Guru routes to the default agent.
chatThreadIdstringContinues an existing conversation instead of starting a new one. See Multi-turn conversations.
minimalAnswerOnlybooleanReturns the answer text without supporting detail.
bypassQuestionDetectionbooleanSkips the check that determines whether the input is a question.
contextobjectIdentifies 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 need

Both responses include chatThreadId and turnId. 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_TOKEN

The GET follows the same pattern as the POST:

  • 202 — still PENDING. Wait and try again.
  • 200 — the turn reached a final state. Read status to find out which.
❗️

A 200 does not mean the question was answered successfully

200 means the turn is finished, which includes finishing badly. Check that status is COMPLETE before using answer. 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:

ValueMeaning
PENDINGGuru is still working. Keep polling.
COMPLETEFinished successfully. answer and sources are populated.
ERROREDFinished 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

FieldDescription
answerThe generated answer text. Present when status is COMPLETE.
chatThreadIdThe conversation this turn belongs to. Pass it back to ask a follow-up.
turnIdThis specific question-and-answer exchange.
answerIdIdentifies the answer for use with the Answers endpoints.
sourcesThe documents the answer drew on. See below.
knowledgeAgentThe agent that produced the answer.
classificationThe outcome of the question, such as ANSWERED.
statusTurn 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:

documentTypeWhat it is
GURUA Guru card
SOURCEA record from a connected Source
GURU_ATTACHMENTA file attached to a card
WEBA page retrieved via web search
MCPA 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.