npm package: @takesales/sdk
Description: SDK for integrating Take Sales AI agents into custom UIs
Latest version: 0.1.33

# Take Sales SDK

SDK for integrating Take Sales AI agents into custom UIs. Connect to the agent via WebSocket and build your own chat interface.

> **Using React?** See the [React integration guide](./REACT.md).

## Installation

```bash
npm install @takesales/sdk
```

## Quick Start

```ts
import { TakeSalesClient } from '@takesales/sdk'

const client = new TakeSalesClient({
  agentId: 'your-agent-uuid',
  wsUrl: 'wss://api-agent.takesales.ai/ws',
})

client.on('connected', () => {
  console.log('Agent ready')
  client.sendText('Hello!')
})

client.on('agentConfig', (config) => {
  console.log('Agent:', config.agentName)
})

client.on('message', (text, endOfTurn) => {
  console.log('Agent says:', text)
  if (endOfTurn) console.log('--- turn complete ---')
})

client.on('richContent', (content) => {
  console.log('Rich content:', content.type, content.data)
})

client.on('error', (err) => {
  console.error('Error:', err)
})

client.on('disconnect', (code, reason) => {
  console.log('Disconnected:', code, reason)
})

client.connect()
```

---

## API Reference

### `TakeSalesClient`

Framework-agnostic WebSocket client.

#### Constructor

```ts
new TakeSalesClient(config: TakeSalesClientConfig)
```

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `agentId` | `string` | Yes | Agent UUID |
| `wsUrl` | `string` | Yes | WebSocket server URL |
| `visitor` | `object` | No | Visitor info (see below) |
| `conversationId` | `string` | No | Resume a previous session |
| `isTest` | `boolean` | No | Mark as test session |
| `clientVariables` | `Record<string, string>` | No | Pre-resolved client variables for tool enrichment |
| `clientVariableConfigs` | `ClientVariableConfig[]` | No | Auto-resolve variables from browser (localStorage, cookies, etc.) |
| `reconnect` | `object` | No | Reconnection settings |

**`visitor` object:**

| Field | Type | Description |
|-------|------|-------------|
| `email` | `string` | Visitor email |
| `name` | `string` | Visitor name |
| `phone` | `string` | Visitor phone |
| `contactData` | `Record<string, string>` | Custom contact fields |

**`reconnect` object:**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enabled` | `boolean` | `true` | Enable auto-reconnect |
| `maxAttempts` | `number` | `5` | Max reconnect attempts |
| `baseDelay` | `number` | `1000` | Base delay in ms (exponential backoff) |

#### Methods

| Method | Description |
|--------|-------------|
| `connect()` | Open WebSocket connection |
| `disconnect()` | Close connection (no auto-reconnect) |
| `sendText(text)` | Send a text message |
| `sendFormSubmit(formId, data)` | Submit a lead form |
| `sendScheduleConfirm(scheduleId, date, time)` | Confirm a schedule |
| `sendToolConfirmation(confirmationId, confirmed)` | Approve/reject a tool action |
| `sendUpdateVisitor(data)` | Update visitor contact info |

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `isConnected` | `boolean` | Current connection state |
| `conversationId` | `string \| null` | Current session ID |
| `agentConfig` | `ServerAgentConfig \| null` | Agent configuration from server |

#### Events

```ts
client.on('connected', () => void)
client.on('message', (text: string, endOfTurn: boolean) => void)
client.on('richContent', (content: RichContent) => void)
client.on('agentConfig', (config: ServerAgentConfig) => void)
client.on('sessionCreated', (data: { conversationId: string }) => void)
client.on('toolConfirmation', (data: ToolConfirmation) => void)
client.on('usageWarning', (data: { message: string, warningType: string }) => void)
client.on('error', (error: string) => void)
client.on('disconnect', (code?: number, reason?: string) => void)
```

Use `client.off(event, listener)` to remove a listener.

---

## Client Variables

Client variables allow authenticated tools to access data from the visitor's browser (localStorage, cookies, etc.). The SDK supports three modes:

### Automatic (recommended)

When no `clientVariables` or `clientVariableConfigs` are provided, the SDK automatically fetches the agent's configuration via preflight and resolves variables from the browser — the same behavior as the embedded widget.

```ts
const client = new TakeSalesClient({
  agentId: 'your-agent-uuid',
  wsUrl: 'wss://api-agent.takesales.ai/ws',
  // Client variables are resolved automatically from preflight config
})
```

### Manual

Pass pre-resolved values directly:

```ts
const client = new TakeSalesClient({
  agentId: 'your-agent-uuid',
  wsUrl: 'wss://api-agent.takesales.ai/ws',
  clientVariables: {
    auth_token: localStorage.getItem('token') ?? '',
    user_id: '12345',
  },
})
```

### Config-based

Pass extraction configs (same format as the dashboard):

```ts
import { type ClientVariableConfig } from '@takesales/sdk'

const client = new TakeSalesClient({
  agentId: 'your-agent-uuid',
  wsUrl: 'wss://api-agent.takesales.ai/ws',
  clientVariableConfigs: [
    { name: 'auth_token', source: 'localStorage', key: 'token' },
    { name: 'user_id', source: 'cookie', key: 'uid' },
  ],
})
```

Supported sources: `localStorage`, `sessionStorage`, `cookie`, `window`, `querySelector`, `meta`.

If both `clientVariables` and `clientVariableConfigs` are provided, manual values take precedence.

---

## Rich Content Types

The agent can send rich content alongside text messages. Each type has a specific data shape:

### Carousel

```ts
{ type: 'carousel', data: CarouselCard[] }

interface CarouselCard {
  title: string
  description?: string
  imageUrl?: string
  url: string
  price?: string
  address?: string
  area?: string
  bedrooms?: number
  images?: string[]
}
```

### Quick Replies

```ts
{ type: 'quick_replies', data: QuickReplyOption[] }

interface QuickReplyOption {
  label: string
  value: string
}
```

**Usage:** render buttons and call `sendText(option.value)` on click.

### Lead Form

```ts
{ type: 'lead_form', data: LeadFormCard }

interface LeadFormCard {
  formId: string
  fields: Array<{
    key: string
    type: 'text' | 'email' | 'phone' | 'textarea'
    label: string
    placeholder?: string
    required: boolean
  }>
  submitLabel?: string
  successMessage?: string
}
```

**Usage:** render a form and call `sendFormSubmit(formId, data)` on submit.

### Schedule Form

```ts
{ type: 'schedule_form', data: ScheduleFormCard }

interface ScheduleFormCard {
  availableSlots: Array<{ date: string; times: string[] }>
  propertyTitle?: string
  scheduleId?: string
}
```

**Usage:** render date/time picker and call `sendScheduleConfirm(scheduleId, date, time)`.

### Inline Map

```ts
{ type: 'inline_map', data: InlineMapCard }

interface InlineMapCard {
  lat: number
  lng: number
  address: string
  zoom?: number
  googleMapsApiKey?: string
}
```

### Photo Gallery

```ts
{ type: 'photo_gallery', data: PhotoGalleryCard }

interface PhotoGalleryCard {
  images: string[]
  title?: string
}
```

### Comparator

```ts
{ type: 'comparator', data: ComparatorCard }

interface ComparatorCard {
  properties: Array<{
    title: string
    url?: string
    imageUrl?: string
    price?: string
    area?: string
    bedrooms?: number
    bathrooms?: number
    differentials?: string[]
  }>
}
```

### Tool Confirmation

When a tool has risk level `confirm`, the agent pauses execution and sends a confirmation event before proceeding. In the embedded widget, a confirmation card appears automatically in the chat. If you're using the SDK, you need to listen for the event and build your own UI.

```ts
{ type: 'tool_confirmation', data: ToolConfirmation }

interface ToolConfirmation {
  confirmationId: string
  toolName: string
  message: string
  details?: Record<string, unknown>
}
```

**Example:**

```ts
// 1. Listen for the confirmation event
client.on('toolConfirmation', (data) => {
  // data.toolName → tool name (e.g. "cancel_subscription")
  // data.message  → action description
  // data.details  → parameters to be sent (e.g. { userId: "123" })

  // Show a confirmation dialog/card in your UI
  showConfirmDialog({
    title: data.toolName,
    message: data.message,
    params: data.details,
    onConfirm: () => client.sendToolConfirmation(data.confirmationId, true),
    onCancel: () => client.sendToolConfirmation(data.confirmationId, false),
  });
});
```

If the visitor confirms, the tool executes normally. If they cancel, the agent receives a notice and continues the conversation without executing the action.

---

## Agent Config

After connecting, you receive the agent configuration from the server:

```ts
interface ServerAgentConfig {
  agentId: string
  workspaceId: string
  agentName: string
  greeting: string
  avatarUrl: string | null
  language: string
  enableVoice: boolean
  enableTextChat: boolean
  collectVisitorEmail: boolean
  collectVisitorName: boolean
  collectVisitorPhone: boolean
  quickReplies?: string[]
  primaryColor?: string
  secondaryColor?: string
  textColor?: string
  backgroundColor?: string
  title?: string
  subtitle?: string
  // ... and more
}
```

Use this to style your UI, show the greeting, and configure contact collection.

---

## Connection Lifecycle

```
connect()
  |
  v
Preflight fetch (auto-discovers client variable configs)
  |
  v
WebSocket open
  |
  v
Service setup sent (agent_id + visitor info + client variables)
  |
  v
Server sends: agentConfig  -->  'agentConfig' event
Server sends: sessionCreated  -->  'sessionCreated' event
  |
  v
Session setup sent (text-only mode)
  |
  v
Server sends: setupComplete  -->  'connected' event
  |
  v
Ready to send/receive messages
  |
  v
disconnect() or connection lost
  |
  v
Auto-reconnect (exponential backoff, up to 5 attempts)
```

---

## Security

Authentication uses the same model as Google Maps or Stripe.js:

- **`agentId`** is a public identifier (UUID) embedded in client code
- **Origin validation** is enforced server-side via the agent's allowed origins list
- Configure allowed origins in the Take Sales dashboard under Agent Settings

No API keys or bearer tokens are needed for client-side usage.

---

## Examples

### Resuming a session

```ts
const savedId = localStorage.getItem('conversationId')

const client = new TakeSalesClient({
  agentId: 'your-agent-uuid',
  wsUrl: 'wss://api-agent.takesales.ai/ws',
  conversationId: savedId ?? undefined,
})

client.on('sessionCreated', ({ conversationId }) => {
  localStorage.setItem('conversationId', conversationId)
})

client.connect()
```

### With visitor identification

```ts
const client = new TakeSalesClient({
  agentId: 'your-agent-uuid',
  wsUrl: 'wss://api-agent.takesales.ai/ws',
  visitor: {
    email: 'visitor@example.com',
    name: 'John Doe',
    phone: '+5511999999999',
  },
})

client.connect()
```