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

# Chat API — Single-Turn RAG Question and Answer Endpoint

> POST /v1/chat — Send a single message to a Ragen assistant and get a RAG-grounded answer back. Supports streaming via Server-Sent Events.

The Chat endpoint is the simplest way to query your Ragen knowledge base. Send a single message, get a single answer — Ragen handles retrieval, reranking, and generation behind the scenes. If you need multi-turn conversation, model selection, or token usage tracking, use [Chat Completions](/api-reference/chat-completions) instead.

## Endpoint

```
POST /v1/chat
```

**Authentication** — include your API key in every request:

```
Authorization: Bearer YOUR_API_KEY
```

API keys are scoped to your organization. Create and manage keys in the Ragen dashboard under **Settings → API Keys**.

<Note>
  Prefer [POST /v1/chat/completions](/api-reference/chat-completions) for new integrations. It supports multi-turn conversations, model override, temperature, and usage tracking via the standard OpenAI wire format.
</Note>

## Request parameters

<ParamField body="assistant_id" type="string" required>
  The assistant (project) ID to query. Find it in the dashboard URL when viewing a project (`…/projects/<assistant_id>`), via `GET /v1/assistants`, or under **Settings → Assistant settings**.
</ParamField>

<ParamField body="content" type="string" required>
  The user's message. Must be between 1 and 10,000 characters.
</ParamField>

<ParamField body="context" type="string">
  Additional page or document context passed directly to the model alongside the retrieved chunks. Maximum 20,000 characters. Useful when building embedded chatbots — pass the current page's content here so the model can answer questions about it even if it isn't in the knowledge base.
</ParamField>

<ParamField body="stream" type="boolean" default="false">
  When `true`, the response is returned as a Server-Sent Events stream instead of a single JSON object.
</ParamField>

<ParamField body="reasoning_effort" type="string">
  OpenAI-style reasoning effort: `"low"`, `"medium"`, or `"high"`. Forwarded to the underlying model; only honored by reasoning-capable models (e.g. GPT-o series). When set, streaming responses additionally emit `{"reasoning": "..."}` events with the model's intermediate thinking.
</ParamField>

## Response

### Non-streaming (default)

A JSON object with a single `text` field:

```json theme={null}
{
  "text": "Based on your documentation, customers can return items within 30 days of purchase for a full refund."
}
```

<ResponseField name="text" type="string">
  The AI-generated answer grounded in your knowledge base.
</ResponseField>

### Streaming (`stream: true`)

Returns a `text/event-stream` response. Two event shapes may appear, followed by a `[DONE]` sentinel:

```
HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache, no-transform
Connection: keep-alive

data: {"text":"Based "}
data: {"text":"on "}
data: {"text":"your "}
data: {"text":"documentation, "}
data: {"text":"customers can return items within 30 days..."}
data: [DONE]
```

| Event shape            | When emitted                                                                                      |
| ---------------------- | ------------------------------------------------------------------------------------------------- |
| `{"text": "..."}`      | A chunk of the final answer                                                                       |
| `{"reasoning": "..."}` | A chunk of intermediate reasoning (only when `reasoning_effort` is set and the model supports it) |

The stream always ends with `data: [DONE]`. Display `reasoning` chunks separately from the answer or ignore them — most users don't need to show them.

## Examples

<Tabs>
  <Tab title="TypeScript SDK">
    Use the official `@webamigos/ragen-sdk-ts` package for typed responses, streaming iterators, and automatic retries.

    ```typescript theme={null}
    import { Ragen } from '@webamigos/ragen-sdk-ts';

    const ragen = new Ragen({ apiKey: process.env.RAGEN_API_KEY });

    // Non-streaming
    const response = await ragen.chat.completions.create({
      assistantId: 'YOUR_ASSISTANT_ID',
      messages: [{ role: 'user', content: 'What is our return policy?' }],
    });
    console.log(response.choices[0].message.content);
    ```

    ```typescript theme={null}
    // Streaming
    const stream = await ragen.chat.completions.create({
      assistantId: 'YOUR_ASSISTANT_ID',
      messages: [{ role: 'user', content: 'Summarize our product features' }],
      stream: true,
    });

    for await (const chunk of stream) {
      process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
    }
    ```
  </Tab>

  <Tab title="curl">
    ```bash theme={null}
    # Non-streaming
    curl -X POST $RAGEN_BASE_URL/chat \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "assistant_id": "YOUR_ASSISTANT_ID",
        "content": "What is our return policy?",
        "context": "This is the FAQ page of our e-commerce store."
      }'
    ```

    ```bash theme={null}
    # Streaming
    curl -X POST $RAGEN_BASE_URL/chat \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      --no-buffer \
      -d '{
        "assistant_id": "YOUR_ASSISTANT_ID",
        "content": "What is our return policy?",
        "stream": true
      }'
    ```
  </Tab>

  <Tab title="TypeScript (fetch)">
    ```typescript theme={null}
    // Non-streaming
    const response = await fetch(`${process.env.RAGEN_BASE_URL}/chat`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RAGEN_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        assistant_id: 'YOUR_ASSISTANT_ID',
        content: 'What is our return policy?',
      }),
    });

    const data = await response.json();
    console.log(data.text);
    ```

    ```typescript theme={null}
    // Streaming
    const response = await fetch(`${process.env.RAGEN_BASE_URL}/chat`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RAGEN_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        assistant_id: 'YOUR_ASSISTANT_ID',
        content: 'What is our return policy?',
        stream: true,
      }),
    });

    const reader = response.body!.getReader();
    const decoder = new TextDecoder();

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

      const text = decoder.decode(value);
      const lines = text.split('\n').filter((line) => line.startsWith('data: '));

      for (const line of lines) {
        const data = line.slice(6); // Remove "data: " prefix
        if (data === '[DONE]') break;
        const parsed = JSON.parse(data);
        if (parsed.text) process.stdout.write(parsed.text);
      }
    }
    ```
  </Tab>
</Tabs>

## Error codes

| Status                  | Meaning                                                                               |
| ----------------------- | ------------------------------------------------------------------------------------- |
| `400 Bad Request`       | Invalid request body — missing `content`, character limit exceeded, or malformed JSON |
| `401 Unauthorized`      | Missing, malformed, or invalid API key                                                |
| `403 Forbidden`         | API key is valid but has been deactivated                                             |
| `429 Too Many Requests` | Rate limit exceeded — wait before retrying                                            |
| `502 Bad Gateway`       | Upstream service temporarily unavailable — retry with exponential backoff             |

**Example error body:**

```json theme={null}
{
  "statusCode": 400,
  "message": ["content must be between 1 and 10000 characters"],
  "error": "Bad Request"
}
```

## Rate limits

| Scope          | Limit                |
| -------------- | -------------------- |
| Per IP address | 20 requests / minute |

When rate-limited, use exponential backoff with jitter. Each streaming and non-streaming request counts equally against this budget.

## How the endpoint works

When you call `POST /v1/chat`, Ragen runs a five-step pipeline:

<Steps>
  <Step title="Authentication">
    Your API key is validated and the organization is resolved from it.
  </Step>

  <Step title="Assistant resolution">
    The `assistant_id` is matched to a project within your organization.
  </Step>

  <Step title="RAG retrieval">
    Relevant document chunks are retrieved from the assistant's knowledge base via vector search.
  </Step>

  <Step title="Reranking">
    Retrieved chunks are reranked for relevance before being included in the prompt.
  </Step>

  <Step title="Generation and response">
    The language model generates an answer using the retrieved context and your message. The response is returned as JSON or streamed as SSE.
  </Step>
</Steps>
