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

# Customer Support

> An action-taking support agent that knows your users and acts inside your product.

## The problem

Your support bot reads docs and creates tickets. It can't actually *do* anything — update a subscription, process a refund, change a setting. Users still have to do it themselves.

**What Kairn enables:** An agent that knows each user's account context, handles requests in natural language, and remembers every conversation.

***

## Setup

**1. Define the soul**

The soul is the agent's identity — instructions, personality, and constraints.

```ts theme={null}
await kairn.souls.create({
  name: 'support',
  label: 'Support Agent',
  soulMd: `
    You are a support agent for Acme SaaS.

    You help users with:
    - Billing questions and invoice issues
    - Account settings and preferences
    - Feature questions and how-to guides
    - Escalating bugs to the engineering team

    Behaviour rules:
    - Always greet users by name if you know it (check memory).
    - Confirm destructive actions (cancellations, deletions) before executing.
    - If you cannot resolve an issue, offer to escalate and create a ticket.
    - Never fabricate information — say you don't know if you're unsure.
    - Keep responses concise. Users are busy.

    You have access to the user's account context in memory.
    Use it to personalise your responses without asking for information you already have.
  `,
  defaultModel: 'anthropic/claude-sonnet-4-6',
})
```

**2. Seed user context before each conversation**

Pull relevant account data from your database and write it to memory. The agent reads this automatically at invocation time.

```ts theme={null}
async function seedUserContext(userId: string) {
  const user         = await db.users.findById(userId)
  const subscription = await db.subscriptions.findByUser(userId)
  const openTickets  = await db.tickets.findOpen({ userId })

  await kairn.memory.set(userId, 'default', {
    name:               user.name,
    email:              user.email,
    plan:               subscription.plan,
    billing_status:     subscription.status,
    account_created:    user.createdAt,
    open_tickets:       String(openTickets.length),
    last_ticket_subject: openTickets[0]?.subject ?? '',
  })
}
```

**3. Wire into your chat endpoint**

```ts theme={null}
// POST /api/support/chat
export async function POST(req: Request) {
  const { userId, message, sessionId } = await req.json()

  await seedUserContext(userId)

  const encoder = new TextEncoder()
  const stream = new ReadableStream({
    async start(controller) {
      for await (const event of kairn.stream({
        agent: 'support',
        userId,
        input: message,
        sessionId, // multi-turn — agent remembers the conversation
      })) {
        if (event.type === 'token') {
          controller.enqueue(encoder.encode(
            `data: ${JSON.stringify({ text: event.text })}\n\n`
          ))
        }
        if (event.type === 'done') {
          controller.enqueue(encoder.encode(
            `data: ${JSON.stringify({ done: true, memoryDelta: event.memoryDelta })}\n\n`
          ))
          controller.close()
        }
      }
    },
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
    },
  })
}
```

**4. Add the React component**

```tsx theme={null}
'use client'
import { useState, useRef } from 'react'

export function SupportChat({ userId }: { userId: string }) {
  const [messages, setMessages] = useState<{ role: string; content: string }[]>([])
  const [input, setInput]       = useState('')
  const [streaming, setStreaming] = useState(false)
  const sessionId = useRef(`support_${userId}_${Date.now()}`)

  async function send() {
    if (!input.trim() || streaming) return
    const userMessage = input
    setInput('')
    setMessages(prev => [...prev, { role: 'user', content: userMessage }])
    setStreaming(true)

    let agentText = ''
    setMessages(prev => [...prev, { role: 'agent', content: '' }])

    const res = await fetch('/api/support/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ userId, message: userMessage, sessionId: sessionId.current }),
    })

    const reader  = res.body!.getReader()
    const decoder = new TextDecoder()

    while (true) {
      const { done, value } = await reader.read()
      if (done) break
      for (const line of decoder.decode(value).split('\n')) {
        if (!line.startsWith('data: ')) continue
        const data = JSON.parse(line.slice(6))
        if (data.text) {
          agentText += data.text
          setMessages(prev => [...prev.slice(0, -1), { role: 'agent', content: agentText }])
        }
      }
    }
    setStreaming(false)
  }

  return (
    <div className="support-chat">
      <div className="messages">
        {messages.map((m, i) => (
          <div key={i} className={`message message--${m.role}`}>{m.content}</div>
        ))}
      </div>
      <div className="input-row">
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && send()}
          placeholder="How can I help you?"
        />
        <button onClick={send} disabled={streaming}>Send</button>
      </div>
    </div>
  )
}
```

***

## Example conversation

```
User:  My invoice from last month looks wrong.

Agent: Hi Sarah — I can see you're on the Pro plan with billing
       currently active. Could you tell me which invoice looks
       incorrect, or would you like me to pull up your recent invoices?

User:  The one from February. It charged me for 10 seats but I only have 7.

Agent: Got it — a 3-seat overcharge on your February invoice.
       I'm flagging this for our billing team now and creating a ticket.
       You should receive a credit within 2 business days.
       Is there anything else I can help with?
```

The agent knew Sarah's name and plan without asking — it read memory. It didn't fabricate a resolution — it told her what it was doing. That's the difference between a chatbot and an agent.

***

## What the agent can do today

* Answer questions using its soul instructions and user memory
* Remember context across the full conversation
* Write notes to memory for future sessions ("issue with invoice #4421 resolved")

> [!NOTE]
>
> **Coming soon:** HTTP webhook tools — wire your backend API endpoints as agent tools. The agent will look up live order status, process refunds, and update account settings directly.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.