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

# Conversation state

> Keep conversation history in your application and send the relevant context on each stateless API call.

NinjaChat API v1 is stateless: it does not store response bodies or server-side sessions. Your application owns conversation state and sends the relevant history on each request.

<CodeGroup>
  ```typescript TypeScript SDK theme={null}
  const messages = [
    { role: "user" as const, content: "My name is Alice. I work in fintech." },
  ];

  const first = await client.chat.completions.create({
    model: "claude-sonnet-5",
    messages,
  });

  messages.push({ role: "assistant", content: first.choices[0].message.content ?? "" });
  messages.push({ role: "user", content: "What do you know about me?" });

  const second = await client.chat.completions.create({
    model: "claude-sonnet-5",
    messages,
  });
  ```

  ```python Python SDK theme={null}
  messages = [{"role": "user", "content": "My name is Alice. I work in fintech."}]

  first = client.chat.completions.create(model="claude-sonnet-5", messages=messages)
  messages.extend([
      {"role": "assistant", "content": first["choices"][0]["message"]["content"]},
      {"role": "user", "content": "What do you know about me?"},
  ])

  second = client.chat.completions.create(model="claude-sonnet-5", messages=messages)
  ```
</CodeGroup>

## What to store

| Data                   | Recommendation                                                                            |
| ---------------------- | ----------------------------------------------------------------------------------------- |
| Conversation messages  | Store in your database under your own user/conversation ID.                               |
| Tool calls and outputs | Keep both items so the model can continue the tool loop.                                  |
| Long threads           | Summarize older turns and retain the most recent verbatim messages.                       |
| Request metadata       | Store NinjaChat `request_id`, resolved model, provider, and `cost_usd` for observability. |

## A compact support bot

```python Python SDK theme={null}
def chat(history: list[dict], message: str) -> str:
    history.append({"role": "user", "content": message})
    response = client.chat.completions.create(
        model="claude-sonnet-5",
        messages=history,
        routing={"strategy": "balanced", "allow_fallbacks": True},
    )
    answer = response["choices"][0]["message"]["content"]
    history.append({"role": "assistant", "content": answer})
    return answer
```

## Limits

| Property            | Value                                                                                                                                                |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| API retention       | Response bodies are not stored by the canonical `/responses` and `/chat/completions` endpoints. The legacy session store below is the one exception. |
| Context size        | Bound by the selected model's context window.                                                                                                        |
| Application storage | Controlled by your own database, retention, and deletion policy.                                                                                     |

### Message limits

Summarize before the history approaches the chosen model's context window. A practical pattern is one structured summary plus the most recent 10–20 turns. This keeps latency and token cost predictable while your database remains the source of truth.

## Legacy sessions

The legacy `POST /api/v1/chat` path still supports server-side session memory for integrations built against it. It is a compatibility surface — new applications should keep state client-side as shown above. These routes are deliberately not wrapped by the SDKs — the clients guide you toward owning your own history. Call them as raw REST with your `Authorization: Bearer` header.

| Endpoint                                                 | What it does                                                                                                                                                                                                                                  |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /api/v1/sessions`                                  | Creates a session. Body `{ "session_id": "..." }` is optional (1–64 letters, digits, `-`, `_`); omit it to receive a generated `sess_…` id. Returns `{ session_id, message_count, created_at, request_id }`. Requires a key with API credits. |
| `GET /api/v1/sessions/{id}`                              | Returns `{ session_id, messages, message_count, created_at, updated_at }`.                                                                                                                                                                    |
| `DELETE /api/v1/sessions/{id}`                           | Returns `{ "deleted": true }`.                                                                                                                                                                                                                |
| `GET /api/v1/sessions/{id}/export?format=json\|markdown` | Downloads the transcript (`Content-Disposition: attachment`). `format` defaults to `json`; any other value is `400 validation_error`.                                                                                                         |
| `POST /api/v1/chat` with `session_id`                    | Prepends the stored history to `messages`, runs the completion, then appends your turn and the assistant reply. Non-streaming responses gain `session: { id, message_count }`. A `session_id` that doesn't exist yet is created on first use. |

Sessions expire **7 days** after their last write. A session holds at most **100 messages** — past that the store keeps only the most recent 50 — and once it passes 60 messages the older turns are compacted in the background into a summary plus the most recent 20. Session ids are scoped to your account; an unknown or foreign id returns `404 not_found`. `session_id` is not accepted on `/responses` or `/chat/completions` (`400 unsupported_parameter`).
