TheDocumentation 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.
IntegrationImplementation class (exported as Integration) implements the runtime behavior of an integration, including webhooks, action handlers, and channel message handlers.
Constructor
import { Integration } from '@botpress/sdk'
import definition from './integration.definition'
export default new Integration({
definition,
register: async ({ ctx, client, logger }) => {
// Registration logic
},
unregister: async ({ ctx, client, logger }) => {
// Cleanup logic
},
handler: async ({ req, ctx, client, logger }) => {
// Webhook handler
},
actions: {
// Action implementations
},
channels: {
// Channel message handlers
}
})
IntegrationImplementationProps
Called when the integration is registered to a bot.
register: async ({ ctx, client, logger, webhookUrl }) => {
logger.forBot().info('Registering integration')
// Configure external platform webhook
await registerWebhook({
url: webhookUrl,
secret: ctx.configuration.webhookSecret
})
}
Called when the integration is removed from a bot.
unregister: async ({ ctx, client, logger }) => {
logger.forBot().info('Unregistering integration')
// Clean up webhooks and resources
await removeWebhook(ctx.configuration.webhookId)
}
Main webhook handler for incoming requests from the external platform.
handler: async ({ req, ctx, client, logger }) => {
// Verify webhook signature
const signature = req.headers['x-webhook-signature']
if (!verifySignature(req.body, signature, ctx.configuration.secret)) {
return {
status: 401,
body: 'Invalid signature'
}
}
const payload = JSON.parse(req.body)
// Handle different webhook events
if (payload.type === 'message') {
await handleIncomingMessage(payload, client, logger)
}
return { status: 200 }
}
Action handler implementations.
actions: {
sendMessage: async ({ input, ctx, client, logger }) => {
const result = await externalAPI.sendMessage({
channel: input.channelId,
text: input.text,
apiKey: ctx.configuration.apiKey
})
return {
messageId: result.id,
timestamp: result.ts
}
}
}
Channel message handlers for outgoing messages.
channels: {
channel: {
messages: {
text: async ({ payload, ctx, client, logger, conversation, ack }) => {
const result = await externalAPI.postMessage({
channel: conversation.tags.channelId,
text: payload.text
})
// Acknowledge message sent
await ack({
tags: {
messageId: result.id
}
})
}
}
}
}
Handler Props
All handlers receive props with these common fields:Integration context with configuration and IDs.
{
botId: string
botUserId: string
integrationId: string
integrationAlias: string
webhookId: string
operation: string
configuration: /* your config schema */
configurationType: string | null
}
Type-safe API client for integration operations.
Logger instance.
logger.forBot().info('Message')
logger.forBot().error('Error', error)
Complete Example
index.ts
import { Integration, RuntimeError } from '@botpress/sdk'
import crypto from 'crypto'
import definition from './integration.definition'
import * as bp from '.botpress'
type SlackAPI = {
postMessage: (args: any) => Promise<any>
updateWebhook: (args: any) => Promise<any>
}
const createSlackClient = (token: string): SlackAPI => {
// Initialize Slack API client
return {
postMessage: async (args) => { /* ... */ },
updateWebhook: async (args) => { /* ... */ }
}
}
export default new Integration({
definition,
register: async ({ ctx, client, logger, webhookUrl }) => {
logger.forBot().info('Registering Slack integration')
const slack = createSlackClient(ctx.configuration.botToken)
try {
// Configure Slack webhook
const result = await slack.updateWebhook({
url: webhookUrl,
events: ['message', 'reaction_added']
})
// Store webhook ID in integration state
await client.setState({
type: 'integration',
name: 'webhook',
id: ctx.integrationId,
payload: {
webhookId: result.id
}
})
logger.forBot().info('Slack integration registered')
} catch (error) {
throw new RuntimeError('Failed to register webhook', error)
}
},
unregister: async ({ ctx, client, logger }) => {
logger.forBot().info('Unregistering Slack integration')
const { state } = await client.getState({
type: 'integration',
name: 'webhook',
id: ctx.integrationId
})
if (state.payload.webhookId) {
// Remove webhook from Slack
const slack = createSlackClient(ctx.configuration.botToken)
await slack.updateWebhook({ id: state.payload.webhookId, enabled: false })
}
},
handler: async ({ req, ctx, client, logger }) => {
// Verify Slack signature
const signature = req.headers['x-slack-signature'] as string
const timestamp = req.headers['x-slack-request-timestamp'] as string
const expectedSignature = crypto
.createHmac('sha256', ctx.configuration.signingSecret)
.update(`v0:${timestamp}:${req.body}`)
.digest('hex')
if (`v0=${expectedSignature}` !== signature) {
return { status: 401, body: 'Invalid signature' }
}
const payload = JSON.parse(req.body)
// Handle URL verification
if (payload.type === 'url_verification') {
return {
status: 200,
body: JSON.stringify({ challenge: payload.challenge })
}
}
// Handle message events
if (payload.event?.type === 'message' && !payload.event.bot_id) {
const { conversation } = await client.getOrCreateConversation({
channel: 'channel',
tags: {
channelId: payload.event.channel
}
})
const { user } = await client.getOrCreateUser({
tags: {
slackUserId: payload.event.user
}
})
await client.createMessage({
type: 'text',
conversationId: conversation.id,
userId: user.id,
payload: {
text: payload.event.text
},
tags: {
messageId: payload.event.ts
}
})
}
return { status: 200 }
},
actions: {
sendMessage: async ({ input, ctx, client, logger, metadata }) => {
logger.forBot().info('Sending Slack message')
const slack = createSlackClient(ctx.configuration.botToken)
const result = await slack.postMessage({
channel: input.channelId,
text: input.text
})
// Track billable action
metadata.setCost(1)
return {
messageId: result.ts,
timestamp: result.ts
}
}
},
channels: {
channel: {
messages: {
text: async ({ payload, ctx, client, logger, conversation, message, user, ack }) => {
logger.forBot().info('Sending text message to Slack')
const slack = createSlackClient(ctx.configuration.botToken)
const result = await slack.postMessage({
channel: conversation.tags.channelId,
text: payload.text,
thread_ts: message.tags.threadId
})
// Acknowledge message delivery
await ack({
tags: {
messageId: result.ts
}
})
},
image: async ({ payload, ctx, client, conversation, ack }) => {
const slack = createSlackClient(ctx.configuration.botToken)
const result = await slack.postMessage({
channel: conversation.tags.channelId,
text: payload.caption || 'Image',
attachments: [{
image_url: payload.imageUrl
}]
})
await ack({
tags: { messageId: result.ts }
})
}
}
}
}
})
See Also
- IntegrationDefinition - Define integration structure
- IntegrationSpecificClient - API client reference
- IntegrationContext - Runtime context