Skip to content

Twilio Conversations API: Multi-Channel Chat and Messaging Platform

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Twilio Conversations API: Multi. We cover key concepts, practical examples, and best practices to help you master this topic.

Twilio Conversations API provides a multi-channel conversation platform where participants can join via SMS, WhatsApp, chat, or voice, with unified message history and per-conversation Webhook management.

What You'll Learn

How to create and manage conversations, add participants from different channels (SMS, WhatsApp, in-app chat), send and receive messages, configure conversation Webhooks, manage conversation state (active, inactive, closed), and handle participant events.

Why It Matters

Modern users expect to switch between channels without losing context. Conversations API unifies SMS, WhatsApp, chat, and voice into single threaded conversations. DodaTech uses it for customer support where users can start on WhatsApp and continue via SMS.

Real-World Use

A DodaTech customer starts a support conversation on WhatsApp. Mid-conversation they switch to SMS. The Conversations API maintains the same thread with full history. The support agent sees the unified conversation in one interface.

flowchart LR
    A["Customer\nWhatsApp"] --> B["Conversations\nAPI"]
    C["Customer\nSMS"] --> B
    D["Customer\nIn-App Chat"] --> B
    B --> E["Your App\nWebhook"]
    E --> F["Agent\nUnified View"]
    F --> G["Reply via\nAny Channel"]
    style A fill:#25D366,color:#fff
    style C fill:#dbeafe,stroke:#2563eb
    style B fill:#f22f46,color:#fff
    style F fill:#bbf7d0,stroke:#16a34a

Creating a Conversation

import os
from twilio.rest import Client

client = Client(
    os.environ["TWILIO_ACCOUNT_SID"],
    os.environ["TWILIO_AUTH_TOKEN"]
)

# Create a conversation
def create_conversation(friendly_name, service_sid=None):
    params = {
        "friendly_name": friendly_name
    }
    if service_sid:
        params["chat_service_sid"] = service_sid
    conversation = client.conversations.conversations.create(**params)
    print(f"Conversation SID: {conversation.sid}")
    print(f"Name: {conversation.friendly_name}")
    print(f"State: {conversation.state}")
    print(f"Created: {conversation.date_created}")
    return conversation

conv = create_conversation("DodaTech Support - Order ORD-12345")
# Expected output:
# Conversation SID: CHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Name: DodaTech Support - Order ORD-12345
# State: active
# Created: 2026-06-28 10:00:00

Adding Participants via Different Channels

# Add an SMS participant
def add_sms_participant(conversation_sid, phone_number, messaging_service_sid):
    participant = client.conversations \
        .conversations(conversation_sid) \
        .participants.create(
            messaging_binding_address=phone_number,
            messaging_binding_proxy_address=messaging_service_sid
        )
    print(f"SMS Participant SID: {participant.sid}")
    print(f"Identity: {participant.identity or phone_number}")
    print(f"Type: SMS")
    return participant

# Add a WhatsApp participant
def add_whatsapp_participant(conversation_sid, whatsapp_number):
    participant = client.conversations \
        .conversations(conversation_sid) \
        .participants.create(
            messaging_binding_address=f"whatsapp:{whatsapp_number}",
            messaging_binding_proxy_address="whatsapp:+14155238886"
        )
    print(f"WhatsApp Participant: {participant.sid}")
    return participant

# Add a chat participant (in-app)
def add_chat_participant(conversation_sid, identity):
    participant = client.conversations \
        .conversations(conversation_sid) \
        .participants.create(
            identity=identity
        )
    print(f"Chat Participant: {participant.sid} ({identity})")
    return participant

# Expected output:
# SMS Participant SID: MBxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Identity: +14155551234
# Type: SMS

Sending Messages to a Conversation

# Send a message to the conversation (appears on all channels)
def send_message_to_conversation(conversation_sid, author, body):
    message = client.conversations \
        .conversations(conversation_sid) \
        .messages.create(
            author=author,
            body=body
        )
    print(f"Message SID: {message.sid}")
    print(f"Author: {message.author}")
    print(f"Body: {message.body}")
    print(f"Index: {message.index}")
    return message

msg = send_message_to_conversation(
    conv.sid,
    "DodaTech Support Agent",
    "Hi there! I see your order ORD-12345 is on its way."
)
# Expected output:
# Message SID: IMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Author: DodaTech Support Agent
# Body: Hi there! I see your order ORD-12345 is on its way.
# Index: 1

# Retrieve conversation messages
def get_conversation_messages(conversation_sid, limit=20):
    messages = client.conversations \
        .conversations(conversation_sid) \
        .messages.list(limit=limit)
    print(f"Messages in conversation: {len(messages)}")
    for msg in messages:
        print(f"  [{msg.index}] {msg.author}: {msg.body[:60]}")
    return messages

# get_conversation_messages(conv.sid)
# Expected output:
# Messages in conversation: 2
#   [1] DodaTech Support Agent: Hi there! I see your order ORD-12345...
#   [2] +14155551234: Thanks! When will it arrive?

Managing Conversation State

def update_conversation_state(conversation_sid, new_state):
    """States: active, inactive, closed"""
    valid_states = ["active", "inactive", "closed"]
    if new_state not in valid_states:
        raise ValueError(f"Invalid state: {new_state}")
    conversation = client.conversations \
        .conversations(conversation_sid) \
        .update(state=new_state)
    print(f"Conversation: {conversation.sid}")
    print(f"State changed to: {conversation.state}")
    return conversation

# Close a resolved conversation
update_conversation_state(conv.sid, "closed")
# Expected output:
# Conversation: CHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# State changed to: closed

# Set conversation timers
def set_conversation_timers(conversation_sid, after=86400):
    """Auto-close conversation after N seconds of inactivity."""
    conversation = client.conversations \
        .conversations(conversation_sid) \
        .update(
            timers_inactive=after,
            timers_closed=after * 7  # Close after 7 days inactive
        )
    print(f"Timers set: inactive={after}s, closed={after*7}s")
    return conversation

Common Mistakes

1. Not Configuring the Chat Service

Conversations require a Chat Service instance. Create one in the Console or via API before creating conversations. Without it, conversation creation fails with service SID errors.

2. Using Wrong Proxy Address for Participants

The messaging_binding_proxy_address must be a Messaging Service SID (MG...) or WhatsApp number. Using a regular phone number causes participant creation to fail.

3. Ignoring Participant Identity

In-app chat participants need unique identities (user IDs, emails). SMS/WhatsApp participants use their phone numbers as identity. Duplicate identities cause unexpected behavior.

4. Not Handling Webhook Events

Conversations send webhooks for new messages, participant changes, and state transitions. Without webhooks, your app doesn't know about new customer replies in real time.

5. Mixing Up Message Index and SID

Messages have both an index (sequential number within conversation) and a SID (unique for the message). Use index for display ordering and SID for API operations.

Practice Questions

  1. How is Conversations API different from regular SMS webhooks?
  2. How do participants from different channels join a single conversation?
  3. What states can a conversation have?
  4. How do you add an SMS user to a conversation?

Answers:

  1. SMS webhooks give isolated per-message callbacks. Conversations API unifies messages from SMS, WhatsApp, and chat into one thread with shared history, participants, and state management.
  2. Each participant is added with a channel binding. SMS participants use their phone number with a Messaging Service proxy. WhatsApp participants use WhatsApp numbers. Chat participants use identity strings.
  3. Active (messaging allowed), inactive (no new messages, timed), closed (resolved, no further messages). Conversations auto-transition based on timer settings.
  4. Call participants.create with messaging_binding_address (customer phone) and messaging_binding_proxy_address (your Messaging Service SID). The customer receives an SMS invite to join.

Challenge: Build a multi-channel support conversation system: create a Chat Service, create conversations for each support ticket, add participants via SMS and WhatsApp, receive and display messages from all channels in one view, manage conversation lifecycle with auto-close timers, and implement a webhook handler for real-time message delivery.

FAQ

How many participants can a conversation have?

Up to 100 participants per conversation. This includes agents, customers, and bots across all channels.

Can I add a bot as a participant?

Yes, bots can join conversations as chat participants with their own identity. Implement webhook handlers to process bot responses.

Is message history preserved across channel switches?

Yes, all messages are stored in the conversation. When a participant switches from WhatsApp to SMS, they see the full history because it's the same conversation thread.

How does billing work for Conversations?

You pay per conversation (active per month), per participant (added per month), and per message sent. Pricing starts at $0.005 per conversation per month.

Can I use Conversations for one-way notifications?

Yes, but it's overengineered for one-way. Use Conversations when you need bidirectional, multi-channel, persistent threads with participant management.

Mini Project

Build a multi-channel support system: create a Chat Service, create a conversation for a mock support ticket, add a customer via SMS and an agent via chat, send messages from both channels, retrieve full message history, close the conversation, verify the auto-close timer, and implement webhooks for real-time updates.

What's Next

Twilio Functions — run Serverless backend logic with Twilio Functions.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro