SDK JavaScript
SDK para integrar agentes Take Sales em interfaces customizadas via WebSocket.
Introdução
O SDK permite integrar agentes Take Sales em qualquer interface customizada. Conecte-se via WebSocket e construa sua própria interface de chat.
Para casos simples, o widget embutido é a forma mais fácil de adicionar um agente ao seu site — basta colar um snippet, sem escrever código. Use o SDK quando precisar de controle total sobre a interface e a experiência do usuário.
Instalação
npm install @takesales/sdkEsta página descreve o @takesales/sdk 0.1.33, a versão publicada hoje. Ela tem limitações conhecidas:
- o hook
useTakeSalesAgentduplica o texto de cada resposta do agente ("Olá" aparece como "OláOlá"). Até a próxima versão, use oTakeSalesCliente trate o streaming como em Streaming de texto; - mensagens de atendente humano (handoff), troca de agente e encerramento da conversa pelo servidor não chegam ao SDK. Se o agente tem transferência para humano habilitada, use o widget embutido;
- o
datade rich content e o eventotoolConfirmationchegam num formato diferente das typings do pacote. As seções Rich Content e Tool Confirmation mostram o formato real.
Quick Start (React)
import { useTakeSalesAgent } from '@takesales/sdk/react'
function Chat() {
const { messages, sendMessage, isConnected, agentConfig } = useTakeSalesAgent({
agentId: 'your-agent-uuid',
wsUrl: 'wss://api-agent.takesales.ai/ws',
})
if (!isConnected) return <div>Connecting...</div>
return (
<div>
<h2>{agentConfig?.agentName}</h2>
{messages.map((msg) => (
<div key={msg.id} className={msg.role}>
<p>{msg.text}</p>
{msg.richContent?.type === 'carousel' && (
<MyCarousel cards={msg.richContent.data} />
)}
{msg.richContent?.type === 'quick_replies' && (
<QuickReplies options={richContentData(msg.richContent)} onSelect={sendMessage} />
)}
</div>
))}
<input
placeholder="Type a message..."
onKeyDown={(e) => {
if (e.key === 'Enter') {
sendMessage(e.currentTarget.value)
e.currentTarget.value = ''
}
}}
/>
</div>
)
}Quick Start (Vanilla JS/TypeScript)
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) => {
// endOfTurn = true carries the whole turn again: replace, don't append
if (endOfTurn) console.log('Agent says:', text)
})
client.on('richContent', (content) => {
console.log('Rich content:', content.type, richContentData(content))
})
client.on('error', (err) => {
console.error('Error:', err)
})
client.on('disconnect', (code, reason) => {
console.log('Disconnected:', code, reason)
})
client.connect()API Reference
TakeSalesClient
Client WebSocket framework-agnostic.
Constructor
new TakeSalesClient(config: TakeSalesClientConfig)| Parameter | Type | Required | Description |
|---|---|---|---|
agentId | string | Sim | UUID do agente |
wsUrl | string | Sim | URL do servidor WebSocket |
visitor | object | Não | Informações do visitante (veja abaixo) |
conversationId | string | Não | Retomar uma sessão anterior |
isTest | boolean | Não | Marcar como sessão de teste |
clientVariables | Record<string, string> | Não | Variáveis de cliente pré-resolvidas para enriquecimento de ferramentas |
clientVariableConfigs | ClientVariableConfig[] | Não | Resolução automática de variáveis do browser (localStorage, cookies, etc.) |
reconnect | object | Não | Configurações de reconexão |
Objeto visitor:
| Campo | Tipo | Descrição |
|---|---|---|
email | string | Email do visitante |
name | string | Nome do visitante |
phone | string | Telefone do visitante |
contactData | Record<string, string> | Campos customizados. Ignorado na conexão: envie com sendUpdateVisitor depois do sessionCreated (veja Com identificação do visitante) |
visitorId | string | ID persistente do visitante. Se omitido, o SDK gera um e guarda em localStorage |
Objeto reconnect:
| Campo | Tipo | Default | Descrição |
|---|---|---|---|
enabled | boolean | true | Habilitar auto-reconexão |
maxAttempts | number | 5 | Máximo de tentativas |
baseDelay | number | 1000 | Delay base em ms (exponential backoff) |
Methods
| Método | Descrição |
|---|---|
connect() | Abre conexão WebSocket |
disconnect() | Fecha conexão (sem auto-reconexão) |
sendText(text) | Envia uma mensagem de texto |
sendFormSubmit(formId, data) | Submete um formulário de lead |
sendScheduleConfirm(scheduleId, date, time) | Confirma um agendamento |
sendToolConfirmation(confirmationId, confirmed) | Aprova/rejeita uma ação de ferramenta |
sendUpdateVisitor(data) | Atualiza informações do visitante (email, name, phone, contactData). Só funciona depois do sessionCreated |
registerTool(name, tool) | Registra uma client tool (veja Client tools). Chame antes de connect() |
unregisterTool(name) | Remove uma client tool |
Properties
| Propriedade | Tipo | Descrição |
|---|---|---|
isConnected | boolean | Estado atual da conexão |
conversationId | string | null | ID da sessão atual |
agentConfig | ServerAgentConfig | null | Configuração do agente recebida do servidor |
Events
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('toolCall', (data: { name: string, args: Record<string, unknown>, callId?: string }) => 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) para remover um listener.
message chega uma vez por pedaço do streaming com endOfTurn = false e, no fim do turno, mais uma vez com endOfTurn = true trazendo o texto inteiro do turno. Concatene os pedaços ou use só o texto final, nunca os dois.
error também recebe recusas do servidor: agente sem permissão para a origin, workspace sem créditos (connection_error) e setup inválido.
useTakeSalesAgent (React Hook)
import { useTakeSalesAgent } from '@takesales/sdk/react'Config
Aceita todos os campos de TakeSalesClientConfig mais:
| Parameter | Type | Default | Description |
|---|---|---|---|
autoConnect | boolean | true | Conectar ao montar o componente |
Return Value
| Campo | Tipo | Descrição |
|---|---|---|
messages | TakeSalesMessage[] | Todas as mensagens (usuário + agente) |
sendMessage | (text: string) => void | Envia uma mensagem (adiciona a messages automaticamente) |
isConnected | boolean | Estado da conexão |
agentConfig | ServerAgentConfig | null | Configuração do agente |
conversationId | string | null | ID da sessão |
connect | () => void | Conectar manualmente |
disconnect | () => void | Desconectar manualmente |
sendFormSubmit | (formId, data) => void | Submeter formulário de lead |
sendScheduleConfirm | (scheduleId, date, time) => void | Confirmar agendamento |
sendToolConfirmation | (confirmationId, confirmed) => void | Aprovar/rejeitar ferramenta |
registerTool | (name, tool) => void | Registrar uma client tool |
unregisterTool | (name) => void | Remover uma client tool |
Aceita também tools: Record<string, LocalToolDefinition> na config para registrar client tools ao montar.
No 0.1.33 o hook duplica o texto de toda resposta do agente e não expõe toolConfirmation, usageWarning nem error. Para produção, prefira o TakeSalesClient até a próxima versão.
TakeSalesMessage
interface TakeSalesMessage {
id: string
role: 'user' | 'agent'
text: string
richContent?: RichContent
timestamp: Date
}Rich Content Types
O agente pode enviar conteúdo rico junto com mensagens de texto. Cada tipo tem uma estrutura de dados específica.
No 0.1.33, só o carousel chega com data no formato abaixo. Nos outros tipos o data vem com o envelope do servidor: { options } em quick_replies, { leadForm } em lead_form, { scheduleForm }, { inlineMap }, { photoGallery } e { comparator }. Use um helper que funcione nos dois formatos:
import type { RichContent } from '@takesales/sdk'
const ENVELOPE: Record<string, string> = {
quick_replies: 'options',
lead_form: 'leadForm',
schedule_form: 'scheduleForm',
inline_map: 'inlineMap',
photo_gallery: 'photoGallery',
comparator: 'comparator',
}
export function richContentData(content: RichContent): any {
const data = content.data as any
const key = ENVELOPE[content.type]
return (key && data?.[key]) ?? data
}Os campos de rich content (URLs, títulos, imagens) podem vir de ferramentas customizadas, ou seja, de APIs de terceiros. Trate como dado não confiável: aceite só URLs https: em href e src e nunca injete o conteúdo como HTML.
Carousel
{ type: 'carousel', data: CarouselCard[] }
interface CarouselCard {
title: string
url: string
subtitle?: string
description?: string
imageUrl?: string
price?: string
}Quick Replies
{ type: 'quick_replies', data: QuickReplyOption[] }
interface QuickReplyOption {
label: string
value: string
}Renderize botões e chame sendText(option.value) (vanilla) ou sendMessage(option.value) (React) no clique. No 0.1.33 o array está em data.options (use richContentData).
Lead Form
{ 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
}Renderize um formulário e chame sendFormSubmit(formId, data) ao submeter.
Schedule Form
{ type: 'schedule_form', data: ScheduleFormCard }
interface ScheduleFormCard {
availableSlots: Array<{ date: string; times: string[] }>
propertyTitle?: string
scheduleId?: string
}Renderize um seletor de data/hora e chame sendScheduleConfirm(scheduleId, date, time).
Inline Map
{ type: 'inline_map', data: InlineMapCard }
interface InlineMapCard {
lat: number
lng: number
address: string
zoom?: number
googleMapsApiKey?: string
}Photo Gallery
{ type: 'photo_gallery', data: PhotoGalleryCard }
interface PhotoGalleryCard {
images: string[]
title?: string
}Comparator
{ type: 'comparator', data: ComparatorCard }
interface ComparatorCard {
title?: string
// Columns: anything comparable (plans, products, listings)
items?: Array<{ title: string; url?: string; imageUrl?: string; price?: string }>
// Rows: a label plus one value per item, in `items` order
attributes?: Array<{ label: string; values: string[] }>
// Legacy real-estate shape sent by older agents
properties?: Array<{
title: string
url?: string
imageUrl?: string
price?: string
area?: string
bedrooms?: number
bathrooms?: number
differentials?: string[]
}>
}Tool Confirmation
Quando uma tool tem nível de risco confirm, o agente pausa a execução e envia um evento de confirmação antes de executar. No widget embutido, um card de confirmação aparece automaticamente no chat. Se você usa o SDK, precisa ouvir o evento e montar sua própria UI.
{ type: 'tool_confirmation', data: ToolConfirmation }
// What the server sends
interface ToolConfirmationFrame {
confirmationId: string
functionName: string // ex: "cancel_subscription"
functionTitle: string // nome amigável configurado na tool
functionDescription: string // descrição da ação
args: Record<string, unknown> // parâmetros que serão enviados
}As typings do 0.1.33 declaram ToolConfirmation como { confirmationId, toolName, message, details }, campos que o servidor não envia. Leia os campos acima com um cast. O hook React não expõe esse evento: use o TakeSalesClient.
Exemplo de uso:
client.on('toolConfirmation', (event) => {
const data = event as unknown as ToolConfirmationFrame
// Exiba um diálogo/card de confirmação na sua UI
showConfirmDialog({
title: data.functionTitle || data.functionName,
message: data.functionDescription,
params: data.args,
onConfirm: () => client.sendToolConfirmation(data.confirmationId, true),
onCancel: () => client.sendToolConfirmation(data.confirmationId, false),
})
})Se o visitante confirmar, a tool é executada normalmente. Se cancelar, o agente recebe um aviso e continua a conversa sem executar a ação.
Agent Config
Após conectar, você recebe a configuração do agente:
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
textColor?: string
backgroundColor?: string
title?: string
subtitle?: string
showBranding?: boolean
enableHandoff?: boolean // true: o agente pode transferir para um humano (sem suporte no 0.1.33)
typingEvents?: boolean // true: o servidor agrupa mensagens seguidas do visitante numa resposta
hasKnowledgeBase?: boolean
}O objeto tem outros campos de uso do widget embutido (voz, formulário de contato, guardrails). As typings do 0.1.33 listam secondaryColor e conversationId, que o servidor não envia: o ID da conversa chega no sessionCreated.
Use para estilizar sua UI, exibir a saudação e configurar coleta de dados do visitante.
Client Variables
Client variables permitem que ferramentas autenticadas acessem dados do browser do visitante (localStorage, cookies, etc.). O SDK suporta três modos:
Automático (recomendado)
Quando nenhum clientVariables ou clientVariableConfigs é fornecido, o SDK busca automaticamente a configuração do agente via preflight e resolve as variáveis do browser — o mesmo comportamento do widget embutido.
const client = new TakeSalesClient({
agentId: 'your-agent-uuid',
wsUrl: 'wss://api-agent.takesales.ai/ws',
// Client variables são resolvidas automaticamente via preflight config
})Manual
Passe valores pré-resolvidos diretamente:
const client = new TakeSalesClient({
agentId: 'your-agent-uuid',
wsUrl: 'wss://api-agent.takesales.ai/ws',
clientVariables: {
auth_token: localStorage.getItem('token') ?? '',
user_id: '12345',
},
})Baseado em configuração
Passe configs de extração (mesmo formato do dashboard):
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' },
],
})Sources suportadas: localStorage, sessionStorage, cookie, window, querySelector, meta.
Se ambos clientVariables e clientVariableConfigs forem fornecidos, os valores manuais têm precedência.
No modo automático, quem decide o que o SDK lê do navegador (chaves de localStorage, cookies sem HttpOnly, caminhos em window) é a configuração do agente no dashboard. Se o seu site guarda tokens no navegador, prefira o modo manual ou o baseado em configuração, com a lista explícita do que enviar. Variável que carrega token ou sessão precisa ser marcada como secreta no agente: variável não secreta entra no contexto do modelo.
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)
Segurança
A autenticação usa o mesmo modelo do Google Maps ou Stripe.js:
agentIdé um identificador público (UUID) embutido no código do cliente- Validação de origin é feita server-side via lista de origins permitidas do agente
- Configure origins permitidas no dashboard em Configurações do Agente
Nenhuma API key ou bearer token é necessária para uso client-side.
Dados que o SDK envia
Ao conectar, o 0.1.33 envia ao servidor, sem opção para desligar:
- um ID persistente do visitante, gravado em
localStorage(takesales_visitor_id); - dados do navegador para identificar o visitante: resolução e color depth da tela, fuso, idiomas, plataforma, número de núcleos e
devicePixelRatio; - a URL completa da página (
location.href, com query string e fragmento) e odocument.referrer.
Se o seu site exige consentimento antes desse tipo de coleta (LGPD/GDPR), só chame connect() (ou monte o hook com autoConnect: true) depois do consentimento. Evite conectar em páginas cuja URL carrega tokens ou dados pessoais, como callback de OAuth e link de redefinição de senha.
O ID da conversa dá acesso ao histórico dela. Guarde-o como guardaria um identificador de sessão.
Exemplos
Streaming de texto
let current = '' // text of the turn being streamed
let typing = false
client.on('message', (text, endOfTurn) => {
if (endOfTurn) {
current = text // full turn: replace, don't append
typing = false
} else {
current += text
typing = true
}
render(current, typing)
})Com o debounce de respostas ligado no agente, várias mensagens seguidas do visitante recebem uma resposta só.
Handling quick replies
function Chat() {
const { messages, sendMessage } = useTakeSalesAgent({ ... })
return (
<div>
{messages.map((msg) => (
<div key={msg.id}>
<p>{msg.text}</p>
{msg.richContent?.type === 'quick_replies' && (
<div className="flex gap-2">
{richContentData(msg.richContent).map((opt: QuickReplyOption) => (
<button key={opt.value} onClick={() => sendMessage(opt.value)}>
{opt.label}
</button>
))}
</div>
)}
</div>
))}
</div>
)
}Retomando uma sessão
function Chat() {
const [savedId] = useState(() => localStorage.getItem('conversationId'))
const { messages, sendMessage, conversationId } = useTakeSalesAgent({
agentId: 'your-agent-uuid',
wsUrl: 'wss://api-agent.takesales.ai/ws',
conversationId: savedId ?? undefined,
})
useEffect(() => {
if (conversationId) localStorage.setItem('conversationId', conversationId)
}, [conversationId])
return <div>...</div>
}Com identificação do visitante
const { messages, sendMessage } = useTakeSalesAgent({
agentId: 'your-agent-uuid',
wsUrl: 'wss://api-agent.takesales.ai/ws',
visitor: {
email: user.email,
name: user.name,
phone: user.phone,
},
})Com client variables (React)
const { messages, sendMessage } = useTakeSalesAgent({
agentId: 'your-agent-uuid',
wsUrl: 'wss://api-agent.takesales.ai/ws',
clientVariables: {
auth_token: localStorage.getItem('token') ?? '',
},
})Retomando uma sessão (Vanilla JS)
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()Com identificação do visitante (Vanilla JS)
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',
},
})
// Custom fields are only accepted after the session exists
client.on('sessionCreated', () => {
client.sendUpdateVisitor({ contactData: { plan: 'pro' } })
})
client.connect()Client tools
Client tools são funções do seu site que o agente pode chamar. Registre antes de connect(): a lista vai no setup da conexão (máximo de 50 tools, nome com até 64 caracteres).
client.registerTool('get_cart', {
description: 'Returns the items in the visitor cart',
parameters: { type: 'object', properties: {} },
handler: async () => ({ items: cart.items }),
})
client.connect()O SDK executa o handler e devolve o resultado ao agente sozinho. Os args são gerados pelo modelo e podem ser induzidos pelo visitante ou por conteúdo externo: valide tudo no handler e não execute ações sensíveis sem confirmação do usuário.
Troubleshooting
Conexão falhando
- Verifique se o
wsUrlestá correto:wss://api-agent.takesales.ai/ws - Confirme que o
agentIdexiste e corresponde a um agente ativo no dashboard - Verifique se o domínio da sua aplicação está na lista de origins permitidas nas configurações do agente
Nenhuma mensagem recebida
- Confirme que o agente tem um system prompt configurado
- Verifique se o agente tem pelo menos uma fonte na base de conhecimento (se aplicável ao seu caso de uso)
- Verifique o evento
errorpara mensagens de diagnóstico do servidor
Erros de CORS
- Adicione o domínio exato da sua aplicação (incluindo protocolo e porta) às origins permitidas nas configurações do agente
- Em desenvolvimento local, adicione
http://localhost:3000(ou a porta que estiver usando)
Nenhuma resposta durante atendimento humano
Quando a conversa é transferida para um atendente, a IA para de responder e as mensagens do atendente não chegam ao SDK 0.1.33. Se o agente tem transferência para humano habilitada, use o widget embutido.
Loop de reconexão
- Se o evento
errortrouxe "Invalid setup message", o servidor recusou os dados da conexão (por exemplo,conversationIdque não é UUID ou mais de 50 client tools). O 0.1.33 tenta reconectar com os mesmos dados: corrija a config em vez de esperar - Verifique se o agente está ativo (não pausado ou arquivado) no dashboard
- Confirme que o plano do workspace tem créditos disponíveis
- Se o problema persistir, desabilite a reconexão automática para investigar:
reconnect: { enabled: false }

