API Quick Start
Get started building Stream CoHost plugins in minutes with our comprehensive API.
Create Plugin
Set up a new plugin with our template
Add Commands
Register chat commands and handlers
Store Data
Use the database API for persistence
Add UI
Create admin interface and overlays
5-Minute Plugin
Create a simple "Hello World" plugin to get familiar with the API:
// plugins/hello/index.js
module.exports = {
name: 'hello',
dependencies: [], // list plugins this depends on
async init(bot) {
// Phase 1: Basic setup — routes, db prep
// Do NOT access other plugins here
bot.app.get('/api/hello/stats', (req, res) => {
res.json({ plugin: 'hello', commands: ['!hello'] });
});
},
async lateInit(bot) {
// Phase 2: All plugins loaded — cross-plugin calls safe here
bot.services.hello = {
greet: (username) => `Hello, ${username}!`
};
},
commands: {
hello: async (bot, msg, args) => {
// Store greeting in memory
await bot.memory.store(msg.username,
`${msg.username} used the hello command`,
'interactions'
);
await bot.say(msg.channel, `Hello ${msg.username}! Welcome to Stream CoHost!`);
}
}
};
Plugin Hook System
The non-blocking hook system lets plugins intercept and modify bot behavior without slowing down chat responses. All hooks run asynchronously and never block the main response pipeline.
beforeOpenAI
HookCalled before each OpenAI request. Can inject context, modify the prompt, or add system information. Runs in the background — never delays the AI response.
async lateInit(bot) {
if (bot.hooks?.register) {
bot.hooks.register('beforeOpenAI', async (context) => {
// Inject screen context before each AI call
context.extra = await getScreenDescription();
});
}
}
afterOpenAI
HookCalled after each OpenAI response. Can log usage, analyze sentiment, or trigger side effects. Never delays delivery of the AI response to chat.
async lateInit(bot) {
if (bot.hooks?.register) {
bot.hooks.register('afterOpenAI', async (context, response) => {
// Log AI response length for analytics
await logAnalytics({ length: response.length, user: context.username });
});
}
}
Bot Core API
The main bot instance provides access to all core functionality and services.
bot.registerCommand(trigger, handler, options)
MethodRegister a new chat command with the bot.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
trigger |
string | Yes | Command trigger (e.g., "!hello") |
handler |
function | Yes | Async function to handle command |
options |
object | No | Command configuration options |
Example
bot.registerCommand('!dice', async (context) => {
const roll = Math.floor(Math.random() * 6) + 1;
return {
success: true,
response: `🎲 ${context.username} rolled a ${roll}!`
};
}, {
description: 'Roll a 6-sided die',
cooldown: 5000, // 5 second cooldown
permissions: ['everyone']
});
bot.unregisterCommand(trigger)
MethodRemove a previously registered command.
Parameters
| Name | Type | Description |
|---|---|---|
trigger |
string | Command trigger to remove |
bot.sendMessage(message, channel)
MethodSend a message to the Twitch chat.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
message |
string | Yes | Message content to send |
channel |
string | No | Channel name (defaults to bot's channel) |
Database API
Access SQLite database for persistent data storage across plugin sessions.
bot.database.query(sql, params)
MethodExecute a SQL query with optional parameters.
Example
// Create a table for your plugin
await bot.database.query(`
CREATE TABLE IF NOT EXISTS user_scores (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
score INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// Insert or update user score
await bot.database.query(`
INSERT OR REPLACE INTO user_scores (username, score)
VALUES (?, ?)
`, [username, newScore]);
// Query user scores
const topScores = await bot.database.query(`
SELECT username, score
FROM user_scores
ORDER BY score DESC
LIMIT 10
`);
bot.database.prepare(sql)
MethodPrepare a SQL statement for better performance with repeated queries.
Example
// Prepare statements for better performance
const insertScore = bot.database.prepare(`
INSERT OR REPLACE INTO user_scores (username, score) VALUES (?, ?)
`);
const getScore = bot.database.prepare(`
SELECT score FROM user_scores WHERE username = ?
`);
// Use prepared statements
insertScore.run(username, score);
const result = getScore.get(username);
Memory API
Interact with Stream CoHost's intelligent AI memory system.
bot.memory.store(username, content, category)
Async MethodStore a memory for a specific user with categorization.
Parameters
| Name | Type | Description |
|---|---|---|
username |
string | User to store memory for |
content |
string | Memory content |
category |
string | Memory category (preferences, achievements, etc.) |
Example
// Store user achievement
await bot.memory.store(username,
`${username} completed the charades challenge in record time!`,
'achievements'
);
// Store user preference
await bot.memory.store(username,
`${username} prefers playing horror games on Friday nights`,
'preferences'
);
bot.memory.recall(username, category)
Async MethodRetrieve memories for a user, optionally filtered by category.
Example
// Get all memories for a user
const memories = await bot.memory.recall(username);
// Get specific category memories
const achievements = await bot.memory.recall(username, 'achievements');
// Use memories in responses
if (achievements.length > 0) {
return {
success: true,
response: `${username}, I remember when ${achievements[0].content}`
};
}
WebSocket API
Real-time communication with overlays and external applications.
bot.websocket.broadcast(data)
MethodBroadcast data to all connected WebSocket clients.
Example
// Send game state update to overlay
bot.websocket.broadcast({
type: 'game-update',
plugin: 'charades',
data: {
currentWord: 'elephant',
guessesRemaining: 3,
score: 150
}
});
// Send notification to all clients
bot.websocket.broadcast({
type: 'notification',
message: 'New follower: CoolViewer123!',
level: 'success'
});
bot.websocket.sendTo(clientId, data)
MethodSend data to a specific WebSocket client.
OpenAI API
Access OpenAI services with built-in caching and optimization.
bot.ai.generateResponse(prompt, options)
Async MethodGenerate AI responses with context awareness and caching.
Example
// Generate contextual response
const response = await bot.ai.generateResponse(
`Help ${username} with their question: "${userMessage}"`,
{
maxTokens: 100,
temperature: 0.7,
includeMemory: true,
username: username
}
);
// Generate creative content
const storyPrompt = `Write a short fantasy story about ${username}'s adventure`;
const story = await bot.ai.generateResponse(storyPrompt, {
maxTokens: 200,
temperature: 0.9
});
bot.ai.generateImage(prompt, options)
Async MethodGenerate images using DALL-E with automatic storage and caching.
Example
// Generate charades visual clue
const imageUrl = await bot.ai.generateImage(
`A movie scene showing ${clueText}, cinematic style`,
{
size: '512x512',
style: 'vivid',
quality: 'standard'
}
);
// Display in overlay
bot.websocket.broadcast({
type: 'image-update',
plugin: 'charades',
imageUrl: imageUrl,
caption: 'Guess the movie!'
});
Example: Basic Plugin
A complete example of a simple quote plugin with database storage.
class QuotesPlugin {
constructor(bot) {
this.bot = bot;
this.name = 'Quotes';
this.version = '1.0.0';
this.description = 'Store and retrieve memorable quotes from chat';
}
async onLoad() {
console.log(`${this.name} plugin loaded`);
// Create quotes table
await this.initDatabase();
// Register commands
this.bot.registerCommand('!addquote', this.addQuote.bind(this));
this.bot.registerCommand('!quote', this.getQuote.bind(this));
this.bot.registerCommand('!quotes', this.listQuotes.bind(this));
// Set up web routes
this.setupRoutes();
}
async initDatabase() {
await this.bot.database.query(`
CREATE TABLE IF NOT EXISTS quotes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
author TEXT NOT NULL,
added_by TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
}
async addQuote(context) {
const { username, args } = context;
if (args.length < 2) {
return {
success: false,
response: 'Usage: !addquote [author] [quote text]'
};
}
const author = args[0];
const content = args.slice(1).join(' ');
try {
await this.bot.database.query(
'INSERT INTO quotes (content, author, added_by) VALUES (?, ?, ?)',
[content, author, username]
);
// Store memory of this action
await this.bot.memory.store(username,
`${username} added a quote by ${author}: "${content}"`,
'contributions'
);
return {
success: true,
response: `Quote added! "${content}" - ${author}`
};
} catch (error) {
console.error('Error adding quote:', error);
return {
success: false,
response: 'Failed to add quote. Please try again.'
};
}
}
async getQuote(context) {
const { args } = context;
try {
let quote;
if (args.length > 0) {
// Get quote by author
const author = args.join(' ');
const quotes = await this.bot.database.query(
'SELECT * FROM quotes WHERE author LIKE ? ORDER BY RANDOM() LIMIT 1',
[`%${author}%`]
);
quote = quotes[0];
} else {
// Get random quote
const quotes = await this.bot.database.query(
'SELECT * FROM quotes ORDER BY RANDOM() LIMIT 1'
);
quote = quotes[0];
}
if (!quote) {
return {
success: false,
response: args.length > 0
? `No quotes found for "${args.join(' ')}"`
: 'No quotes available yet! Add one with !addquote'
};
}
return {
success: true,
response: `"${quote.content}" - ${quote.author}`
};
} catch (error) {
console.error('Error getting quote:', error);
return {
success: false,
response: 'Failed to retrieve quote. Please try again.'
};
}
}
async listQuotes(context) {
try {
const count = await this.bot.database.query(
'SELECT COUNT(*) as count FROM quotes'
);
const recentQuotes = await this.bot.database.query(
'SELECT author FROM quotes GROUP BY author ORDER BY MAX(created_at) DESC LIMIT 5'
);
const authors = recentQuotes.map(q => q.author).join(', ');
return {
success: true,
response: `We have ${count[0].count} quotes! Recent authors: ${authors}`
};
} catch (error) {
console.error('Error listing quotes:', error);
return {
success: false,
response: 'Failed to list quotes.'
};
}
}
setupRoutes() {
// API endpoint for quotes
this.bot.app.get('/api/quotes', async (req, res) => {
try {
const quotes = await this.bot.database.query(
'SELECT * FROM quotes ORDER BY created_at DESC LIMIT 50'
);
res.json({ quotes });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch quotes' });
}
});
// Admin interface
this.bot.app.get('/quotes', (req, res) => {
res.send(`
Quotes Plugin
Manage your chat quotes
`);
});
}
async onUnload() {
this.bot.unregisterCommand('!addquote');
this.bot.unregisterCommand('!quote');
this.bot.unregisterCommand('!quotes');
console.log(`${this.name} plugin unloaded`);
}
}
module.exports = QuotesPlugin;