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

# Create Session

> Create an AI Ask session, optionally answering a first message

Create a conversation session. A session's **scope** is set at creation and is immutable — it determines what the conversation can draw on:

* **`recording`** — a single recording (its full transcript is used). Pass `recording_id`; the session's end user is the recording's owner.
* **`library`** — all of the end user's recordings, via retrieval (RAG).
* **`selected`** — a chosen set of recordings, via retrieval.

There is no separate "ask" action: provide an `initial_message` to create the session **and** answer the first turn in one call, or omit it to create an empty session and ask later with [Send Message](/api-reference/ask/send-message).

## Authentication

Requires an [API key](/authentication). Provide `end_user_id` in the body for `library` / `selected` scopes unless the API key is itself scoped to an end user. For `recording` scope the end user is derived from the recording.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.bota.dev/v1/ask/sessions \
    -H "Authorization: Bearer sk_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "end_user_id": "eu_abc123",
      "scope": { "type": "library" },
      "initial_message": { "content": "What did I commit to this week?" }
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.bota.dev/v1/ask/sessions', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_live_...',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      end_user_id: 'eu_abc123',
      scope: { type: 'library' },
      initial_message: { content: 'What did I commit to this week?' },
    }),
  });

  const { session_id, message, sources } = await response.json();
  ```

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

  response = requests.post(
      'https://api.bota.dev/v1/ask/sessions',
      headers={
          'Authorization': 'Bearer sk_live_...',
          'Content-Type': 'application/json',
      },
      json={
          'end_user_id': 'eu_abc123',
          'scope': {'type': 'library'},
          'initial_message': {'content': 'What did I commit to this week?'},
      },
  )

  result = response.json()
  ```
</RequestExample>

## Request Body

<ParamField body="scope" type="object" required>
  Conversation scope. Set once at creation and immutable. A discriminated union on `type`:

  * `{ "type": "recording", "recording_id": "rec_..." }`
  * `{ "type": "library" }`
  * `{ "type": "selected", "recording_ids": ["rec_...", "rec_..."] }`

  (`folder` is reserved and not yet supported.)
</ParamField>

<ParamField body="initial_message" type="object">
  An optional first message. When present, the session is created and this message is answered in the same call (the response is the ask result below); when absent, an empty session is returned. Same shape as the body of [Send Message](/api-reference/ask/send-message):

  * `content` (string, required) — the user's question, 1–10000 characters.
  * `provider` (string, optional) — LLM provider (`gemini`, `openai`, or `claude`); defaults to the project/system default.
  * `context_refs` (array, optional) — @mentioned recordings, `[{ "type": "recording", "id": "rec_..." }]` (max 20). Reserved for upcoming features; persisted today.
</ParamField>

<ParamField body="title" type="string">
  Session title (1–200 characters). Defaults to the `initial_message` text, or `"New chat"`.
</ParamField>

<ParamField body="end_user_id" type="string">
  End user the session belongs to (`eu_*`). Required for `library` / `selected` scope unless the API key is end-user-scoped. For `library` scope this is also the retrieval boundary.
</ParamField>

## Response

* **With `initial_message`** → `200`, the **ask result** (`{ session_id, message, sources }`) — see [Send Message](/api-reference/ask/send-message#response) for the full message shape.
* **Without `initial_message`** → `201`, the empty [session object](/api-reference/ask/get-session#response).

<ResponseExample>
  ```json 200 (with initial_message) theme={null}
  {
    "session_id": "as_abc123",
    "message": {
      "id": "msg_def456",
      "role": "assistant",
      "content": "You agreed to send the budget draft by Friday.",
      "parts": [
        { "type": "text", "text": "You agreed to send the budget draft by Friday." },
        { "type": "citation", "recording_id": "rec_ghi789", "start_ms": 754000 }
      ],
      "tokens": { "input": 1240, "output": 32, "cached": 0 },
      "model": "gemini-2.0-flash",
      "provider": "gemini",
      "finish_reason": "stop",
      "created_at": "2026-05-20T10:23:45Z"
    },
    "sources": [
      {
        "chunk_id": "chk_jkl012",
        "recording_id": "rec_ghi789",
        "transcription_id": "txn_mno345",
        "chunk_text": "I'll send the budget draft by Friday.",
        "speaker": "SPEAKER_1",
        "start_ms": 754000,
        "end_ms": 759000,
        "recorded_at": "2026-05-16T09:12:00Z",
        "score": 0.0312
      }
    ]
  }
  ```

  ```json 201 (without initial_message) theme={null}
  {
    "id": "as_abc123",
    "scope": { "type": "library", "recording_ids": [], "recording_title": null },
    "title": "New chat",
    "message_count": 0,
    "last_message_at": "2026-05-20T10:20:00Z",
    "last_model": null,
    "last_provider": null,
    "created_at": "2026-05-20T10:20:00Z",
    "updated_at": "2026-05-20T10:20:00Z"
  }
  ```
</ResponseExample>

<Note>
  When `initial_message` is present, the user message is persisted before the model is called, so on an LLM failure (`502`) you can safely retry against the returned `session_id`. Oversized single-recording transcripts return `413`; use a `library` / `selected` scope instead.
</Note>
