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

# Multi-Turn Sessions

> Stateful conversations across multiple requests — the server remembers context so you don't have to.

## Overview

By default, each `/chat/completions` request is stateless. Send `store: true` and OkraPDF creates a server-side session that accumulates message history. Follow-up requests only need the new user message — prior context is loaded automatically.

<Info>
  **OpenAI-compatible.** `store` and `metadata` are both [official OpenAI fields](https://platform.openai.com/docs/api-reference/chat/create#chat-create-store). No invented parameters.
</Info>

## How it works

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant OkraPDF

    Client->>OkraPDF: POST /chat/completions (store: true)
    OkraPDF-->>Client: response + metadata.session_id

    Client->>OkraPDF: POST /chat/completions (metadata.session_id)
    Note over OkraPDF: Loads prior messages, appends new one
    OkraPDF-->>Client: response (uses full context)

    Client->>OkraPDF: POST /chat/completions (metadata.session_id)
    OkraPDF-->>Client: response (3-turn context)
```

## Step 1: Start a session

Send `"store": true` to create a session. The response includes `metadata.session_id`.

```bash theme={null}
curl https://api.okrapdf.com/document/$DOC_ID/chat/completions \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "Summarize this document in 2 sentences."}],
    "store": true
  }'
```

```json theme={null}
{
  "id": "chatcmpl-abc123",
  "metadata": { "session_id": "sess_b3dc7753bac57dc633010d48" },
  "choices": [{
    "message": { "role": "assistant", "content": "This is IRS Form W-9..." },
    "finish_reason": "stop"
  }]
}
```

## Step 2: Follow up

Pass the `session_id` in `metadata`. Only send the new user message — the server prepends prior history.

```bash theme={null}
curl https://api.okrapdf.com/document/$DOC_ID/chat/completions \
  -H "Authorization: Bearer $OKRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "What certifications does the signer make?"}],
    "metadata": { "session_id": "sess_b3dc7753bac57dc633010d48" }
  }'
```

The assistant now has full context from turn 1 and answers accordingly.

## Step 3: Manage sessions

```bash theme={null}
# List all sessions for this document
curl https://api.okrapdf.com/document/$DOC_ID/sessions \
  -H "Authorization: Bearer $OKRA_API_KEY"

# Get session metadata
curl https://api.okrapdf.com/document/$DOC_ID/sessions/sess_xxx \
  -H "Authorization: Bearer $OKRA_API_KEY"

# Delete a session
curl -X DELETE https://api.okrapdf.com/document/$DOC_ID/sessions/sess_xxx \
  -H "Authorization: Bearer $OKRA_API_KEY"
```

## With the OpenAI SDK

```typescript theme={null}
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: `https://api.okrapdf.com/document/${docId}`,
  apiKey: process.env.OKRA_API_KEY,
});

// Turn 1: start session
const turn1 = await client.chat.completions.create({
  model: "kimi-k2p5",
  store: true,
  messages: [{ role: "user", content: "Summarize this document." }],
});

const sessionId = (turn1 as any).metadata?.session_id;

// Turn 2: follow up (server loads prior context)
const turn2 = await client.chat.completions.create({
  model: "kimi-k2p5",
  messages: [{ role: "user", content: "What tables are on page 3?" }],
  metadata: { session_id: sessionId },
} as any);
```

## Behavior matrix

| `metadata.session_id` | `store`      | `messages` | Behavior                                       |
| --------------------- | ------------ | ---------- | ---------------------------------------------- |
| absent                | absent/false | required   | Stateless (default, unchanged)                 |
| absent                | `true`       | required   | Auto-create session, return `session_id`       |
| present               | --           | 1 message  | Resume: prepend history, append new message    |
| present               | --           | full array | Replace: use provided messages, update session |

## Security

* Sessions are scoped to the API key's `user_id` — you cannot load another user's session
* Session data is never exposed to the LLM's `query_sql` tool
* Sessions expire after 24 hours of inactivity (lazy GC)
* `DELETE /sessions/:id` immediately removes all stored messages
