Skip to main content

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.
Text agents only. This API requires agents with text architecture. Voice agents (Pipeline/Realtime) cannot be used here.
Base URL:
https://api.talkifai.dev/v1/chat
Authentication — two tokens used at different stages:
StageHeaderValue
Create sessionX-API-KeyYour tk_live_... API key (server-side only)
Send messagesAuthorizationBearer {session_token} (JWT from create response)
Full integration tutorial with code examples → Chat API Guide

Endpoints

MethodPathDescription
POST/v1/chat/sessionsCreate a new chat session
POST/v1/chat/sessions/{id}/messagesSend a message (SSE stream)
GET/v1/chat/sessions/{id}/messagesGet message history
POST/v1/chat/sessions/{id}/endEnd the session

Create Session

POST /v1/chat/sessions
X-API-Key: tk_live_your_key_here
Content-Type: application/json
Request body:
{
  "agent_id": "5b710eca-ee67-4c3a-aeb6-8b541f451b40"
}
FieldRequiredDescription
agent_idYesID of the text agent to use
Response:
{
  "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"
}
FieldDescription
conversation_idUnique session ID — use in all subsequent calls
session_tokenJWT for authenticating subsequent messages (24h expiry)
agent_nameName of the agent
greetingAgent’s opening message — display immediately in chat UI. null if agent uses userFirst greeting mode
agent_modelLLM model the agent is using
Make this call from your backend server only. Never expose your API key in frontend JavaScript.

Send Message

POST /v1/chat/sessions/{conversation_id}/messages
Authorization: Bearer {session_token}
Content-Type: application/json
Request body:
{
  "message": "What are your business hours?"
}
FieldRequiredDescription
messageYesUser’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

EventData fieldsDescription
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.
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

GET /v1/chat/sessions/{conversation_id}/messages
Authorization: Bearer {session_token}
Query parameters:
ParamDefaultDescription
limit50Number of messages to return
offset0Pagination offset
Response:
{
  "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.
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:
{
  "success": true,
  "message": "Session ended successfully"
}
Sessions automatically expire after 30 minutes of inactivity. Always call /end explicitly to ensure memory ingestion and webhooks fire correctly.

Error Codes

StatusCodeCauseFix
400not_text_agentAgent is Pipeline or Realtime (not Text)Create a Text architecture agent
400message_too_longMessage exceeds 10,000 charactersTrim the message
401invalid_api_keyAPI key missing, invalid, or expiredCheck X-API-Key header
401invalid_tokenSession JWT expired or invalidCreate a new session
402insufficient_creditsOrganization has insufficient creditsTop up credits in billing settings
403conversation_mismatchconversation_id in URL does not match the session tokenUse the correct conversation_id returned from session creation
404session_not_foundSession expired (idle >30 min) or wrong IDCreate a new session
500Unexpected server errorRetry or contact support
503Billing service temporarily unavailableRetry 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.