How to Use Channel-Based Function Calling with Dispatch
This tutorial demonstrates how to implement channel-based function calling using the dispatch() method in ChatBotKit. This approach is ideal for background AI tasks where the conversation runs asynchronously on the server.
Learning Objectives
By the end of this tutorial, you will be able to:
- Use the
dispatch()method to start background conversations - Subscribe to conversation events via channels
- Implement static function results for predetermined responses
- Implement channel-based function results for dynamic execution
- Handle the
waitForChannelMessageBeginevent to execute functions
Prerequisites
- Node.js 18+ installed
- A ChatBotKit account with an API secret
- Basic understanding of async/await and event streams
Estimated time: 20 minutes
Understanding Dispatch vs Complete
ChatBotKit offers two primary ways to run conversations:
| Method | Execution | Best For |
|---|---|---|
dispatch() | Background/async | Long-running tasks, webhooks, background processing |
complete() | Inline/blocking | Interactive chat, real-time responses |
The dispatch() method starts a conversation that runs in the background on the server. It immediately returns a channelId that you can subscribe to for receiving events.
Step 1: Set Up Your Project
Create a new Node.js project and install the ChatBotKit SDK:
mkdir function-calling-example
cd function-calling-example
npm init -y
npm install @chatbotkit/sdk dotenv
Create a .env file with your API secret:
CHATBOTKIT_API_SECRET=your_api_secret_here
Step 2: Create the Main Script
Create a file called index.js:
import * as dotenv from 'dotenv'
import { ChatBotKit } from '@chatbotkit/sdk/index.js'
import { randomBytes } from 'node:crypto'
dotenv.config()
async function main() {
const client = new ChatBotKit({
secret: process.env.CHATBOTKIT_API_SECRET,
})
// Generate unique channel IDs (must be at least 16 characters)
const weatherChannelId = `weather-${randomBytes(16).toString('hex')}`
console.log('Starting background conversation...')
}
main().catch(console.error)
Step 3: Define Functions with Different Result Types
ChatBotKit supports two types of function results:
Static Results
Static results return predetermined data immediately to the AI:
{
name: 'get_current_time',
description: 'Get the current time for a specified timezone',
parameters: {
type: 'object',
properties: {
timezone: {
type: 'string',
description: 'The timezone, e.g. America/New_York',
},
},
required: ['timezone'],
},
// Static result - returned immediately
result: {
data: {
time: '10:30 AM',
date: 'Monday, January 26, 2026',
timezone: 'America/New_York',
},
},
}
Channel-Based Results
Channel-based results require you to execute the function and publish the result:
{
name: 'get_weather',
description: 'Get the current weather for a location',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'The city name, e.g. New York',
},
},
required: ['location'],
},
// Channel result - you must publish the result
result: {
channel: weatherChannelId,
},
}
Step 4: Dispatch the Conversation
Use dispatch() to start the background conversation:
const { channelId } = await client.conversation.dispatch(null, {
model: 'claude-4.5-sonnet',
backstory: 'You are a helpful assistant that provides weather and time information.',
messages: [
{
type: 'user',
text: 'What time is it in New York, and what is the weather like there?',
},
],
functions: [
// ... your function definitions
],
})
console.log(`Conversation dispatched. Channel: ${channelId}`)
Step 5: Subscribe to Events
Subscribe to the channel to receive events as the conversation progresses:
for await (const event of client.channel.subscribe(channelId).stream()) {
if (event.type !== 'message') {
continue
}
const eventData = event.data
// Handle different event types based on eventData.type
if (eventData.type === 'waitForChannelMessageBegin') {
// Function execution required
}
if (eventData.type === 'message') {
// Bot message or activity
}
if (eventData.type === 'result') {
// Conversation completed
break
}
}
Step 6: Handle Channel Function Calls
When you receive a waitForChannelMessageBegin event, execute the function and publish the result:
if (eventData.type === 'waitForChannelMessageBegin') {
const waitData = eventData.data
const functionName = waitData.function.name
const functionArgs = waitData.function.args
const resultChannel = waitData.channel
console.log(`Function call: ${functionName}`)
console.log(`Arguments: ${JSON.stringify(functionArgs)}`)
// Execute your function
let functionResult
if (functionName === 'get_weather') {
functionResult = await fetchWeatherData(functionArgs.location)
}
// Publish the result back to the channel
await client.channel.publish(resultChannel, {
message: functionResult,
})
console.log('Result published')
}
Complete Example
Here's the complete working example:
import * as dotenv from 'dotenv'
import { ChatBotKit } from '@chatbotkit/sdk/index.js'
import { randomBytes } from 'node:crypto'
dotenv.config()
async function main() {
const client = new ChatBotKit({
secret: process.env.CHATBOTKIT_API_SECRET,
})
const weatherChannelId = `weather-${randomBytes(16).toString('hex')}`
// Dispatch the conversation
const { channelId } = await client.conversation.dispatch(null, {
model: 'claude-4.5-sonnet',
backstory: 'You are a helpful assistant that provides weather and time information.',
messages: [
{
type: 'user',
text: 'What time is it in New York, and what is the weather like there?',
},
],
functions: [
{
name: 'get_current_time',
description: 'Get the current time for a specified timezone',
parameters: {
type: 'object',
properties: {
timezone: { type: 'string', description: 'The timezone' },
},
required: ['timezone'],
},
result: {
data: { time: '10:30 AM', date: 'Monday, January 26, 2026' },
},
},
{
name: 'get_weather',
description: 'Get the current weather for a location',
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: 'The city name' },
},
required: ['location'],
},
result: { channel: weatherChannelId },
},
],
})
// Subscribe to events
for await (const event of client.channel.subscribe(channelId).stream()) {
if (event.type !== 'message') continue
const eventData = event.data
if (eventData.type === 'waitForChannelMessageBegin') {
const { function: fn, channel } = eventData.data
// Execute and publish result
const result = { temperature: 42, conditions: 'partly cloudy' }
await client.channel.publish(channel, { message: result })
}
if (eventData.type === 'message' && eventData.data.type === 'bot') {
console.log(`Bot: ${eventData.data.text}`)
}
if (eventData.type === 'result') {
console.log('Conversation completed')
break
}
}
}
main().catch(console.error)
Troubleshooting
Channel ID Too Short
Channel IDs must be at least 16 characters. Use randomBytes(16).toString('hex') to generate sufficiently long IDs.
Missing Function Result
If you don't publish a result to the channel, the conversation will timeout. Always handle waitForChannelMessageBegin events.
Event Structure
Remember that events from channel.subscribe() come wrapped: the actual event type is in event.data.type, not event.type.
Next Steps
- Learn about inline function calling with complete()
- Explore caller-handled pure functions
- Read about building agentic systems