> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gurubase.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Ask Question

> Ask a question to your Guru

Ask a question to your AI-powered Q\&A assistant and receive a detailed response with references.

<Note>
  To ask follow-up questions, include the `session_id` from a previous response in your request. This allows the Guru to maintain context of the conversation.
</Note>

## Path Parameters

<ParamField path="guru_slug" type="string" required>
  The slug of the Guru to ask a question to
</ParamField>

## Headers

<ParamField header="x-api-key" type="string" required>
  Your API key for authentication. You can obtain your API key from the [Gurubase dashboard](https://app.gurubase.io/api-keys).
</ParamField>

### Body Parameters

<ParamField body="question" type="string" required>
  The question to ask your Guru
</ParamField>

<ParamField body="stream" type="boolean" default={false}>
  Whether to stream the response or not. If true, only the content of the response will be returned in chunks.
</ParamField>

<ParamField body="session_id" type="string">
  Maintain conversation context for follow-up questions. When a question is asked, the response includes a session\_id that can be used in subsequent requests. On the Gurubase platform, these conversation sessions are called "Binges".
</ParamField>

<ParamField body="external_user_id" type="string">
  External user identifier for tracking user-specific conversation sessions. When provided, this ID is stored in the session. If a session already exists with a different external\_user\_id, the request will be rejected.
</ParamField>

<ParamField body="fetch_existing" type="boolean" default={false}>
  Fetch an existing answer. Do not ask a new question (generally used after streaming to fetch the answer fields like references etc.).
</ParamField>

<ParamField body="images" type="array">
  Optional list of image attachments to include with the question. Each item is an object with:

  * `data` (string): Base64-encoded image as a data URL, formatted as `data:<mime>;base64,<payload>` (see the `encode_binary` helper in the "Asking With Attachments" example).
  * `name` (string): File name (e.g. `screenshot.png`).
  * `type` (string): MIME type (e.g. `image/png`).

  Item count and per-file size are subject to the Guru's limits. Images exceeding the limits are silently skipped and the response includes a note with how many were dropped.
</ParamField>

<ParamField body="log_files" type="array">
  Optional list of log file attachments to include with the question. Each item is an object with:

  * `data` (string): Raw UTF-8 log text (see the `read_log` helper in the "Asking With Attachments" example). Not base64-encoded.
  * `name` (string): File name (e.g. `app.log`).

  Item count and per-file size are subject to the Guru's limits. Log files exceeding the limits are silently skipped and the response includes a note with how many were dropped.
</ParamField>

<ParamField body="pdf_files" type="array">
  Optional list of PDF attachments to include with the question. Each item is an object with:

  * `data` (string): Base64-encoded PDF as a data URL, formatted as `data:<mime>;base64,<payload>` (see the `encode_binary` helper in the "Asking With Attachments" example).
  * `name` (string): File name (e.g. `doc.pdf`).
  * `type` (string): MIME type (typically `application/pdf`).

  Item count and per-file size are subject to the Guru's limits. Unlike images and log files, PDFs are validated upfront: if the request exceeds the limits, the endpoint returns HTTP 400 and no answer is generated.
</ParamField>

### Response

<ResponseField name="slug" type="string">
  Unique identifier for the question-answer pair
</ResponseField>

<ResponseField name="content" type="string">
  The answer in Markdown format
</ResponseField>

<ResponseField name="question" type="string">
  The original question
</ResponseField>

<ResponseField name="date_updated" type="string">
  The date when the answer was generated
</ResponseField>

<ResponseField name="trust_score" type="number">
  Confidence score of the answer (0-100)
</ResponseField>

<ResponseField name="references" type="array">
  Array of reference sources used to generate the answer

  <Expandable title="Reference Object">
    <ResponseField name="link" type="string">
      URL of the reference
    </ResponseField>

    <ResponseField name="icon" type="string">
      Icon URL for the reference source
    </ResponseField>

    <ResponseField name="title" type="string">
      Title of the reference
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="session_id" type="string">
  Unique identifier for the conversation session
</ResponseField>

<ResponseField name="question_url" type="string">
  URL link to open the question on the app
</ResponseField>

<ResponseField name="can_receive_feedback" type="boolean">
  Whether the question can receive votes and feedback. Returns `false` for simple interactions and clarifications, `true` for normal answers. See the [Record Vote](/api-reference/endpoints/record-vote) endpoint for more information on providing feedbacks and votes.
</ResponseField>

<Note>
  When `stream=true`, the response will be a text stream containing only the answer content in chunks, not the full JSON object. The JSON response format shown below applies only when `stream=false` (default).
</Note>

<ResponseExample>
  ```json 200 theme={null}
  {
      "slug": "is-slack-supported-on-anteon-5fdf19ad-de4c-4f07-9dff-0fe1003dccac",
      "content": "# Is Slack supported on Anteon?\n\n**Yes, Slack is supported on Anteon.** You can integrate Slack to receive notifications about anomalies in your Kubernetes cluster. This integration is available for Anteon Cloud and the Self-Hosted Enterprise Edition. For the Self-Hosted version, you need to create a Slack application and configure it with specific OAuth scopes. Once set up, you can receive alerts directly in your Slack channels. For detailed instructions, refer to the [Slack Integration Guide](https://getanteon.com/docs/self-hosted/self-hosted-slack-integration/).",
      "question": "Is Slack supported on Anteon?",
      "date_updated": "22 January 2025",
      "trust_score": 76,
      "references": [
          {
              "link": "https://getanteon.com/docs/self-hosted/self-hosted-slack-integration/",
              "icon": "https://getanteon.com/img/favicon.ico",
              "title": "📧 Slack Integration | Anteon"
          }
      ],
      "session_id": "3cdbdc4c-023f-45ce-832f-f1881b4f238b",
      "question_url": "https://app.gurubase.io/g/gurubase/is-slack-supported-on-anteon-5fdf19ad-de4c-4f07-9dff-0fe1003dccac",
      "can_receive_feedback": true
  } 
  ```

  ```text 200 (stream=true) theme={null}
  # Is Slack supported on Anteon?

  **Yes, Slack is supported on Anteon.** You can integrate Slack to receive notifications about anomalies in your Kubernetes cluster. This integration is available for Anteon Cloud and the Self-Hosted Enterprise Edition. For the Self-Hosted version, you need to create a Slack application and configure it with specific OAuth scopes. Once set up, you can receive alerts directly in your Slack channels. For detailed instructions, refer to the [Slack Integration Guide](https://getanteon.com/docs/self-hosted/self-hosted-slack-integration/).
  ```

  ```json 400 theme={null}
  {
      "msg": "<error message>"
  } 
  ```

  ```json 401 theme={null}
  {
      "msg": "Invalid API key"
  }
  ```

  ```json 429 theme={null}
  {
      "msg": "Request was throttled. Expected available in 56 seconds."
  }
  ```

  ```json 400 theme={null}
  {
      "msg": "External user ID mismatch for this session. Code: S-902"
  }
  ```
</ResponseExample>

## Code Examples

<Tabs>
  <Tab title="Streaming and Fetching">
    Stream a response for real-time output, then fetch the completed answer to get metadata like `trust_score`, `references`, and `session_id`.

    ```javascript theme={null}
    // Configuration
    const API_KEY = 'your-api-key-here';
    const BASE_URL = 'https://api.gurubase.io/api/v1';
    const GURU_SLUG = 'your-guru-slug';

    // Simple stream example
    async function askGurubase(question) {
      console.log("Asking question:", question);
      const response = await fetch(`${BASE_URL}/${GURU_SLUG}/answer/`, {
        method: 'POST',
        headers: {
          'X-API-KEY': API_KEY,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          question: question,
          stream: true
        })
      });

      // Read stream chunks
      const reader = response.body.getReader();
      const decoder = new TextDecoder();

      console.log("Answer below:\n");

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value);
        process.stdout.write(chunk.replace(/\n/g, ' '));
      }

      console.log("\n");
    }

    // Fetch existing answer with metadata
    async function fetchExisting(question) {
      const response = await fetch(`${BASE_URL}/${GURU_SLUG}/answer/`, {
        method: 'POST',
        headers: {
          'X-API-KEY': API_KEY,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          question: question,
          stream: false,
          fetch_existing: true
        })
      });

      const data = await response.json();
      console.log('Answer JSON:');
      console.log(data);
      return data;
    }

    // Usage
    const question = "Does Gurubase support Slack?";

    // 1. Ask with stream
    await askGurubase(question);

    // 2. Then fetch the answer from the db (for the JSON fields)
    await fetchExisting(question);
    ```
  </Tab>

  <Tab title="Asking With Attachments">
    Ask a question with image, log, and PDF attachments. Images and PDFs are base64-encoded into `data:<mime>;base64,...` URLs; log files are sent as raw UTF-8 text.

    Fill in the `Config` block, list file paths under `IMAGE_PATHS`, `LOG_PATHS`, and `PDF_PATHS`, then run:

    ```bash theme={null}
    pip install requests
    python ask_with_attachments.py
    ```

    ```python ask_with_attachments.py theme={null}
    import base64
    import json
    import mimetypes
    import sys
    from pathlib import Path
    from typing import List

    import requests

    # ---------- Config ----------
    BASE_URL = "https://api.gurubase.io/api/v1"
    API_KEY = "your-api-key-here"
    GURU_SLUG = "your-guru-slug"
    QUESTION = "Summarize the attached files."
    STREAM = False

    # ---------- Attachments (absolute paths) ----------
    IMAGE_PATHS: List[str] = [
        # "/path/to/screenshot.png",
    ]
    LOG_PATHS: List[str] = [
        # "/path/to/app.log",
    ]
    PDF_PATHS: List[str] = [
        # "/path/to/doc.pdf",
    ]


    def encode_binary(path: Path, fallback_mime: str) -> dict:
        mime = mimetypes.guess_type(path.name)[0] or fallback_mime
        b64 = base64.b64encode(path.read_bytes()).decode("ascii")
        return {
            "data": f"data:{mime};base64,{b64}",
            "name": path.name,
            "type": mime,
        }


    def read_log(path: Path) -> dict:
        return {
            "data": path.read_text(encoding="utf-8", errors="replace"),
            "name": path.name,
        }


    def build_payload() -> dict:
        payload: dict = {"question": QUESTION, "stream": STREAM}
        if IMAGE_PATHS:
            payload["images"] = [encode_binary(Path(p), "image/png") for p in IMAGE_PATHS]
        if LOG_PATHS:
            payload["log_files"] = [read_log(Path(p)) for p in LOG_PATHS]
        if PDF_PATHS:
            payload["pdf_files"] = [encode_binary(Path(p), "application/pdf") for p in PDF_PATHS]
        return payload


    def main() -> int:
        url = f"{BASE_URL.rstrip('/')}/{GURU_SLUG}/answer/"
        headers = {"x-api-key": API_KEY, "Content-Type": "application/json"}
        payload = build_payload()

        resp = requests.post(url, headers=headers, json=payload, timeout=180, stream=STREAM)

        if STREAM:
            for chunk in resp.iter_content(chunk_size=None, decode_unicode=True):
                if chunk:
                    sys.stdout.write(chunk)
                    sys.stdout.flush()
            sys.stdout.write("\n")
            return 0 if resp.ok else 1

        try:
            print(json.dumps(resp.json(), indent=2, ensure_ascii=False))
        except ValueError:
            print(resp.text)
        return 0 if resp.ok else 1


    if __name__ == "__main__":
        sys.exit(main())
    ```

    <Note>
      **PDFs** are validated upfront: if the request exceeds the Guru's PDF count or per-file size limit, the endpoint returns HTTP 400 and no answer is generated.

      **Images** and **log files** that exceed the Guru's count or size limits are silently skipped during answering, and the response includes a note stating how many attachments were dropped.
    </Note>
  </Tab>
</Tabs>
