Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/botpress/botpress/llms.txt

Use this file to discover all available pages before exploring further.

What are Plugins?

Plugins are reusable packages that extend bot functionality without being tied to a specific platform. They work across any bot and can integrate with multiple platforms through interfaces. Plugins provide:
  • Actions: Reusable operations (analytics tracking, logging)
  • States: Data schemas (analytics data, configuration)
  • Events: Custom events (metrics recorded, thresholds exceeded)
  • Event Handlers: React to bot events
  • Message Handlers: Process messages
  • Tables: Custom data storage

Plugin Architecture

Creating a Plugin

Plugin Definition

From packages/sdk/src/plugin/definition.ts:162, plugins are defined using PluginDefinition:
import { PluginDefinition, z } from '@botpress/sdk'
import llmInterface from '@botpress/interface-llm'

export default new PluginDefinition({
  name: 'analytics',
  version: '1.0.0',
  title: 'Analytics Plugin',
  description: 'Track and analyze bot interactions',
  icon: 'icon.svg',
  
  // Declare interface dependencies
  interfaces: {
    llm: llmInterface
  },
  
  // Plugin configuration
  configuration: {
    schema: z.object({
      trackingEnabled: z.boolean().default(true),
      sampleRate: z.number().min(0).max(1).default(1.0)
    })
  },
  
  // Plugin-specific states
  states: {
    metrics: {
      type: 'bot',
      schema: z.object({
        totalMessages: z.number(),
        totalTokens: z.number(),
        lastReset: z.string()
      })
    },
    userMetrics: {
      type: 'user',
      schema: z.object({
        messageCount: z.number(),
        firstSeen: z.string(),
        lastSeen: z.string()
      })
    }
  },
  
  // Custom events
  events: {
    metricRecorded: {
      schema: z.object({
        metricName: z.string(),
        value: z.number(),
        timestamp: z.string()
      })
    },
    thresholdExceeded: {
      schema: z.object({
        threshold: z.string(),
        currentValue: z.number(),
        maxValue: z.number()
      })
    }
  },
  
  // Plugin actions
  actions: {
    trackEvent: {
      title: 'Track Event',
      description: 'Record an analytics event',
      input: {
        schema: z.object({
          name: z.string(),
          properties: z.record(z.string(), z.any()).optional()
        })
      },
      output: {
        schema: z.object({
          recorded: z.boolean()
        })
      }
    },
    getMetrics: {
      title: 'Get Metrics',
      description: 'Retrieve analytics metrics',
      input: {
        schema: z.object({
          timeRange: z.enum(['hour', 'day', 'week', 'month'])
        })
      },
      output: {
        schema: z.object({
          totalMessages: z.number(),
          activeUsers: z.number(),
          avgResponseTime: z.number()
        })
      }
    }
  },
  
  // Custom tables for analytics data
  tables: {
    events: {
      schema: z.object({
        eventName: z.string(),
        userId: z.string(),
        timestamp: z.string(),
        properties: z.record(z.string(), z.any())
      }),
      indexes: ['eventName', 'userId', 'timestamp']
    }
  },
  
  // User/conversation/message tags
  user: {
    tags: {
      analyticsId: { title: 'Analytics User ID' }
    }
  },
  conversation: {
    tags: {
      sessionId: { title: 'Analytics Session ID' }
    }
  },
  message: {
    tags: {
      tracked: { title: 'Analytics Tracked' }
    }
  }
})

Plugin Implementation

Implement the plugin’s runtime behavior:
import { PluginImplementation } from '@botpress/sdk'

export default new PluginImplementation({
  // Action implementations
  actions: {
    trackEvent: async ({ input, client, ctx }) => {
      // Store event in table
      await client.createTableRow({
        table: 'events',
        row: {
          eventName: input.name,
          userId: ctx.userId,
          timestamp: new Date().toISOString(),
          properties: input.properties || {}
        }
      })
      
      // Update metrics state
      const { state } = await client.getState({
        type: 'user',
        id: ctx.userId,
        name: 'analytics:userMetrics'
      })
      
      await client.setState({
        type: 'user',
        id: ctx.userId,
        name: 'analytics:userMetrics',
        payload: {
          messageCount: (state.messageCount || 0) + 1,
          lastSeen: new Date().toISOString(),
          firstSeen: state.firstSeen || new Date().toISOString()
        }
      })
      
      return { recorded: true }
    },
    
    getMetrics: async ({ input, client }) => {
      // Query analytics data
      const { rows } = await client.listTableRows({
        table: 'events',
        filter: {
          // Filter by time range
        }
      })
      
      return {
        totalMessages: rows.length,
        activeUsers: new Set(rows.map(r => r.userId)).size,
        avgResponseTime: calculateAverage(rows)
      }
    }
  },
  
  // Message handler
  messageHandler: async ({ message, client, ctx }) => {
    // Track all messages automatically
    await client.callAction({
      type: 'analytics:trackEvent',
      input: {
        name: 'message_received',
        properties: {
          messageType: message.type,
          conversationId: ctx.conversationId
        }
      }
    })
  },
  
  // Event handler
  eventHandler: async ({ event, client }) => {
    // React to custom events
    if (event.type === 'analytics:thresholdExceeded') {
      console.warn('Threshold exceeded:', event.payload)
    }
  }
})

Using Interface Dependencies

Plugins can depend on interfaces to work with any compatible integration:
import { PluginDefinition, z } from '@botpress/sdk'
import llmInterface from '@botpress/interface-llm'

export default new PluginDefinition({
  name: 'sentiment-analysis',
  version: '1.0.0',
  
  // Declare interface dependency
  interfaces: {
    llm: llmInterface
  },
  
  actions: {
    analyzeSentiment: {
      input: {
        // Reference interface entities
        schema: ({ entities }) => z.object({
          text: z.string(),
          model: entities.llm.modelRef
        })
      },
      output: {
        schema: z.object({
          sentiment: z.enum(['positive', 'negative', 'neutral']),
          score: z.number()
        })
      }
    }
  }
})
Implementation using the interface:
export default new PluginImplementation({
  actions: {
    analyzeSentiment: async ({ input, client }) => {
      // Call LLM interface action
      // Works with any LLM integration (Anthropic, OpenAI, etc.)
      const result = await client.callAction({
        type: 'llm:generateContent',
        input: {
          model: input.model,
          messages: [{
            role: 'user',
            content: `Analyze the sentiment of: ${input.text}`
          }]
        }
      })
      
      return {
        sentiment: parseSentiment(result.content),
        score: calculateScore(result.content)
      }
    }
  }
})

Using Integration Dependencies

Plugins can also depend directly on specific integrations:
import anthropic from '@botpress/anthropic'

export default new PluginDefinition({
  name: 'claude-assistant',
  version: '1.0.0',
  
  // Direct integration dependency
  integrations: {
    anthropic: anthropic
  },
  
  actions: {
    askClaude: {
      input: {
        schema: z.object({
          question: z.string()
        })
      },
      output: {
        schema: z.object({
          answer: z.string()
        })
      }
    }
  }
})

Installing Plugins in Bots

From packages/sdk/src/bot/definition.ts:281, add plugins to bots:
import { BotDefinition } from '@botpress/sdk'
import analytics from '@botpress/analytics'
import anthropic from '@botpress/anthropic'
import llmInterface from '@botpress/interface-llm'

const bot = new BotDefinition({})

// First, add integrations that implement required interfaces
bot.addIntegration(anthropic, {
  alias: 'llm',
  configuration: {
    apiKey: process.env.ANTHROPIC_API_KEY
  }
})

// Then add the plugin and wire dependencies
bot.addPlugin(analytics, {
  alias: 'analytics',
  configuration: {
    trackingEnabled: true,
    sampleRate: 1.0
  },
  dependencies: {
    // Map plugin interface to integration
    llm: {
      integrationAlias: 'llm',              // Bot's integration alias
      integrationInterfaceAlias: 'llm'      // Integration's interface alias
    }
  }
})
Plugin actions and states are automatically prefixed with the plugin alias. For example, analytics:trackEvent and analytics:userMetrics.

Plugin State Scoping

Plugin states are namespaced to avoid conflicts:
// In bot code
const { state } = await client.getState({
  type: 'user',
  id: ctx.userId,
  name: 'analytics:userMetrics'  // Prefixed with plugin alias
})
From packages/sdk/src/consts.ts, the separator between plugin alias and name is :.

Plugin Actions

Call plugin actions from bot handlers:
import { BotImplementation } from '@botpress/sdk'

const botImpl = new BotImplementation({
  actions: {},
  plugins: {}
})

botImpl.on.message('text', async ({ message, client, ctx }) => {
  // Call plugin action
  await client.callAction({
    type: 'analytics:trackEvent',
    input: {
      name: 'user_message',
      properties: {
        text: message.payload.text
      }
    }
  })
})

Plugin Event Handlers

Plugins can register event handlers that run automatically:
export default new PluginImplementation({
  messageHandler: async ({ message, client }) => {
    // Runs for every message
    console.log('Message received:', message.type)
  },
  
  eventHandler: async ({ event, client }) => {
    // Runs for every event
    console.log('Event received:', event.type)
  }
})
These handlers run in addition to the bot’s own handlers.

Plugin Hooks

Plugins can register hooks to intercept and modify data:
export default new PluginImplementation({
  beforeMessageHandler: async ({ message }) => {
    // Modify message before processing
    message.tags.processed = 'true'
  },
  
  afterMessageHandler: async ({ message }) => {
    // Execute after message processing
    console.log('Message processed:', message.id)
  }
})

Dereferencing Entities

When using interface entities, you may need to dereference them. From packages/sdk/src/plugin/definition.ts:352:
const plugin = new PluginDefinition({
  // ... definition with interface entities
})

// Dereference entities to concrete schemas
const dereferenced = plugin.dereferenceEntities({
  intersectWithUnknownRecord: true  // Allows additional properties
})
This resolves z.ref() entities to their actual schemas.

Tables in Plugins

Plugins can define custom tables:
new PluginDefinition({
  name: 'crm',
  tables: {
    customers: {
      schema: z.object({
        customerId: z.string(),
        email: z.string().email(),
        name: z.string(),
        tags: z.array(z.string()),
        metadata: z.record(z.string(), z.any())
      }),
      indexes: ['email', 'customerId']
    },
    interactions: {
      schema: z.object({
        customerId: z.string(),
        type: z.enum(['email', 'chat', 'call']),
        timestamp: z.string(),
        notes: z.string()
      }),
      indexes: ['customerId', 'timestamp']
    }
  }
})
Use tables in actions:
export default new PluginImplementation({
  actions: {
    getCustomer: async ({ input, client }) => {
      const { rows } = await client.listTableRows({
        table: 'crm:customers',
        filter: { email: input.email }
      })
      return rows[0]
    }
  }
})

Recurring Events

Plugins can emit scheduled events:
new PluginDefinition({
  name: 'scheduler',
  
  events: {
    dailyReport: {
      schema: z.object({
        date: z.string(),
        metrics: z.record(z.string(), z.number())
      })
    }
  },
  
  recurringEvents: {
    dailyReport: {
      type: 'dailyReport',
      payload: { /* ... */ },
      schedule: {
        cron: '0 9 * * *'  // Every day at 9 AM
      }
    }
  }
})

Workflows (Experimental)

Plugins can define workflows:
new PluginDefinition({
  name: 'onboarding',
  
  workflows: {
    userOnboarding: {
      title: 'User Onboarding',
      input: {
        schema: z.object({
          userId: z.string(),
          plan: z.string()
        })
      },
      output: {
        schema: z.object({
          completed: z.boolean(),
          steps: z.array(z.string())
        })
      }
    }
  }
})

Plugin Merging

From packages/sdk/src/bot/definition.ts:370, when plugins are added to bots:
  1. Plugin states, events, and actions are prefixed with the plugin alias
  2. User/conversation/message tags are merged (no prefixing)
  3. Tables and workflows are merged directly
// Plugin defines: action "track"
// In bot becomes: "analytics:track"

// Plugin defines: user tag "analyticsId"
// In bot becomes: user tag "analyticsId" (no prefix)

Publishing Plugins

1

Develop

Create your plugin:
bp init
# Select "Plugin" template
2

Build

Build the plugin implementation:
npm run build
3

Deploy Private

Deploy to your workspace:
bp deploy
4

Test

Install in test bots and verify functionality
5

Deploy Public

Publish to the Botpress Hub:
bp deploy --visibility public

Best Practices

Use Interfaces

Depend on interfaces instead of specific integrations for maximum reusability.

Namespace Properly

Plugin states and actions are automatically prefixed. Design accordingly.

Document Dependencies

Clearly document which interfaces or integrations your plugin requires.

Handle Errors

Gracefully handle cases where dependencies aren’t available or properly configured.

Examples

Simple Analytics Plugin

export default new PluginDefinition({
  name: 'analytics',
  version: '0.0.1',
  configuration: { schema: z.object({}) },
  actions: {
    track: {
      input: {
        schema: z.object({
          name: z.string(),
          count: z.number()
        })
      },
      output: {
        schema: z.object({})
      }
    }
  }
})

Plugin with Interface Dependency

import llmInterface from '@botpress/interface-llm'

export default new PluginDefinition({
  name: 'summarizer',
  version: '1.0.0',
  interfaces: {
    llm: llmInterface
  },
  actions: {
    summarize: {
      input: {
        schema: ({ entities }) => z.object({
          text: z.string(),
          model: entities.llm.modelRef
        })
      },
      output: {
        schema: z.object({
          summary: z.string()
        })
      }
    }
  }
})

Next Steps

Interfaces

Learn how to use interfaces in plugins

Bots

Install plugins in your bots

Examples

Browse plugin examples

SDK Reference

Explore the complete plugin API