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

# Ask a question and get a cited answer

> Ask a question and receive the whole grounded answer, with its citations, in one response. The non-streaming turn, for work that cannot read an event stream.

This is the non-streaming turn, for server-to-server integrations and jobs that cannot read an event stream. It returns once the answer is complete. To show the answer as it is written, use [Stream a message](/api-reference/conversations/stream-message) instead; both run the same turn.

The response is the whole turn: the updated conversation, your question, and the answer, each as its own object. An answer with `abstained: true` means the assistant declined because your knowledge did not support an answer. That is a successful `201`, not an error. See [How answers work](/concepts/how-answers-work).

A conversation answers one question at a time. While a question is in progress, another one returns `409`. Only the conversation's owner can ask questions in it, and an archived conversation refuses new questions with `409`.

This request also resolves the conversation's assistant. If that assistant can no longer answer (it is `disabled` or `archived`, or has no knowledge attached), this request returns `400` `validation_error`.

**Retrying safely.** Send an `idempotency_key` with each question. If you retry with the same key after the first attempt finished, you get the turn it produced, with `201`, and the model is not asked again. If the first attempt is still being answered, the retry returns `409`. A turn that failed or was interrupted on the [streaming endpoint](/api-reference/conversations/stream-message) is kept, so a retry with its key returns it as it ended: send a new key to ask again.

If generation fails, you get a `502` or `503` error and nothing from that attempt is kept: neither your question nor an answer is added, and the conversation is free for the next question. A retry with the same `idempotency_key` asks again.

<ParamField path="conversation_id" type="string" required>
  The conversation ID.
</ParamField>

<ParamField body="text" type="string" required>
  The question. 1 to 16,000 characters after trimming. Newlines and tabs are allowed; other control
  characters and invisible formatting characters are rejected. Windows line endings are normalized
  to `\n`.
</ParamField>

<ParamField body="idempotency_key" type="string">
  Your own key for this question, unique within the conversation. Up to 128 characters from `A-Z`,
  `a-z`, `0-9`, `_`, `.`, `:` and `-`.
</ParamField>

<ParamField body="metadata" type="object">
  Your own labels for the question, as string keys and string values. At most 20 entries; keys up to
  64 characters matching `^[a-z][a-z0-9_.-]*$`; values up to 512 characters on a single line.
  Returned verbatim, so never put secrets here.
</ParamField>

The request body can be at most 256 KiB.

## Response

Returns `201 Created`.

<ResponseField name="conversation" type="object" required>
  The conversation after this turn. It has the same fields as [Get a
  conversation](/api-reference/conversations/get-conversation). If the conversation was untitled, it
  is now named after the first line of your question.
</ResponseField>

<ResponseField name="question" type="object" required>
  Your question, as stored. It has the same fields as `answer`, with `role` `user`, `status`
  `complete`, empty `citations`, and `model` and `finish_reason` set to `null`.
</ResponseField>

<ResponseField name="answer" type="object" required>
  The assistant's answer.

  <Expandable title="properties">
    <ResponseField name="id" type="string">
      The message ID.
    </ResponseField>

    <ResponseField name="conversation_id" type="string">
      The conversation ID.
    </ResponseField>

    <ResponseField name="role" type="string">
      `assistant`.
    </ResponseField>

    <ResponseField name="sequence" type="integer">
      Position in the conversation. One more than the question's.
    </ResponseField>

    <ResponseField name="status" type="string">
      `complete` for a finished answer. A replayed turn can also be `interrupted` or `failed`.
    </ResponseField>

    <ResponseField name="text" type="string">
      The answer text, with `[1]`-style citation markers.
    </ResponseField>

    <ResponseField name="citations" type="object[]">
      The sources the answer is grounded in.

      <Expandable title="properties">
        <ResponseField name="source_id" type="string">
          Identifier of the cited passage. It stays the same while the document's content is
          unchanged and changes when changed content is re-ingested. It is not a knowledge source ID
          or a document ID.
        </ResponseField>

        <ResponseField name="ordinal" type="integer">
          The citation's number, from 1, matching the marker in `text`.
        </ResponseField>

        <ResponseField name="title" type="string">
          Title of the cited document. May be an empty string.
        </ResponseField>

        <ResponseField name="url" type="string | null">
          The source URL, when the document has one.
        </ResponseField>

        <ResponseField name="snippet" type="string | null">
          An excerpt supporting the answer.
        </ResponseField>

        <ResponseField name="anchor" type="string | null">
          A location within the source, such as a heading or page.
        </ResponseField>

        <ResponseField name="score" type="number | null">
          The retrieval relevance score of the cited passage. Higher is more relevant. Use it for
          diagnostics and ranking, not as a probability.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="abstained" type="boolean">
      `true` when the assistant declined to answer from the available knowledge.
    </ResponseField>

    <ResponseField name="created_at" type="string">
      ISO 8601 timestamp with a UTC offset.
    </ResponseField>

    <ResponseField name="model" type="string | null">
      The model that produced the answer, as `provider/name`.
    </ResponseField>

    <ResponseField name="finish_reason" type="string | null">
      Why generation stopped, as reported. `length` means the answer was cut off at the output limit
      and is incomplete.
    </ResponseField>

    <ResponseField name="idempotency_key" type="string | null">
      Always `null` on an answer. Your key is echoed on `question`.
    </ResponseField>

    <ResponseField name="metadata" type="object">
      Always empty on an answer.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --request POST "$MITHUNAI_URL/arukz/api/v1/conversations/c41f8a2e-6d93-4b0a-b7e5-3f1d2c8a9e60/messages" \
    --header "Authorization: Bearer $MITHUNAI_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{"text": "How do I rotate an API key?", "idempotency_key": "ticket-4821-q1"}'
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import os, requests

  conversation_id = "c41f8a2e-6d93-4b0a-b7e5-3f1d2c8a9e60"
  response = requests.post(
      f"{os.environ['MITHUNAI_URL']}/arukz/api/v1/conversations/{conversation_id}/messages",
      headers={"Authorization": f"Bearer {os.environ['MITHUNAI_API_KEY']}"},
      json={"text": "How do I rotate an API key?", "idempotency_key": "ticket-4821-q1"},
      timeout=120,
  )
  response.raise_for_status()
  turn = response.json()
  print(turn["answer"]["text"])
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const conversationId = 'c41f8a2e-6d93-4b0a-b7e5-3f1d2c8a9e60'
  const response = await fetch(
    `${process.env.MITHUNAI_URL}/arukz/api/v1/conversations/${conversationId}/messages`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.MITHUNAI_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        text: 'How do I rotate an API key?',
        idempotency_key: 'ticket-4821-q1',
      }),
    },
  )
  const turn = await response.json()
  console.log(turn.answer.text)
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "conversation": {
      "id": "c41f8a2e-6d93-4b0a-b7e5-3f1d2c8a9e60",
      "assistant_id": "5b0e2c7a-91d4-4f3e-8a6b-2d7c9e1f4a38",
      "title": "How do I rotate an API key?",
      "status": "active",
      "turn_state": "idle",
      "message_count": 2,
      "created_at": "2026-09-24T10:15:02.184311+00:00",
      "updated_at": "2026-09-24T10:15:09.402117+00:00",
      "owner": { "type": "service", "id": "9a7d3e51-2f6c-4b88-a0d4-6e1b5c3f7a92" },
      "metadata": { "source": "support-portal" }
    },
    "question": {
      "id": "0e6b1d4f-8a27-4c93-b5d0-7f2e9a3c6b18",
      "conversation_id": "c41f8a2e-6d93-4b0a-b7e5-3f1d2c8a9e60",
      "role": "user",
      "sequence": 1,
      "status": "complete",
      "text": "How do I rotate an API key?",
      "citations": [],
      "abstained": false,
      "created_at": "2026-09-24T10:15:05.771204+00:00",
      "model": null,
      "finish_reason": null,
      "idempotency_key": "ticket-4821-q1",
      "metadata": {}
    },
    "answer": {
      "id": "7c3a9e52-1b6d-4f08-a4e7-2d9b5f1c8e36",
      "conversation_id": "c41f8a2e-6d93-4b0a-b7e5-3f1d2c8a9e60",
      "role": "assistant",
      "sequence": 2,
      "status": "complete",
      "text": "Mint a new key, move your integration to it, then revoke the old key [1].",
      "citations": [
        {
          "source_id": "3d8f2a61-7e4b-4c19-9a05-b6e1d7c2f480",
          "ordinal": 1,
          "title": "Managing API keys",
          "url": "https://docs.example.com/admin/api-keys#rotation",
          "snippet": "Rotation is mint-then-revoke: create the replacement first, then revoke the old key.",
          "anchor": "rotation",
          "score": 0.82
        }
      ],
      "abstained": false,
      "created_at": "2026-09-24T10:15:05.771204+00:00",
      "model": "anthropic/claude-sonnet-5",
      "finish_reason": "end_turn",
      "idempotency_key": null,
      "metadata": {}
    }
  }
  ```

  ```json 400 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "validation_error", "message": "'text' is required." }
  ```

  ```json 404 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "not_found", "message": "The requested resource was not found." }
  ```

  ```json 409 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "conflict", "message": "This conversation is already waiting for an answer." }
  ```

  ```json 502 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "provider_failure", "message": "The upstream provider could not complete the request." }
  ```

  ```json 503 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "retrieval_failure", "message": "Knowledge retrieval could not be completed." }
  ```
</ResponseExample>
