Get Started

Channels

Deliver agents to end users via embeddable chat widgets, Slack, email, or phone. Agents meet users where they are — and every channel is AgentCo-branded.

Channels are AgentCo-branded. The chat widget shows AgentCo's name and colors. The Slack bot is AgentCo's bot. Theazo is invisible to end users.

Chat embed

Add a chat widget to any website with a single script tag. Customize colors, position, title, and placeholder text to match your brand.

chat-widget.ts
import { Theazo } from 'theazo'

const theazo = new Theazo({ apiKey: 'th_live_...' })

const widget = await theazo.channels.chatEmbed({
  agent: 'support_agent',
  theme: {
    primaryColor: '#6366f1',
    position: 'bottom-right',
    title: 'AI Support',
    placeholder: 'Ask anything...',
  },
})

console.log(widget.id)        // 'ch_abc'
console.log(widget.scriptTag)
// '<script src="https://widget.theazo.com/w/ch_abc.js"></script>'

Drop the scriptTag into any HTML page. The widget handles conversation creation, message streaming, and reconnection automatically.

Public widget endpoints

Chat widgets run in end-user browsers where there is no API key. Theazo exposes a set of unauthenticated endpoints scoped to a channel ID — the channel ID itself acts as the auth token. These routes are mounted outside the API key auth middleware.

POST/v1/channels/:id/messagesSend a message (no API key needed, channel ID is the auth)
GET/v1/channels/:id/messagesGet message history for a visitor
GET/v1/channels/:id/streamSSE stream for real-time responses

These endpoints are designed for browser-side use. No API key or authorization header is required — the channel ID in the URL controls access.

browser-fetch.js
// Send a message from the browser — no API key needed
const res = await fetch('https://api.theazo.com/v1/channels/ch_abc/messages', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    visitorId: 'user_456',
    message: 'How do I reset my password?',
  }),
})

const data = await res.json()
console.log(data.messageId) // 'msg_x1y2z3'

// Stream the agent's response in real time
const sse = new EventSource(
  'https://api.theazo.com/v1/channels/ch_abc/stream?visitorId=user_456'
)
sse.onmessage = (event) => {
  const chunk = JSON.parse(event.data)
  console.log(chunk.text) // partial response text
}

Visitor identity

Visitors are identified by a visitorId string passed by the widget. Each unique visitorId gets its own conversation thread within the channel.

If no visitorId is provided, one is auto-generated per browser session and stored in sessionStorage. This means anonymous visitors keep their conversation for the duration of the tab, but start fresh on reload.

request-body.json
{
  "visitorId": "user_456",
  "message": "hello"
}
Use a stable visitorId (like your app's user ID) to preserve conversation history across sessions. Anonymous visitors get a random ID scoped to sessionStorage.

Rate limiting

Public widget endpoints are rate-limited via a Redis sliding window to prevent abuse. Three independent limits are enforced on every request:

100 messages/minPer channel — total throughput across all visitors
20 messages/minPer visitor — scoped by visitorId
30 messages/minPer IP address — prevents single-origin flooding

When any limit is exceeded, the endpoint returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.

rate-limit-response.json
// 429 Too Many Requests
// Retry-After: 12

{
  "error": "rate_limit_exceeded",
  "message": "Visitor rate limit exceeded (20/min). Retry after 12 seconds.",
  "retryAfter": 12
}

Slack

Connect an agent to one or more Slack channels. The bot listens for messages, runs the agent, and replies in-thread.

slack-channel.ts
await theazo.channels.slack({
  agent: 'team_assistant',
  workspace: 'T01234567',
  channels: ['#general', '#support'],
  botName: 'Acme Bot',
})
// Bot appears in Slack as "Acme Bot"
// Responds to @mentions and DMs
Slack integration requires a Slack app with the chat:write, app_mentions:read, and channels:history scopes. Configure the OAuth redirect URL in your Theazo dashboard under Settings.

Email

Route incoming emails to an agent. The agent processes the email content and sends a response from your domain.

email-channel.ts
await theazo.channels.email({
  agent: 'email_handler',
  address: 'support@yourproduct.com',
})
// Incoming emails to support@yourproduct.com trigger the agent
// Agent reads the email, processes it, and sends a reply

Configure DNS records (MX, SPF, DKIM) for your domain in Settings to enable email receiving. Theazo handles parsing, threading, and attachment extraction.

Phone

Connect a phone number to an agent for voice conversations. Powered by Twilio for telephony and real-time voice synthesis.

phone-channel.ts
await theazo.channels.phone({
  agent: 'phone_agent',
  phoneNumber: '+1234567890',
  voice: 'alloy',
})
// Incoming calls to +1234567890 connect to the agent
// Agent speaks using the selected voice model

Managing channels

List channels

const channels = await theazo.channels.list()

// channels = [
//   { id: 'ch_001', type: 'chat_embed', enabled: true,  conversationCount: 1243 },
//   { id: 'ch_002', type: 'slack',      enabled: true,  conversationCount: 872  },
//   { id: 'ch_003', type: 'email',      enabled: true,  conversationCount: 3456 },
//   { id: 'ch_004', type: 'phone',      enabled: false, conversationCount: 567  },
// ]

Update a channel

await theazo.channels.update('ch_001', {
  config: {
    theme: {
      primaryColor: '#10b981',
      title: 'Help Center',
    },
  },
})

Disable and enable

Disabling a channel stops it from accepting new conversations. Existing conversations are not affected.

// Disable — stops accepting new conversations
await theazo.channels.disable('ch_001')

// Re-enable
await theazo.channels.enable('ch_001')

View conversations

const conversations = await theazo.channels.conversations('ch_001')

// conversations = [
//   { id: 'conv_01', userId: 'user_42', messageCount: 12, ... },
//   { id: 'conv_02', userId: 'user_99', messageCount: 3,  ... },
// ]

Test a channel

Send a test message through a channel to verify it's configured correctly.

const result = await theazo.channels.test('ch_001')
console.log(result.ok)      // true
console.log(result.message) // 'Test message sent via chat_embed'

Delete a channel

await theazo.channels.delete('ch_001')
// Channel is permanently removed
// Existing conversations are preserved in logs

API reference

theazo.channels.chatEmbed(opts)Promise<Channel>Create a chat embed widget. Returns channel with scriptTag for embedding.
theazo.channels.slack(opts)Promise<Channel>Connect an agent to Slack channels. Requires workspace ID and channel names.
theazo.channels.email(opts)Promise<Channel>Route emails to an agent. Requires a configured email address.
theazo.channels.phone(opts)Promise<Channel>Connect a phone number to an agent. Requires Twilio integration.
theazo.channels.list()Promise<Channel[]>List all channels for the platform.
theazo.channels.get(id)Promise<Channel>Get a channel by ID.
theazo.channels.update(id, opts)Promise<Channel>Update channel config or enabled status.
theazo.channels.disable(id)Promise<Channel>Disable a channel. Stops accepting new conversations.
theazo.channels.enable(id)Promise<Channel>Re-enable a disabled channel.
theazo.channels.delete(id)Promise<void>Permanently delete a channel.
theazo.channels.conversations(id)Promise<Conversation[]>List conversations for a channel.
theazo.channels.test(id)Promise<TestResult>Send a test message through the channel.

REST endpoints

POST/v1/channelsCreate a channel (type discriminates config shape)
GET/v1/channelsList all channels
GET/v1/channels/:idGet channel by ID
PUT/v1/channels/:idUpdate channel config or enabled status
DELETE/v1/channels/:idDelete a channel
GET/v1/channels/:id/conversationsList conversations for a channel
POST/v1/channels/:id/testSend a test message
Was this page helpful?
Ask anything...⌘I