Skip to main content
The Twilio provider integrates BuilderBot with Twilio’s programmable messaging service, enabling SMS and WhatsApp messaging through Twilio’s infrastructure.

Features

  • SMS messaging
  • WhatsApp messaging via Twilio
  • Media messages (MMS)
  • Reliable message delivery
  • Message status tracking
  • Webhook support
  • Multi-channel support
  • Enterprise-grade infrastructure

Prerequisites

1

Create Twilio Account

Sign up at twilio.com
2

Get Phone Number

Purchase a Twilio phone number with SMS/MMS capabilities
3

Get Credentials

From your Twilio Console, obtain:
  • Account SID
  • Auth Token
  • Twilio Phone Number
4

Configure Webhook

Set up webhook URL for incoming messages

Installation

npm install @builderbot/bot @builderbot/provider-twilio

Configuration

Basic Setup

import { createBot, createProvider, createFlow } from '@builderbot/bot'
import { TwilioProvider } from '@builderbot/provider-twilio'
import { MemoryDB } from '@builderbot/bot'

const provider = createProvider(TwilioProvider, {
  accountSid: 'YOUR_ACCOUNT_SID',
  authToken: 'YOUR_AUTH_TOKEN',
  vendorNumber: '+14155238886', // Your Twilio number
  port: 3000
})

const { handleCtx, httpServer } = await createBot({
  flow: adapterFlow,
  provider: provider,
  database: new MemoryDB(),
})

httpServer(3000)

Environment Variables

TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_PHONE_NUMBER=+14155238886
TWILIO_PUBLIC_URL=https://your-domain.com
PORT=3000
const provider = createProvider(TwilioProvider, {
  accountSid: process.env.TWILIO_ACCOUNT_SID,
  authToken: process.env.TWILIO_AUTH_TOKEN,
  vendorNumber: process.env.TWILIO_PHONE_NUMBER,
  publicUrl: process.env.TWILIO_PUBLIC_URL, // For media files
  port: parseInt(process.env.PORT) || 3000
})

Configuration Options

interface TwilioProviderArgs {
  accountSid: string      // Required: Twilio Account SID
  authToken: string       // Required: Twilio Auth Token
  vendorNumber: string    // Required: Your Twilio phone number
  publicUrl?: string      // Public URL for media hosting
  name?: string           // Bot name (default: 'bot')
  port?: number           // Server port (default: 3000)
  writeMyself?: string    // Options: 'none', 'both', 'host'
}

Basic Usage

Sending Text Messages

import { addKeyword } from '@builderbot/bot'

const welcomeFlow = addKeyword(['hi', 'hello'])
  .addAnswer('Hello! Welcome to our service')
  .addAnswer('How can we help you today?')

Sending Media (MMS)

const mediaFlow = addKeyword('media')
  // Send image from public URL
  .addAnswer('Here is an image:', {
    media: 'https://example.com/image.jpg'
  })
  // Send video
  .addAnswer('Check this video:', {
    media: 'https://example.com/video.mp4'
  })
For local files, you need to provide a public URL. The provider can help expose local files via the /tmp endpoint, but this requires proper configuration of publicUrl.

Media Handling

Public URL Requirement

Twilio requires media to be accessible via public HTTPS URLs:
const provider = createProvider(TwilioProvider, {
  accountSid: process.env.TWILIO_ACCOUNT_SID,
  authToken: process.env.TWILIO_AUTH_TOKEN,
  vendorNumber: process.env.TWILIO_PHONE_NUMBER,
  publicUrl: 'https://your-domain.com' // Required for local files
})

Sending Local Files

When sending local files, the provider automatically creates a temporary public endpoint:
const localFileFlow = addKeyword('file')
  .addAnswer('Sending local file...', {
    media: './assets/document.pdf'
  })
  // Provider converts to: https://your-domain.com/tmp?path=encrypted_path

Direct Media Method

const sendMediaFlow = addKeyword('image')
  .addAction(async (ctx, { provider }) => {
    await provider.sendMedia(
      ctx.from,
      'Check out this image!',
      'https://example.com/image.jpg'
    )
  })

Saving Received Media

import { join } from 'path'

const receiveMediaFlow = addKeyword('MEDIA')
  .addAction(async (ctx, { provider, flowDynamic }) => {
    const filePath = await provider.saveFile(ctx, {
      path: join(process.cwd(), 'downloads')
    })
    
    await flowDynamic(`File saved: ${filePath}`)
  })

Advanced Features

Direct Twilio API Access

You can access the Twilio client directly:
const advancedFlow = addKeyword('advanced')
  .addAction(async (ctx, { provider }) => {
    await provider.send(
      ctx.from,
      'Custom message',
      {
        statusCallback: 'https://your-domain.com/status',
        provideFeedback: true
      }
    )
  })

Message Options

const optionsFlow = addKeyword('options')
  .addAction(async (ctx, { provider }) => {
    await provider.send(
      ctx.from,
      'Message with options',
      {
        statusCallback: 'https://your-domain.com/callback',
        maxPrice: '0.05',
        validityPeriod: 600, // 10 minutes
        forceDelivery: true
      }
    )
  })

WhatsApp via Twilio

To use WhatsApp with Twilio:
const provider = createProvider(TwilioProvider, {
  accountSid: process.env.TWILIO_ACCOUNT_SID,
  authToken: process.env.TWILIO_AUTH_TOKEN,
  vendorNumber: 'whatsapp:+14155238886', // Twilio WhatsApp number
  publicUrl: process.env.TWILIO_PUBLIC_URL
})
Send messages to WhatsApp numbers:
await bot.sendMessage(
  'whatsapp:+1234567890',
  'Hello from WhatsApp!'
)

Webhook Configuration

Webhook Endpoints

The provider exposes these endpoints:
POST /        - Home endpoint
POST /webhook - Receive incoming messages
GET  /tmp     - Serve local media files

Twilio Console Setup

  1. Go to your Twilio phone number settings
  2. Set “A MESSAGE COMES IN” webhook:
    https://your-domain.com/webhook
    HTTP POST
    
  3. Save configuration

Ngrok for Development

For local development, use ngrok:
ngrok http 3000
Then set the ngrok URL in Twilio and your config:
const provider = createProvider(TwilioProvider, {
  accountSid: process.env.TWILIO_ACCOUNT_SID,
  authToken: process.env.TWILIO_AUTH_TOKEN,
  vendorNumber: process.env.TWILIO_PHONE_NUMBER,
  publicUrl: 'https://your-subdomain.ngrok.io'
})

Event Handling

Ready Event

provider.on('ready', () => {
  console.log('Twilio provider is ready!')
})

Host Information

provider.on('host', (host) => {
  console.log('Connected with number:', host.phone)
})

Error Handling

provider.on('auth_failure', (error) => {
  console.error('Twilio auth failed:', error)
})

Phone Number Formatting

The provider automatically formats phone numbers:
// All these formats work:
await bot.sendMessage('+1234567890', 'Hello')
await bot.sendMessage('1234567890', 'Hello')
await bot.sendMessage('whatsapp:+1234567890', 'Hello')

Best Practices

  • Always use HTTPS URLs for media
  • Host media on reliable CDN
  • Set proper publicUrl for local file serving
  • Validate media URLs before sending
  • Implement status callbacks for delivery tracking
  • Handle failed messages gracefully
  • Set appropriate validity periods
  • Monitor Twilio console for delivery issues
  • Monitor message usage in Twilio console
  • Set maxPrice to prevent unexpected costs
  • Use WhatsApp when possible (lower cost)
  • Implement message queuing for bulk sends
  • Never commit credentials to version control
  • Use environment variables for all secrets
  • Validate webhook requests from Twilio
  • Use HTTPS in production

Troubleshooting

  • Verify Account SID and Auth Token are correct
  • Check credentials haven’t expired
  • Ensure account is active and funded
  • Verify URL is publicly accessible (HTTPS)
  • Check publicUrl is configured correctly
  • Test media URL in browser
  • Review Twilio error logs
  • Verify webhook URL is publicly accessible
  • Check URL is configured in Twilio console
  • Test with ngrok for local development
  • Review Twilio webhook debugger
If you see a warning about local files, ensure:
  • publicUrl is set in configuration
  • Your server is publicly accessible
  • Port is open in firewall
  • HTTPS is configured (production)

Example: Complete Bot

import { createBot, createProvider, createFlow, addKeyword } from '@builderbot/bot'
import { TwilioProvider } from '@builderbot/provider-twilio'
import { MemoryDB } from '@builderbot/bot'

const welcomeFlow = addKeyword(['hi', 'hello', 'start'])
  .addAnswer('👋 Hello! Welcome to our service')
  .addAnswer(
    'How can we help you?',
    { capture: true },
    async (ctx, { flowDynamic }) => {
      await flowDynamic(`You said: ${ctx.body}`)
    }
  )

const mediaFlow = addKeyword('image')
  .addAnswer('Here is an image:', {
    media: 'https://example.com/image.jpg'
  })

const main = async () => {
  const adapterFlow = createFlow([welcomeFlow, mediaFlow])
  
  const adapterProvider = createProvider(TwilioProvider, {
    accountSid: process.env.TWILIO_ACCOUNT_SID,
    authToken: process.env.TWILIO_AUTH_TOKEN,
    vendorNumber: process.env.TWILIO_PHONE_NUMBER,
    publicUrl: process.env.TWILIO_PUBLIC_URL
  })
  
  const adapterDB = new MemoryDB()

  const { handleCtx, httpServer } = await createBot({
    flow: adapterFlow,
    provider: adapterProvider,
    database: adapterDB,
  })

  httpServer(3000)
}

main()

Pricing Considerations

  • SMS: Varies by country (~$0.0075/message in US)
  • WhatsApp: Lower cost than SMS in most regions
  • MMS: Higher cost than SMS (~$0.02/message)
  • Check Twilio Pricing for details

Further Resources

Build docs developers (and LLMs) love