Skip to main content

Overview

The addAction method adds a callback function to a conversation flow without sending a message to the user. It’s useful for performing background tasks, data processing, API calls, or flow control logic.

Method Signature

addAction(
  actionProps: ActionPropertiesGeneric | CallbackFunction<P, B>,
  cb?: CallbackFunction<P, B>,
  nested?: TFlow<P, B> | TFlow<P, B>[] | null
): TFlow<P, B>

Parameters

actionProps
ActionPropertiesGeneric | CallbackFunction
Either configuration options or the callback function directly.When passing options object:When passing a function directly, it becomes the callback.
cb
CallbackFunction<P, B>
The callback function to execute (when first parameter is options object).
nested
TFlow | TFlow[]
Optional child flow(s) to include in the action’s scope.

Return Value

flow
TFlow<P, B>
Returns the flow object for method chaining.

Usage Examples

Simple Action

import { addKeyword } from '@builderbot/bot'

const flow = addKeyword('start')
  .addAnswer('Processing your request...')
  .addAction(async (ctx, { flowDynamic }) => {
    // Perform background task
    const result = await someAsyncOperation()
    await flowDynamic(`Operation completed: ${result}`)
  })

Database Operations

const registerFlow = addKeyword('register')
  .addAnswer(
    'What is your email?',
    { capture: true }
  )
  .addAction(async (ctx, { state, database, flowDynamic }) => {
    // Save to database
    await database.save({
      from: ctx.from,
      email: ctx.body,
      timestamp: Date.now()
    })
    
    await state.update({ registered: true })
    await flowDynamic('Registration successful!')
  })

API Integration

const lookupFlow = addKeyword('lookup')
  .addAnswer('Please enter the order ID:', { capture: true })
  .addAction(async (ctx, { flowDynamic }) => {
    const orderId = ctx.body
    
    try {
      const response = await fetch(`https://api.example.com/orders/${orderId}`)
      const order = await response.json()
      
      await flowDynamic([
        `Order Status: ${order.status}`,
        `Items: ${order.items.length}`,
        `Total: $${order.total}`
      ])
    } catch (error) {
      await flowDynamic('Order not found. Please check the ID.')
    }
  })

Flow Control

const premiumFlow = addKeyword('premium')
  .addAnswer('Premium features...')

const freeFlow = addKeyword('free')
  .addAnswer('Free features...')

const checkSubscription = addKeyword('features')
  .addAction(async (ctx, { gotoFlow, database }) => {
    const user = await database.findOne({ phone: ctx.from })
    
    if (user?.subscription === 'premium') {
      return gotoFlow(premiumFlow)
    }
    return gotoFlow(freeFlow)
  })

With Capture Option

const validateFlow = addKeyword('validate')
  .addAnswer('Please enter your code:')
  .addAction(
    { capture: true },
    async (ctx, { flowDynamic, endFlow }) => {
      const code = ctx.body
      const isValid = await validateCode(code)
      
      if (isValid) {
        await flowDynamic('Code verified successfully!')
      } else {
        await flowDynamic('Invalid code. Please try again.')
        return endFlow()
      }
    }
  )

State Management

const cartFlow = addKeyword('cart')
  .addAnswer('What would you like to add?', { capture: true })
  .addAction(async (ctx, { state, flowDynamic }) => {
    // Get current cart
    const cart = state.get<string[]>('cart') || []
    
    // Add item
    cart.push(ctx.body)
    await state.update({ cart })
    
    await flowDynamic(`Added "${ctx.body}" to cart. Total items: ${cart.length}`)
  })
  .addAnswer('Add another item?', { capture: true })
  .addAction(async (ctx, { state, flowDynamic, endFlow }) => {
    if (ctx.body.toLowerCase() === 'no') {
      const cart = state.get<string[]>('cart')
      await flowDynamic(`Final cart: ${cart.join(', ')}`)
      return endFlow()
    }
  })

Delayed Action

const reminderFlow = addKeyword('remind')
  .addAnswer('Setting up your reminder...')
  .addAction(
    { delay: 5000 }, // Wait 5 seconds
    async (ctx, { flowDynamic }) => {
      await flowDynamic('This is your reminder!')
    }
  )

Blacklist Management

const blockFlow = addKeyword('block')
  .addAnswer('Enter phone number to block:', { capture: true })
  .addAction(async (ctx, { blacklist, flowDynamic }) => {
    const phoneToBlock = ctx.body
    blacklist.add(phoneToBlock)
    await flowDynamic(`Blocked ${phoneToBlock}`)
  })

Key Differences from addAnswer

FeatureaddAnsweraddAction
Sends messageYesNo
Executes callbackOptionalRequired
Use caseRespond to userBackground processing
VisibilityUser sees messageSilent operation

Notes

  • addAction does not send any message by default - use flowDynamic() to send messages from within the callback
  • Perfect for data validation, API calls, database operations, and flow control
  • When capture: true, the action waits for user input before executing
  • Callbacks are async-compatible - always use async/await for asynchronous operations
  • Use endFlow() to stop the conversation, fallBack() to return to previous step
  • State changes persist across the conversation for the same user

Build docs developers (and LLMs) love