# Channels

Channels are the communication methods that connect customers to your agent. TAC integrates with Twilio communication channels — including Voice, SMS, WhatsApp, RCS, and Chat — each with its own transport mechanism but sharing a unified callback interface.

All channels deliver messages to your message-ready callback, so you can handle messages from any channel with the same function. The `ConversationSession` object includes the `channel` field (`"voice"`, `"sms"`, `"whatsapp"`, `"rcs"`, or `"chat"`) so you can customize responses per channel.

## TAC server routes

TAC registers the following endpoints on your server. All paths are defaults and can be customized in your server configuration.

| Endpoint                       | Method    | Channel   | Purpose                                                                         |
| :----------------------------- | :-------- | :-------- | :------------------------------------------------------------------------------ |
| `/twiml`                       | `POST`    | Voice     | Generates TwiML to connect incoming calls to Conversation Relay                 |
| `/ws`                          | WebSocket | Voice     | Streams real-time audio through Conversation Relay                              |
| `/conversation-relay-callback` | `POST`    | Voice     | Handles call state changes and handoff data                                     |
| `/webhook`                     | `POST`    | Messaging | Receives incoming messages from Twilio Conversations (SMS, WhatsApp, RCS, Chat) |
| `/ci-webhook`                  | `POST`    | —         | Receives Conversation Intelligence events (optional)                            |

## Voice channel

The Voice channel handles phone calls using [Conversation Relay](/docs/voice/conversationrelay/onboarding), which manages speech-to-text and text-to-speech over a WebSocket connection. Your agent works with text messages rather than audio streams.

### How Voice works

1. A caller dials your Twilio phone number.
2. Twilio sends a webhook to your server's TwiML endpoint (`/twiml`).
3. TAC responds with TwiML that connects the call to a Conversation Relay WebSocket.
4. Conversation Relay transcribes the caller's speech and sends text to TAC over the WebSocket. TAC processes the message and delivers it to your `on_message_ready` callback.
5. Your agent processes the text and sends a response through the Voice channel.
6. Conversation Relay converts the text response to speech and plays it to the caller.

### Configure the Voice channel

## Python

```python
from tac.channels.voice import VoiceChannel, VoiceChannelConfig

voice_channel = VoiceChannel(
    tac,
    config=VoiceChannelConfig(
        memory_mode="always",
    ),
)
```

## TypeScript

```typescript
import { VoiceChannel } from 'twilio-agent-connect';

const voiceChannel = new VoiceChannel(tac);
```

### Send a Voice response

## Python

```python
async def handle_message_ready(
    message: str,
    context: ConversationSession,
    memory: TACMemoryResponse | None,
) -> str | None:
    llm_response = await generate_response(message)

    await voice_channel.send_response(
        conversation_id=context.conversation_id,
        response=llm_response,
    )
```

## TypeScript

```typescript
tac.onMessageReady(async ({ conversationId, message, channel }) => {
  const llmResponse = await generateResponse(message);

  if (channel === 'voice') {
    await voiceChannel.sendResponse(conversationId, llmResponse);
  }
});
```

### Handle interrupts

When a user interrupts the agent during a Voice conversation (for example, by speaking while the agent is still talking), TAC calls the `on_interrupt` callback. Use this to cancel in-progress LLM generation or other operations.

## Python

```python
async def handle_interrupt(
    context: ConversationSession,
    interrupt_data,
) -> None:
    # Cancel any pending operations for this conversation
    await cancel_pending_operations(context.conversation_id)

tac.on_interrupt(handle_interrupt)
```

## TypeScript

```typescript
tac.onInterrupt(async ({ conversationId, utteranceUntilInterrupt, durationUntilInterruptMs, session }) => {
  // Cancel any pending operations for this conversation
  await cancelPendingOperations(conversationId);
});
```

## SMS channel

The SMS channel handles text-based messaging through Twilio's Conversation Orchestrator webhooks. When a user sends an SMS to your Twilio phone number, Conversation Orchestrator routes the message to your webhook endpoint, and TAC processes it into your `on_message_ready` callback.

### Configure the SMS channel and send responses

## Python

```python
from tac.channels.sms import SMSChannel, SMSChannelConfig

sms_channel = SMSChannel(
    tac,
    config=SMSChannelConfig(
        memory_mode="always",
    ),
)

async def handle_message_ready(
    message: str,
    context: ConversationSession,
    memory: TACMemoryResponse | None,
) -> str | None:
    llm_response = await generate_response(message)

    await sms_channel.send_response(
        conversation_id=context.conversation_id,
        response=llm_response,
    )
```

## TypeScript

```typescript
import { SMSChannel } from 'twilio-agent-connect';

const smsChannel = new SMSChannel(tac);

tac.onMessageReady(async ({ conversationId, message, channel }) => {
  const llmResponse = await generateResponse(message);

  if (channel === 'sms') {
    await smsChannel.sendResponse(conversationId, llmResponse);
  }
});
```

## WhatsApp channel

The WhatsApp channel handles messaging through Twilio's WhatsApp Business API. It uses the same webhook endpoint (`/webhook`) as SMS and follows the same messaging pattern.

### Configure the WhatsApp channel

Set the `TWILIO_WHATSAPP_NUMBER` environment variable to your WhatsApp-enabled number (format: `whatsapp:+1234567890`).

## Python

```python
from tac.channels.whatsapp import WhatsAppChannel, WhatsAppChannelConfig

whatsapp_channel = WhatsAppChannel(
    tac,
    config=WhatsAppChannelConfig(
        memory_mode="always",
    ),
)
```

## TypeScript

```typescript
import { WhatsAppChannel } from 'twilio-agent-connect';

const whatsAppChannel = new WhatsAppChannel(tac);
tac.registerChannel(whatsAppChannel);
```

## RCS channel

The RCS channel handles Rich Communication Services messaging. It uses the same webhook endpoint (`/webhook`) as SMS and WhatsApp.

### Configure the RCS channel

Set the `TWILIO_RCS_SENDER_ID` environment variable to your RCS Sender ID.

## Python

```python
from tac.channels.rcs import RCSChannel, RCSChannelConfig

rcs_channel = RCSChannel(
    tac,
    config=RCSChannelConfig(
        memory_mode="always",
    ),
)
```

## TypeScript

```typescript
import { RCSChannel } from 'twilio-agent-connect';

const rcsChannel = new RCSChannel(tac);
tac.registerChannel(rcsChannel);
```

## Unified callback pattern

All channels deliver messages to the same message-ready callback. Return the response string and TAC automatically routes it to the correct channel:

## Python

```python
async def handle_message_ready(
    message: str,
    context: ConversationSession,
    memory: TACMemoryResponse | None,
) -> str | None:
    llm_response = await generate_response(message)
    return llm_response

tac.on_message_ready(handle_message_ready)
```

## TypeScript

```typescript
tac.onMessageReady(async ({ message, memory }) => {
  const llmResponse = await generateResponse(message);
  return llmResponse;
});
```

If you need per-channel behavior (for example, shorter responses for voice), check `context.channel`:

## Python

```python
async def handle_message_ready(
    message: str,
    context: ConversationSession,
    memory: TACMemoryResponse | None,
) -> str | None:
    if context.channel == "voice":
        return await generate_response(message, max_tokens=100)
    else:
        return await generate_response(message)

tac.on_message_ready(handle_message_ready)
```

## TypeScript

```typescript
tac.onMessageReady(async ({ message, channel }) => {
  if (channel === 'voice') {
    return await generateResponse(message, { maxTokens: 100 });
  }
  return await generateResponse(message);
});
```

## Memory retrieval per channel

Memory retrieval behavior is controlled by the `memory_mode` setting on each channel's config:

* **Messaging channels** (SMS, WhatsApp, RCS): Set `memory_mode` to `"always"` to retrieve memory with each incoming message, or `"once"` to retrieve only on the first message in a conversation.
* **Voice**: Set `memory_mode` to `"always"` in the `VoiceChannelConfig` to retrieve memory for each utterance, or `"once"` for only the first utterance. Default is `"never"`.

You can also retrieve memory on demand from within your callback:

## Python

```python
# Manual memory retrieval
memory = await tac.retrieve_memory(context, query="customer preferences")
```

## TypeScript

```typescript
// Manual memory retrieval
const memory = await tac.retrieveMemory(session, 'customer preferences');
```

## Conversation end

Register a callback to handle conversation cleanup when a session ends:

## Python

```python
async def handle_conversation_ended(context: ConversationSession) -> None:
    # Clean up resources for this conversation
    conversation_history.pop(context.conversation_id, None)

tac.on_conversation_ended(handle_conversation_ended)
```

## TypeScript

```typescript
tac.onConversationEnded(async ({ session }) => {
  // Clean up resources for this conversation
  delete conversationHistory[session.conversationId];
});
```

## Next steps

* [Add TAC to your agent](/docs/conversations/agent-connect/build-with-tac): Configure channels alongside tools, knowledge, and memory.
* [Core concepts](/docs/conversations/agent-connect/core-concepts): Understand TAC's architecture and how channels fit into the system.
* [Escalate to a human agent](/docs/conversations/agent-connect/escalate-to-human-agent): Transfer conversations from your AI agent to a human agent.
