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

# Get Question Status

> Retrieve the status and results of a previously submitted question

Retrieves the current status and results of a question that was submitted to the agent. This endpoint is primarily used to check the progress of async questions or to retrieve results after webhook notifications.

## Authentication

This endpoint requires a valid API key in the Authorization header.

## Path Parameters

<ParamField path="agent_id" type="string" required>
  The agent that was asked. Accepts the agent id shown in the app, the `id` returned by
  [List Agents](/api-reference/agents/list-agents), or `"default"` for the general
  Wordsmith agent.
</ParamField>

<ParamField path="question_id" type="string" required>
  The `id` returned when the question was originally created. This addresses that
  specific question — for the first question in a session it equals the session ID,
  and for follow-up questions it is a distinct question ID. See
  [Understanding IDs](#understanding-ids).
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.wordsmith.ai/api/v1/agents/default/questions/550e8400-e29b-41d4-a716-446655440000" \
    -H "Authorization: Bearer sk-ws-api1-your_api_key_here" \
    -H "Content-Type: application/json"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.wordsmith.ai/api/v1/agents/default/questions/550e8400-e29b-41d4-a716-446655440000",
    {
      method: "GET",
      headers: {
        Authorization: "Bearer sk-ws-api1-your_api_key_here",
        "Content-Type": "application/json",
      },
    }
  );

  const questionStatus = await response.json();
  ```

  ```python Python theme={null}
  import requests

  headers = {
      "Authorization": "Bearer sk-ws-api1-your_api_key_here",
      "Content-Type": "application/json"
  }

  response = requests.get(
      "https://api.wordsmith.ai/api/v1/agents/default/questions/550e8400-e29b-41d4-a716-446655440000",
      headers=headers
  )
  question_status = response.json()
  ```
</CodeGroup>

## Response

<ResponseField name="id" type="string">
  The unique identifier for this specific question.
</ResponseField>

<ResponseField name="session_id" type="string" nullable>
  The session ID that contains this question. This remains constant for all
  questions within the same conversation session. `null` only when `status` is
  `"filtered"`, where no session was created.
</ResponseField>

<ResponseField name="session_url" type="string" nullable>
  A direct URL to view this session in the Wordsmith web application. This
  allows users to access the full conversation history and interact with the
  agent through the web interface. `null` only when `status` is
  `"filtered"`, where there is no session to view.
</ResponseField>

<ResponseField name="status" type="string">
  The current status of the question: `"in_progress"`: The agent is still
  processing your question. `"completed"`: Processing is complete and the answer
  is available. `"error"`: An error occurred during processing. `"filtered"`:
  The agent declined the question, so nothing was processed and no session
  exists — see [Filtered
  questions](/api-reference/agents/create-question#filtered-questions).

  `"completed"`, `"error"` and `"filtered"` are all **terminal**: once returned, a question's status never changes again. Only `"in_progress"` is worth polling on.
</ResponseField>

<ResponseField name="answer" type="string" nullable>
  The agent's response to your question. Present when `status` is
  `"completed"`, and also when it is `"filtered"` — where it carries the
  decline wording, so a client that only reads `answer` still has something to
  show. See [Filtered
  questions](/api-reference/agents/create-question#filtered-questions).
</ResponseField>

<ResponseField name="attachments" type="array" nullable>
  Array of files generated by the agent during processing. Only present when `status` is `"completed"` and files were generated.

  <Expandable title="Attachment Object">
    <ResponseField name="file_name" type="string" nullable>
      The name of the generated file
    </ResponseField>

    <ResponseField name="content_type" type="string" nullable>
      The MIME type of the file (e.g., "application/pdf", "text/plain")
    </ResponseField>

    <ResponseField name="content_length" type="integer" nullable>
      The size of the file in bytes
    </ResponseField>

    <ResponseField name="url" type="string">
      A presigned URL to download the file. This URL expires after 24 hours.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="reason" type="string" nullable>
  One sentence explaining why the agent declined this particular question, stored when it was
  filtered and returned unchanged on every later poll. Present only when `status` is `"filtered"`,
  and `null` for every other status. `answer` carries the same sentence, unless the agent's owner
  has set fixed wording to return instead. Both can be `null` on a filtered question — questions
  filtered before these fields existed have no stored explanation — so show them when they are there
  rather than depending on them.
</ResponseField>

<ResponseExample>
  ```json In Progress theme={null}
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "session_id": "550e8400-e29b-41d4-a716-446655440000",
    "session_url": "https://app.wordsmith.ai/chat/550e8400-e29b-41d4-a716-446655440000",
    "status": "in_progress",
    "answer": null,
    "attachments": null,
    "reason": null
  }
  ```

  ```json Completed - Text Only theme={null}
  {
    "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "session_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "session_url": "https://app.wordsmith.ai/chat/6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "status": "completed",
    "answer": "Based on the contract you provided, I've identified several key areas of concern:\n\n1. **Liability Limitations**: The limitation of liability clause in Section 8.3 may be too broad...",
    "attachments": null,
    "reason": null
  }
  ```

  ```json Completed - With Attachments theme={null}
  {
    "id": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
    "session_id": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
    "session_url": "https://app.wordsmith.ai/chat/6ba7b811-9dad-11d1-80b4-00c04fd430c8",
    "status": "completed",
    "answer": "I've completed a comprehensive analysis of your document. The key findings are summarized below, and I've also generated a detailed report attached to this response.",
    "attachments": [
      {
        "file_name": "contract_analysis_report.pdf",
        "content_type": "application/pdf",
        "content_length": 1024000,
        "url": "https://files.wordsmith.ai/download/abc123def456"
      },
      {
        "file_name": "risk_assessment_summary.docx",
        "content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "content_length": 256000,
        "url": "https://files.wordsmith.ai/download/def456ghi789"
      }
    ],
    "reason": null
  }
  ```

  ```json Error theme={null}
  {
    "id": "6ba7b812-9dad-11d1-80b4-00c04fd430c8",
    "session_id": "6ba7b812-9dad-11d1-80b4-00c04fd430c8",
    "session_url": "https://app.wordsmith.ai/chat/6ba7b812-9dad-11d1-80b4-00c04fd430c8",
    "status": "error",
    "answer": "Unable to process the attached document. The file appears to be corrupted or in an unsupported format. Please check the file and try uploading again.",
    "attachments": null,
    "reason": null
  }
  ```

  ```json Filtered theme={null}
  {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "session_id": null,
    "session_url": null,
    "status": "filtered",
    "answer": "This agent only handles commercial contract questions, and this one is about employment law.",
    "attachments": null,
    "reason": "This agent only handles commercial contract questions, and this one is about employment law."
  }
  ```
</ResponseExample>

## Understanding IDs

### ID vs Session ID

The response includes two important identifiers:

* **`id`**: The unique identifier for this specific question. For the first question in a session, this equals the session\_id. For follow-up questions, this is a unique feed item ID.
* **`session_id`**: The session ID that contains this question. This remains constant for all questions within the same conversation, and is `null` for a filtered question.

**Examples:**

* First question in a new session: `id` and `session_id` are the same
* Follow-up question: `id` is a unique question ID, `session_id` is the original session ID
* [Filtered](/api-reference/agents/create-question#filtered-questions) question: `id` names the question itself, `session_id` is `null` because no session was created

Use the `id` field to check the status of individual questions, and the `session_id` to identify which conversation session the question belongs to.

## Polling Strategy

When using async mode, you can poll this endpoint to check for completion. Stop on any terminal status — `"completed"`, `"error"` or `"filtered"` — and treat everything else as still in flight. A loop that waits only for `"completed"` or `"error"` polls a filtered question until it times out.

Here's a recommended polling strategy:

<CodeGroup>
  ```javascript JavaScript - Polling Example theme={null}
  const TERMINAL_STATUSES = ["completed", "error", "filtered"];

  async function waitForCompletion(questionId, maxWaitTime = 300000) {
    // 5 minutes max
    const startTime = Date.now();
    const pollInterval = 2000; // Start with 2 seconds
    let currentInterval = pollInterval;

    while (Date.now() - startTime < maxWaitTime) {
      const response = await fetch(
        `https://api.wordsmith.ai/api/v1/agents/default/questions/${questionId}`,
        {
          headers: { Authorization: "Bearer sk-ws-api1-your_api_key_here" },
        }
      );

      const result = await response.json();

      // Stop on every terminal status, including "filtered" — a filtered
      // question never progresses, so waiting on it would spin until timeout.
      if (TERMINAL_STATUSES.includes(result.status)) {
        return result;
      }

      // Exponential backoff: increase interval up to 30 seconds
      await new Promise((resolve) => setTimeout(resolve, currentInterval));
      currentInterval = Math.min(currentInterval * 1.5, 30000);
    }

    throw new Error("Timeout waiting for question completion");
  }

  // Usage
  try {
    const result = await waitForCompletion(
      "550e8400-e29b-41d4-a716-446655440000"
    );
    if (result.status === "filtered") {
      console.log("The agent declined this question:", result.id);
    } else {
      console.log("Question completed:", result);
    }
  } catch (error) {
    console.error("Failed to get result:", error);
  }
  ```

  ```python Python - Polling Example theme={null}
  import requests
  import time

  TERMINAL_STATUSES = {"completed", "error", "filtered"}

  def wait_for_completion(question_id, max_wait_time=300):  # 5 minutes max
      start_time = time.time()
      poll_interval = 2  # Start with 2 seconds
      current_interval = poll_interval

      headers = {"Authorization": "Bearer sk-ws-api1-your_api_key_here"}

      while time.time() - start_time < max_wait_time:
          response = requests.get(
              f"https://api.wordsmith.ai/api/v1/agents/default/questions/{question_id}",
              headers=headers
          )
          result = response.json()

          # Stop on every terminal status, including "filtered" — a filtered
          # question never progresses, so waiting on it would spin until timeout.
          if result["status"] in TERMINAL_STATUSES:
              return result

          # Exponential backoff: increase interval up to 30 seconds
          time.sleep(current_interval)
          current_interval = min(current_interval * 1.5, 30)

      raise Exception("Timeout waiting for question completion")

  # Usage
  try:
      result = wait_for_completion("550e8400-e29b-41d4-a716-446655440000")
      if result["status"] == "filtered":
          print("The agent declined this question:", result["id"])
      else:
          print("Question completed:", result)
  except Exception as error:
      print("Failed to get result:", error)
  ```
</CodeGroup>

## Error Responses

See the [Errors](/errors) page for the full error format reference.

<ResponseExample>
  ```json 404 Not Found theme={null}
  {
    "error_code": "resource_not_found",
    "ws_api_error_code": "resource_not_found",
    "message": "Question not found for this agent."
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error_code": "unauthorized",
    "ws_api_error_code": "unauthorized",
    "message": "Invalid API key"
  }
  ```

  ```json 403 Forbidden theme={null}
  {
    "error_code": "forbidden",
    "ws_api_error_code": "forbidden",
    "message": "You do not have permission to perform this action."
  }
  ```
</ResponseExample>

## File Download

When the response includes attachments, you can download them using the provided URLs:

<CodeGroup>
  ```bash cURL - Download Attachment theme={null}
  curl -o "contract_analysis.pdf" "https://files.wordsmith.ai/download/abc123def456"
  ```

  ```javascript JavaScript - Download Attachment theme={null}
  async function downloadAttachment(attachment) {
    const response = await fetch(attachment.url);

    if (!response.ok) {
      throw new Error(`Failed to download: ${response.statusText}`);
    }

    const blob = await response.blob();

    // Create download link
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = attachment.file_name || "download";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    window.URL.revokeObjectURL(url);
  }
  ```

  ```python Python - Download Attachment theme={null}
  import requests

  def download_attachment(attachment, local_filename=None):
      if not local_filename:
          local_filename = attachment.get('file_name', 'download')

      response = requests.get(attachment['url'], stream=True)
      response.raise_for_status()

      with open(local_filename, 'wb') as f:
          for chunk in response.iter_content(chunk_size=8192):
              f.write(chunk)

      return local_filename
  ```
</CodeGroup>

## Best Practices

### Polling Guidelines

* **Start with short intervals**: Begin with 2-3 second intervals for quick questions
* **Use exponential backoff**: Gradually increase intervals to reduce API calls
* **Set reasonable timeouts**: Most questions complete within 2-5 minutes
* **Stop on every terminal status**: `"completed"`, `"error"` and `"filtered"` are all final — never poll past one
* **Handle errors gracefully**: Always check for error status and handle appropriately

### File Management

* **Download promptly**: Attachment URLs expire after 24 hours
* **Store locally if needed**: Download and store important generated files
* **Check file sizes**: Large files may take time to generate and download

### Performance Optimization

* **Use webhooks when possible**: More efficient than polling for production applications
* **Cache results**: Store completed responses to avoid unnecessary API calls
* **Batch requests**: If checking multiple questions, consider batching your requests

## Use Cases

* **Status checking**: Monitor progress of long-running document analysis
* **Result retrieval**: Get answers after receiving webhook notifications
* **Error handling**: Check for and handle processing errors
* **File downloads**: Retrieve generated reports and analysis documents
* **Integration workflows**: Build automated systems that process legal documents
