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

# Text Chat API

> REST + SSE endpoints for text chat sessions — power website chatbots and in-app messaging.

## Overview

The Chat API lets you embed a TalkifAI text agent as a chat interface in any website or application. It uses standard **REST + Server-Sent Events (SSE)** streaming — no WebRTC or microphone required.

<Warning>
  **Text agents only.** This API requires agents with `text` architecture. Voice agents (Pipeline/Realtime) cannot be used here.
</Warning>

**Base URL:**

```
https://api.talkifai.dev/v1/chat
```

**Authentication — two tokens used at different stages:**

| Stage          | Header          | Value                                               |
| -------------- | --------------- | --------------------------------------------------- |
| Create session | `X-API-Key`     | Your `tk_live_...` API key (server-side only)       |
| Send messages  | `Authorization` | `Bearer {session_token}` (JWT from create response) |

<Tip>
  Full integration tutorial with code examples → [Chat API Guide](/guides/chat-api)
</Tip>

***

## Endpoints

| Method | Path                              | Description                 |
| ------ | --------------------------------- | --------------------------- |
| `POST` | `/v1/chat/sessions`               | Create a new chat session   |
| `POST` | `/v1/chat/sessions/{id}/messages` | Send a message (SSE stream) |
| `GET`  | `/v1/chat/sessions/{id}/messages` | Get message history         |
| `POST` | `/v1/chat/sessions/{id}/end`      | End the session             |

***

## Create Session

```bash theme={null}
POST /v1/chat/sessions
X-API-Key: tk_live_your_key_here
Content-Type: application/json
```

**Request body:**

```json theme={null}
{
  "agent_id": "5b710eca-ee67-4c3a-aeb6-8b541f451b40"
}
```

| Field      | Required | Description                 |
| ---------- | -------- | --------------------------- |
| `agent_id` | Yes      | ID of the text agent to use |

**Response:**

```json theme={null}
{
  "conversation_id": "chat_5b710eca_user123_1705312800000",
  "session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "agent_name": "Support Agent",
  "greeting": "Hello! How can I help you today?",
  "agent_model": "gpt-4o-mini"
}
```

| Field             | Description                                                                                              |
| ----------------- | -------------------------------------------------------------------------------------------------------- |
| `conversation_id` | Unique session ID — use in all subsequent calls                                                          |
| `session_token`   | JWT for authenticating subsequent messages (24h expiry)                                                  |
| `agent_name`      | Name of the agent                                                                                        |
| `greeting`        | Agent's opening message — display immediately in chat UI. `null` if agent uses `userFirst` greeting mode |
| `agent_model`     | LLM model the agent is using                                                                             |

<Warning>
  Make this call from your **backend server only**. Never expose your API key in frontend JavaScript.
</Warning>

***

## Send Message

```bash theme={null}
POST /v1/chat/sessions/{conversation_id}/messages
Authorization: Bearer {session_token}
Content-Type: application/json
```

**Request body:**

```json theme={null}
{
  "message": "What are your business hours?"
}
```

| Field     | Required | Description                                 |
| --------- | -------- | ------------------------------------------- |
| `message` | Yes      | User's message text (max 10,000 characters) |

**Response:** Server-Sent Events (SSE) stream (`Content-Type: text/event-stream`)

Each SSE event has a named `event:` field followed by a `data:` line, separated by a blank line:

```
event: stream_start
data: {"agent_name": "Support Agent"}

event: chunk
data: {"delta": "Our business "}

event: chunk
data: {"delta": "hours are 9am–6pm EST."}

event: stream_end
data: {"usage": {"input_tokens": 12, "output_tokens": 18}, "end_session": false}
```

### SSE Event Types

| Event          | Data fields                                                                 | Description                                                                        |
| -------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `stream_start` | `{ "agent_name": "..." }`                                                   | Agent started responding — show loading indicator                                  |
| `chunk`        | `{ "delta": "..." }`                                                        | Incremental text — append to chat bubble                                           |
| `tool_call`    | `{ "name": "...", "status": "executing" }`                                  | Agent invoked a tool — optionally show status to user                              |
| `tool_result`  | `{ "name": "...", "status": "completed" }`                                  | Tool finished — agent continues streaming                                          |
| `handoff`      | `{ "from_agent": "...", "to_agent": "..." }`                                | Multi-agent handoff — a subagent is now responding                                 |
| `stream_end`   | `{ "usage": {"input_tokens": N, "output_tokens": N}, "end_session": bool }` | Response complete — clear loading. If `end_session: true`, call `/end` immediately |
| `error`        | `{ "error": "..." }`                                                        | Error mid-stream — **clear loading state and show error message to user**          |

### Parsing the Stream

SSE events are separated by blank lines (`\n\n`). Each event block contains `event:` and `data:` lines. Parse them together — do **not** rely on the `data:` line alone to determine event type.

```javascript theme={null}
const response = await fetch(
  `https://api.talkifai.dev/v1/chat/sessions/${conversationId}/messages`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${sessionToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ message: userInput })
  }
);

const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = '';
let buffer = '';

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

  buffer += decoder.decode(value, { stream: true });

  // SSE events are separated by blank lines
  const blocks = buffer.split('\n\n');
  buffer = blocks.pop(); // keep any incomplete trailing block

  for (const block of blocks) {
    let eventType = '';
    let dataStr = '';

    for (const line of block.split('\n')) {
      if (line.startsWith('event: ')) eventType = line.slice(7).trim();
      else if (line.startsWith('data: ')) dataStr = line.slice(6);
    }

    if (!dataStr) continue;
    const data = JSON.parse(dataStr);

    if (eventType === 'stream_start') {
      // Show loading indicator
    } else if (eventType === 'chunk') {
      fullText += data.delta;
      // Update chat bubble with streaming text
    } else if (eventType === 'tool_call') {
      // Optionally show "Looking that up..." indicator
    } else if (eventType === 'tool_result') {
      // Tool finished — agent continues
    } else if (eventType === 'handoff') {
      // { from_agent, to_agent } — a subagent took over
    } else if (eventType === 'stream_end') {
      setIsLoading(false); // clear loading state
      if (data.end_session) {
        // Agent called end_chat — close the session immediately
        endSession();
      }
    } else if (eventType === 'error') {
      setIsLoading(false); // clear loading — don't leave UI hanging
      showError(data.error || 'Something went wrong. Please try again.');
    }
  }
}
```

***

## Get Message History

```bash theme={null}
GET /v1/chat/sessions/{conversation_id}/messages
Authorization: Bearer {session_token}
```

**Query parameters:**

| Param    | Default | Description                  |
| -------- | ------- | ---------------------------- |
| `limit`  | 50      | Number of messages to return |
| `offset` | 0       | Pagination offset            |

**Response:**

```json theme={null}
{
  "messages": [
    {
      "role": "assistant",
      "content": "Hello! How can I help you today?",
      "timestamp": "2024-01-15T10:00:00+00:00",
      "token_count": null
    },
    {
      "role": "user",
      "content": "What are your business hours?",
      "timestamp": "2024-01-15T10:00:08+00:00",
      "token_count": null
    },
    {
      "role": "assistant",
      "content": "Our business hours are 9am–6pm EST, Monday through Friday.",
      "timestamp": "2024-01-15T10:00:09+00:00",
      "token_count": 18
    }
  ],
  "count": 3
}
```

***

## End Session

Always end sessions explicitly to trigger memory ingestion, post-call analysis, and webhooks.

```bash theme={null}
POST /v1/chat/sessions/{conversation_id}/end
Authorization: Bearer {session_token}
```

**What happens:**

1. Billing session closed and credits consumed
2. Memory ingested into Graphiti (for returning user context)
3. Post-call analysis triggered (if configured on agent)
4. Webhook fired (if agent has webhook configured)

**Response:**

```json theme={null}
{
  "success": true,
  "message": "Session ended successfully"
}
```

<Note>
  Sessions automatically expire after **30 minutes of inactivity**. Always call `/end` explicitly to ensure memory ingestion and webhooks fire correctly.
</Note>

***

## Error Codes

| Status | Code                    | Cause                                                     | Fix                                                              |
| ------ | ----------------------- | --------------------------------------------------------- | ---------------------------------------------------------------- |
| `400`  | `not_text_agent`        | Agent is Pipeline or Realtime (not Text)                  | Create a Text architecture agent                                 |
| `400`  | `message_too_long`      | Message exceeds 10,000 characters                         | Trim the message                                                 |
| `401`  | `invalid_api_key`       | API key missing, invalid, or expired                      | Check `X-API-Key` header                                         |
| `401`  | `invalid_token`         | Session JWT expired or invalid                            | Create a new session                                             |
| `402`  | `insufficient_credits`  | Organization has insufficient credits                     | Top up credits in billing settings                               |
| `403`  | `conversation_mismatch` | `conversation_id` in URL does not match the session token | Use the correct `conversation_id` returned from session creation |
| `404`  | `session_not_found`     | Session expired (idle >30 min) or wrong ID                | Create a new session                                             |
| `500`  | —                       | Unexpected server error                                   | Retry or contact support                                         |
| `503`  | —                       | Billing service temporarily unavailable                   | Retry after a short delay                                        |

***

## Session Lifecycle

```
POST /sessions          → session_token + conversation_id
        ↓
POST /messages          → SSE stream (repeatable)
        ↓
POST /end               → billing cleanup + memory + webhooks
```

Sessions expire automatically after **30 minutes of inactivity**, even without calling `/end`. However, calling `/end` is required to trigger billing finalization, memory ingestion, and post-call analysis.
