AWS connectors
This guide shows how to connect AWS agents to Twilio communication channels using the twilio-agent-connect-aws package. The package provides connectors that handle channel routing and memory context. This guide covers three connectors: Strands (recommended), Bedrock Agent, and Bedrock AgentCore.
When you're ready to deploy to production, see Deploy to AWS Fargate.
Before you begin, make sure you have:
- TAC SDK installed and configured with your Twilio credentials and a Conversation Configuration (see Add TAC to your agent prerequisites)
- An AWS account with access to Amazon Bedrock
- A Bedrock foundation model activated in your AWS region (for example, Amazon Nova, Anthropic Claude, or Meta Llama)
- AWS credentials configured locally (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION) - Python 3.10+
The twilio-agent-connect-aws package provides connectors for different AWS agent runtimes.
| Connector | Use case | Install extra |
|---|---|---|
StrandsConnector | Build agents with the AWS Strands Agents SDK. Recommended for most use cases. | strands |
BedrockConnector | Use a managed Bedrock Agent with an agent ID and alias. | bedrock |
BedrockAgentCoreConnector | Use Bedrock AgentCore with HTTP and WebSocket runtimes. | agentcore |
The Strands connector is the simplest way to connect a Bedrock-powered agent to Twilio channels.
pip install twilio-agent-connect-aws[strands,server]
The Strands SDK manages Bedrock calls internally, so no additional AWS environment variables are needed beyond your existing TAC credentials. Make sure your AWS credentials are configured through the standard AWS credential chain (environment variables, ~/.aws/credentials, or IAM role).
The StrandsConnector takes an agent factory function that creates a new Strands Agent for each conversation. The connector handles memory context, channel routing, and conversation cleanup.
1from dotenv import load_dotenv2from strands import Agent3from tac import TAC4from tac.core.config import TACConfig5from tac.channels.voice import VoiceChannelConfig6from tac.channels.sms import SMSChannelConfig7from tac.models.session import ConversationSession8from tac.server import TACFastAPIServer910from tac_aws.connectors import StrandsConnector1112load_dotenv()1314tac = TAC(config=TACConfig.from_env())151617def create_agent(context: ConversationSession) -> Agent:18return Agent(19model="amazon.nova-pro-v1:0",20system_prompt="You are a helpful customer service agent. Be concise and friendly.",21)222324connector = StrandsConnector(25tac=tac,26agent_factory=create_agent,27voice_config=VoiceChannelConfig(memory_mode="always"),28sms_config=SMSChannelConfig(memory_mode="always"),29)3031server = TACFastAPIServer(32tac=tac,33voice_channel=connector.voice,34messaging_channels=[connector.sms],35)3637if __name__ == "__main__":38server.start()
The connector creates one agent per conversation and automatically:
- Retrieves memory context from Conversation Memory and includes it in the first message (when
memory_modeis set to"always"or"once") - Routes responses to the correct channel (Voice or SMS)
- Cleans up agent resources when the conversation ends
To disable memory retrieval, omit voice_config and sms_config or set memory_mode to "never".
If you have a managed Bedrock Agent, use the BedrockConnector to connect it to Twilio channels.
pip install twilio-agent-connect-aws[bedrock,server]
Add the following to your .env file alongside your existing TAC credentials.
1BEDROCK_AGENT_ID=your_agent_id2BEDROCK_AGENT_ALIAS_ID=your_agent_alias_id3AWS_REGION=us-east-1
1import boto32from dotenv import load_dotenv3from tac import TAC4from tac.core.config import TACConfig5from tac.server import TACFastAPIServer67from tac_aws.connectors import BedrockConnector89load_dotenv()1011tac = TAC(config=TACConfig.from_env())12client = boto3.client("bedrock-agent-runtime", region_name="us-east-1")1314connector = BedrockConnector(15tac=tac,16bedrock_client=client,17config={18"agentId": "YOUR_AGENT_ID",19"agentAliasId": "YOUR_AGENT_ALIAS_ID",20},21)2223server = TACFastAPIServer(24tac=tac,25voice_channel=connector.voice,26messaging_channels=[connector.sms],27)2829if __name__ == "__main__":30server.start()
The connector automatically passes sessionId and inputText to each invoke_agent call. If Conversation Memory is configured, memory context is prepended to the user's message.
For advanced use cases like dynamic agent selection based on channel, you can provide a custom invoke function instead of a static config.
1from tac.models.session import ConversationSession23def invoke_agent(4context: ConversationSession,5user_message: str,6memory_context: str | None,7):8agent_id = "VOICE_AGENT" if context.channel == "voice" else "SMS_AGENT"910full_message = user_message11if memory_context:12full_message = f"{memory_context}\n\nUser: {user_message}"1314return client.invoke_agent(15agentId=agent_id,16agentAliasId="YOUR_AGENT_ALIAS_ID",17sessionId=context.conversation_id,18inputText=full_message,19)2021connector = BedrockConnector(tac=tac, invoke_fn=invoke_agent)
Bedrock AgentCore supports a dual-runtime pattern: HTTP for SMS and a WebSocket option for low-latency voice streaming.
pip install twilio-agent-connect-aws[agentcore,server]
Add the following to your .env file alongside your existing TAC credentials.
1BEDROCK_AGENTCORE_AGENT_ARN=arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/agent-xxx2AWS_REGION=us-east-1
The BedrockAgentCoreConnector requires an HTTP invoke function. You can optionally add a WebSocket configuration for voice optimization.
1import json2import boto33from dotenv import load_dotenv4from tac import TAC5from tac.core.config import TACConfig6from tac.channels.voice import VoiceChannelConfig7from tac.models.session import ConversationSession8from tac.server import TACFastAPIServer9from tac.session import ThreadSafeSessionManager1011from tac_aws.connectors import BedrockAgentCoreConnector12from tac_aws.connectors.bedrock_agentcore.config import RuntimeConfig, WebSocketConfig1314load_dotenv()1516tac = TAC(config=TACConfig.from_env())17AGENT_ARN = "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/agent-xxx"1819# HTTP invoke function (required for both Voice and SMS)20agentcore_http_client = boto3.client("bedrock-agentcore", region_name="us-east-1")212223def invoke_agent_http(24context: ConversationSession,25user_message: str,26memory_context: str | None,27):28payload_data = {"prompt": user_message}29if memory_context:30payload_data["memory_context"] = memory_context3132return agentcore_http_client.invoke_agent_runtime(33agentRuntimeArn=AGENT_ARN,34runtimeSessionId=context.conversation_id,35payload=json.dumps(payload_data).encode("utf-8"),36)373839connector = BedrockAgentCoreConnector(40tac=tac,41runtime=RuntimeConfig(http=invoke_agent_http),42voice_config=VoiceChannelConfig(43session_manager=ThreadSafeSessionManager(),44memory_mode="always",45),46)4748server = TACFastAPIServer(49tac=tac,50voice_channel=connector.voice,51messaging_channels=[connector.sms],52)5354if __name__ == "__main__":55server.start()
For lower latency on the Voice channel (approximately 50ms vs 200ms), add a WebSocket configuration. The connector uses WebSocket for voice and falls back to HTTP for SMS.
1import websockets2from bedrock_agentcore.runtime import AgentCoreRuntimeClient3from websockets.client import WebSocketClientProtocol45agentcore_client = AgentCoreRuntimeClient(region="us-east-1")678async def create_websocket(context: ConversationSession) -> WebSocketClientProtocol:9ws_url, headers = agentcore_client.generate_ws_connection(10runtime_arn=AGENT_ARN,11session_id=context.conversation_id,12)13return await websockets.connect(ws_url, additional_headers=headers)141516def build_websocket_payload(17context: ConversationSession,18user_message: str,19memory_context: str | None,20) -> dict:21payload = {"type": "prompt", "voicePrompt": user_message}22if memory_context:23payload["memoryContext"] = memory_context24return payload252627connector = BedrockAgentCoreConnector(28tac=tac,29runtime=RuntimeConfig(30http=invoke_agent_http,31websocket=WebSocketConfig(32factory=create_websocket,33payload_fn=build_websocket_payload,34),35),36voice_config=VoiceChannelConfig(37session_manager=ThreadSafeSessionManager(),38memory_mode="always",39),40)
The connector pools WebSocket connections per conversation and automatically cleans them up when conversations end.
To start from a working example instead of writing the server code yourself, clone the twilio-agent-connect-aws repository and run one of the included examples.
-
Clone the repository and install dependencies:
1git clone https://github.com/twilio/twilio-agent-connect-aws.git2cd twilio-agent-connect-aws3make sync4make dev-setup -
Create a
.envfile in the examples directory:cp getting_started/examples/.env.example getting_started/examples/.env -
Fill in your TAC credentials and the AWS variables for the connector you want to run:
StrandsBedrock AgentAgentCoreStrands uses the standard AWS credential chain — no Bedrock agent IDs needed.
AWS_REGION=us-east-1 -
From the repository root, start the example server:
StrandsBedrock AgentAgentCoreuv run getting_started/examples/strands_agents.py -
Expose your local server with ngrok so Twilio can reach it, then configure your Twilio webhooks to point at the ngrok domain. For detailed steps, see Expose your local server with ngrok and Configure Twilio webhooks.
- Start your server and make sure ngrok is running.
- Send an SMS to your Twilio phone number and verify that you receive a response.
- Call your Twilio phone number and verify that the voice channel works.
- If you configured Conversation Memory, verify that responses include personalized context from the customer's profile.
- Deploy to AWS Fargate: Deploy your TAC application to production on AWS.
- Add TAC to your agent: Add custom tools and knowledge search.
- Troubleshooting: Common issues and solutions.