Twilio Communications Builder
Twilio communications

Build communication features with Twilio: SMS messaging, voice calls, WhatsApp Business API, and user verification (2FA). Covers the full spectrum from simple notifications to complex IVR systems and multi-channel authentication.
--- name: twilio-communications description: "Build communication features with Twilio: SMS messaging, voice calls, WhatsApp Business API, and user verification (2FA). Covers the full spectrum from simple notifications to complex IVR systems and multi-channel authentication. Critical focus on compliance, rate limits, and error handling. Use when: twilio, send SMS, text message, voice call, phone verification." source: vibeship-spawner-skills (Apache 2.0) --- # Twilio Communications ## Patterns ### SMS Sending Pattern Basic pattern for sending SMS messages with Twilio. Handles the fundamentals: phone number formatting, message delivery, and delivery status callbacks. Key considerations: - Phone numbers must be in E.164 format (+1234567890)
Overview
Basic pattern for sending SMS messages with Twilio. Handles the fundamentals: phone number formatting, message delivery, and delivery status callbacks.
Manual install (npx, ZIP)
npx skills add davila7/claude-code-templates --skill twilio-communications -g -a claude-codenpx degit davila7/claude-code-templates/cli-tool/components/skills/enterprise-communication/twilio-communications#main ~/.claude/skills/twilio-communicationsConnects with Agent Alley MCP · agentalley.io/mcp
Files included
SKILL.md8.8 KB
Read SKILL.md here
Show the full text296 lines
Twilio Communications
Patterns
SMS Sending Pattern
Basic pattern for sending SMS messages with Twilio. Handles the fundamentals: phone number formatting, message delivery, and delivery status callbacks.
Key considerations:
- Phone numbers must be in E.164 format (+1234567890)
- Default rate limit: 80 messages per second (MPS)
- Messages over 160 characters are split (and cost more)
- Carrier filtering can block messages (especially to US numbers)
When to use: ['Sending notifications to users', 'Transactional messages (order confirmations, shipping)', 'Alerts and reminders']
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
import os
import re
class TwilioSMS:
"""
SMS sending with proper error handling and validation.
"""
def __init__(self):
self.client = Client(
os.environ["TWILIO_ACCOUNT_SID"],
os.environ["TWILIO_AUTH_TOKEN"]
)
self.from_number = os.environ["TWILIO_PHONE_NUMBER"]
def validate_e164(self, phone: str) -> bool:
"""Validate phone number is in E.164 format."""
pattern = r'^\+[1-9]\d{1,14}$'
return bool(re.match(pattern, phone))
def send_sms(
self,
to: str,
body: str,
status_callback: str = None
) -> dict:
"""
Send an SMS message.
Args:
to: Recipient phone number in E.164 format
body: Message text (160 chars = 1 segment)
status_callback: URL for delivery status webhooks
Returns:
Message SID and status
"""
# Validate phone number format
if not self.validate_e164(to):
return {
"success": False,
"error": "Phone number must be in E.164 format (+1234567890)"
}
# Check message length (warn about segmentation)
segment_count = (len(body) + 159) // 160
if segment_count > 1:
print(f"Warning: Message will be sent as {segment_count} segments")
try:
message = self.client.messages.create(
to=to,
from_=self.from_number,
body=body,
status_callback=status_callback
)
return {
"success": True,
"message_sid": message.sid,
"status": message.status,
"segments": segment_count
}
except TwilioRestException as e:
return self._handle_error(e)
def _handle_error(self, error: Twilio
Twilio Verify Pattern (2FA/OTP)
Use Twilio Verify for phone number verification and 2FA. Handles code generation, delivery, rate limiting, and fraud prevention.
Key benefits over DIY OTP:
- Twilio manages code generation and expiration
- Built-in fraud prevention (saved customers $82M+ blocking 747M attempts)
- Handles rate limiting automatically
- Multi-channel: SMS, Voice, Email, Push, WhatsApp
Google found SMS 2FA blocks "100% of automated bots, 96% of bulk phishing attacks, and 76% of targeted attacks."
When to use: ['User phone number verification at signup', 'Two-factor authentication (2FA)', 'Password reset verification', 'High-value transaction confirmation']
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
import os
from enum import Enum
from typing import Optional
class VerifyChannel(Enum):
SMS = "sms"
CALL = "call"
EMAIL = "email"
WHATSAPP = "whatsapp"
class TwilioVerify:
"""
Phone verification with Twilio Verify.
Never store OTP codes - Twilio handles it.
"""
def __init__(self, verify_service_sid: str = None):
self.client = Client(
os.environ["TWILIO_ACCOUNT_SID"],
os.environ["TWILIO_AUTH_TOKEN"]
)
# Create a Verify Service in Twilio Console first
self.service_sid = verify_service_sid or os.environ["TWILIO_VERIFY_SID"]
def send_verification(
self,
to: str,
channel: VerifyChannel = VerifyChannel.SMS,
locale: str = "en"
) -> dict:
"""
Send verification code to phone/email.
Args:
to: Phone number (E.164) or email
channel: SMS, call, email, or whatsapp
locale: Language code for message
Returns:
Verification status
"""
try:
verification = self.client.verify \
.v2 \
.services(self.service_sid) \
.verifications \
.create(
to=to,
channel=channel.value,
locale=locale
)
return {
"success": True,
"status": verification.status, # "pending"
"channel": channel.value,
"valid": verification.valid
}
except TwilioRestException as e:
return self._handle_verify_error(e)
def check_verification(self, to: str, code: str) -> dict:
"""
Check if verification code is correct.
Args:
to: Phone number or email that received code
code: The code entered by user
R
TwiML IVR Pattern
Build Interactive Voice Response (IVR) systems using TwiML. TwiML (Twilio Markup Language) is XML that tells Twilio what to do when receiving calls.
Core TwiML verbs:
- <Say>: Text-to-speech
- <Play>: Play audio file
- <Gather>: Collect keypad/speech input
- <Dial>: Connect to another number
- <Record>: Record caller's voice
- <Redirect>: Move to another TwiML endpoint
Key insight: Twilio makes HTTP request to your webhook, you return TwiML, Twilio executes it. Stateless, so use URL params or sessions.
When to use: ['Phone menu systems (press 1 for sales...)', 'Automated customer support', 'Appointment reminders with confirmation', 'Voicemail systems']
from flask import Flask, request, Response
from twilio.twiml.voice_response import VoiceResponse, Gather
from twilio.request_validator import RequestValidator
import os
app = Flask(__name__)
def validate_twilio_request(f):
"""Decorator to validate requests are from Twilio."""
def wrapper(*args, **kwargs):
validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"])
# Get request details
url = request.url
params = request.form.to_dict()
signature = request.headers.get("X-Twilio-Signature", "")
if not validator.validate(url, params, signature):
return "Invalid request", 403
return f(*args, **kwargs)
wrapper.__name__ = f.__name__
return wrapper
@app.route("/voice/incoming", methods=["POST"])
@validate_twilio_request
def incoming_call():
"""Handle incoming call with IVR menu."""
response = VoiceResponse()
# Gather digits with timeout
gather = Gather(
num_digits=1,
action="/voice/menu-selection",
method="POST",
timeout=5
)
gather.say(
"Welcome to Acme Corp. "
"Press 1 for sales. "
"Press 2 for support. "
"Press 3 to leave a message."
)
response.append(gather)
# If no input, repeat
response.redirect("/voice/incoming")
return Response(str(response), mimetype="text/xml")
@app.route("/voice/menu-selection", methods=["POST"])
@validate_twilio_request
def menu_selection():
"""Route based on menu selection."""
response = VoiceResponse()
digit = request.form.get("Digits", "")
if digit == "1":
# Transfer to sales
response.say("Connecting you to sales.")
response.dial(os.environ["SALES_PHONE"])
elif digit == "2":
# Transfer to support
response.say("Connecting you to support.")
response.dial(os.environ["SUPPORT_PHONE"])
elif digit == "3":
# Voicemail
response.say("Please leave a message after
⚠️ Sharp Edges
| Issue | Severity | Solution |
|---|---|---|
| Issue | high | ## Track opt-out status in your database |
| Issue | medium | ## Implement retry logic for transient failures |
| Issue | high | ## Register for A2P 10DLC (US requirement) |
| Issue | critical | ## ALWAYS validate the signature |
| Issue | high | ## Track session windows per user |
| Issue | critical | ## Never hardcode credentials |
| Issue | medium | ## Implement application-level rate limiting too |
| 1 | |
| 2 | name twilio-communications |
| 3 | description "Build communication features with Twilio: SMS messaging, voice calls, WhatsApp Business API, and user verification (2FA). Covers the full spectrum from simple notifications to complex IVR systems and multi-channel authentication. Critical focus on compliance, rate limits, and error handling. Use when: twilio, send SMS, text message, voice call, phone verification." |
| 4 | source vibeship-spawner-skills (Apache 2.0) |
| 5 | |
| 6 | |
| 7 | # Twilio Communications |
| 8 | |
| 9 | ## Patterns |
| 10 | |
| 11 | ### SMS Sending Pattern |
| 12 | |
| 13 | Basic pattern for sending SMS messages with Twilio. |
| 14 | Handles the fundamentals: phone number formatting, message delivery, |
| 15 | and delivery status callbacks. |
| 16 | |
| 17 | Key considerations: |
| 18 | Phone numbers must be in E.164 format (+1234567890) |
| 19 | Default rate limit: 80 messages per second (MPS) |
| 20 | Messages over 160 characters are split (and cost more) |
| 21 | Carrier filtering can block messages (especially to US numbers) |
| 22 | |
| 23 | |
| 24 | **When to use**: ['Sending notifications to users', 'Transactional messages (order confirmations, shipping)', 'Alerts and reminders'] |
| 25 | |
| 26 | |
| 27 | from twilio.rest import Client |
| 28 | from twilio.base.exceptions import TwilioRestException |
| 29 | import os |
| 30 | import re |
| 31 | |
| 32 | class TwilioSMS: |
| 33 | """ |
| 34 | SMS sending with proper error handling and validation. |
| 35 | """ |
| 36 | |
| 37 | def __init__(self): |
| 38 | self.client = Client( |
| 39 | os.environ["TWILIO_ACCOUNT_SID"], |
| 40 | os.environ["TWILIO_AUTH_TOKEN"] |
| 41 | ) |
| 42 | self.from_number = os.environ["TWILIO_PHONE_NUMBER"] |
| 43 | |
| 44 | def validate_e164(self, phone: str) -> bool: |
| 45 | """Validate phone number is in E.164 format.""" |
| 46 | pattern = r'^\+[1-9]\d{1,14}$' |
| 47 | return bool(re.match(pattern, phone)) |
| 48 | |
| 49 | def send_sms( |
| 50 | self, |
| 51 | to: str, |
| 52 | body: str, |
| 53 | status_callback: str = None |
| 54 | ) -> dict: |
| 55 | """ |
| 56 | Send an SMS message. |
| 57 | |
| 58 | Args: |
| 59 | to: Recipient phone number in E.164 format |
| 60 | body: Message text (160 chars = 1 segment) |
| 61 | status_callback: URL for delivery status webhooks |
| 62 | |
| 63 | Returns: |
| 64 | Message SID and status |
| 65 | """ |
| 66 | # Validate phone number format |
| 67 | if not self.validate_e164(to): |
| 68 | return { |
| 69 | "success": False, |
| 70 | "error": "Phone number must be in E.164 format (+1234567890)" |
| 71 | } |
| 72 | |
| 73 | # Check message length (warn about segmentation) |
| 74 | segment_count = (len(body) + 159) // 160 |
| 75 | if segment_count > 1: |
| 76 | print(f"Warning: Message will be sent as {segment_count} segments") |
| 77 | |
| 78 | try: |
| 79 | message = self.client.messages.create( |
| 80 | to=to, |
| 81 | from_=self.from_number, |
| 82 | body=body, |
| 83 | status_callback=status_callback |
| 84 | ) |
| 85 | |
| 86 | return { |
| 87 | "success": True, |
| 88 | "message_sid": message.sid, |
| 89 | "status": message.status, |
| 90 | "segments": segment_count |
| 91 | } |
| 92 | |
| 93 | except TwilioRestException as e: |
| 94 | return self._handle_error(e) |
| 95 | |
| 96 | def _handle_error(self, error: Twilio |
| 97 | |
| 98 | |
| 99 | ### Twilio Verify Pattern (2FA/OTP) |
| 100 | |
| 101 | Use Twilio Verify for phone number verification and 2FA. |
| 102 | Handles code generation, delivery, rate limiting, and fraud prevention. |
| 103 | |
| 104 | Key benefits over DIY OTP: |
| 105 | Twilio manages code generation and expiration |
| 106 | Built-in fraud prevention (saved customers $82M+ blocking 747M attempts) |
| 107 | Handles rate limiting automatically |
| 108 | Multi-channel: SMS, Voice, Email, Push, WhatsApp |
| 109 | |
| 110 | Google found SMS 2FA blocks "100% of automated bots, 96% of bulk |
| 111 | phishing attacks, and 76% of targeted attacks." |
| 112 | |
| 113 | |
| 114 | **When to use**: ['User phone number verification at signup', 'Two-factor authentication (2FA)', 'Password reset verification', 'High-value transaction confirmation'] |
| 115 | |
| 116 | |
| 117 | from twilio.rest import Client |
| 118 | from twilio.base.exceptions import TwilioRestException |
| 119 | import os |
| 120 | from enum import Enum |
| 121 | from typing import Optional |
| 122 | |
| 123 | class VerifyChannel(Enum): |
| 124 | SMS = "sms" |
| 125 | CALL = "call" |
| 126 | EMAIL = "email" |
| 127 | WHATSAPP = "whatsapp" |
| 128 | |
| 129 | class TwilioVerify: |
| 130 | """ |
| 131 | Phone verification with Twilio Verify. |
| 132 | Never store OTP codes - Twilio handles it. |
| 133 | """ |
| 134 | |
| 135 | def __init__(self, verify_service_sid: str = None): |
| 136 | self.client = Client( |
| 137 | os.environ["TWILIO_ACCOUNT_SID"], |
| 138 | os.environ["TWILIO_AUTH_TOKEN"] |
| 139 | ) |
| 140 | # Create a Verify Service in Twilio Console first |
| 141 | self.service_sid = verify_service_sid or os.environ["TWILIO_VERIFY_SID"] |
| 142 | |
| 143 | def send_verification( |
| 144 | self, |
| 145 | to: str, |
| 146 | channel: VerifyChannel = VerifyChannel.SMS, |
| 147 | locale: str = "en" |
| 148 | ) -> dict: |
| 149 | """ |
| 150 | Send verification code to phone/email. |
| 151 | |
| 152 | Args: |
| 153 | to: Phone number (E.164) or email |
| 154 | channel: SMS, call, email, or whatsapp |
| 155 | locale: Language code for message |
| 156 | |
| 157 | Returns: |
| 158 | Verification status |
| 159 | """ |
| 160 | try: |
| 161 | verification = self.client.verify \ |
| 162 | .v2 \ |
| 163 | .services(self.service_sid) \ |
| 164 | .verifications \ |
| 165 | .create( |
| 166 | to=to, |
| 167 | channel=channel.value, |
| 168 | locale=locale |
| 169 | ) |
| 170 | |
| 171 | return { |
| 172 | "success": True, |
| 173 | "status": verification.status, # "pending" |
| 174 | "channel": channel.value, |
| 175 | "valid": verification.valid |
| 176 | } |
| 177 | |
| 178 | except TwilioRestException as e: |
| 179 | return self._handle_verify_error(e) |
| 180 | |
| 181 | def check_verification(self, to: str, code: str) -> dict: |
| 182 | """ |
| 183 | Check if verification code is correct. |
| 184 | |
| 185 | Args: |
| 186 | to: Phone number or email that received code |
| 187 | code: The code entered by user |
| 188 | |
| 189 | R |
| 190 | |
| 191 | |
| 192 | ### TwiML IVR Pattern |
| 193 | |
| 194 | Build Interactive Voice Response (IVR) systems using TwiML. |
| 195 | TwiML (Twilio Markup Language) is XML that tells Twilio what to do |
| 196 | when receiving calls. |
| 197 | |
| 198 | Core TwiML verbs: |
| 199 | <Say>: Text-to-speech |
| 200 | <Play>: Play audio file |
| 201 | <Gather>: Collect keypad/speech input |
| 202 | <Dial>: Connect to another number |
| 203 | <Record>: Record caller's voice |
| 204 | <Redirect>: Move to another TwiML endpoint |
| 205 | |
| 206 | Key insight: Twilio makes HTTP request to your webhook, you return |
| 207 | TwiML, Twilio executes it. Stateless, so use URL params or sessions. |
| 208 | |
| 209 | |
| 210 | **When to use**: ['Phone menu systems (press 1 for sales...)', 'Automated customer support', 'Appointment reminders with confirmation', 'Voicemail systems'] |
| 211 | |
| 212 | |
| 213 | from flask import Flask, request, Response |
| 214 | from twilio.twiml.voice_response import VoiceResponse, Gather |
| 215 | from twilio.request_validator import RequestValidator |
| 216 | import os |
| 217 | |
| 218 | app = Flask(__name__) |
| 219 | |
| 220 | def validate_twilio_request(f): |
| 221 | """Decorator to validate requests are from Twilio.""" |
| 222 | def wrapper(*args, **kwargs): |
| 223 | validator = RequestValidator(os.environ["TWILIO_AUTH_TOKEN"]) |
| 224 | |
| 225 | # Get request details |
| 226 | url = request.url |
| 227 | params = request.form.to_dict() |
| 228 | signature = request.headers.get("X-Twilio-Signature", "") |
| 229 | |
| 230 | if not validator.validate(url, params, signature): |
| 231 | return "Invalid request", 403 |
| 232 | |
| 233 | return f(*args, **kwargs) |
| 234 | wrapper.__name__ = f.__name__ |
| 235 | return wrapper |
| 236 | |
| 237 | @app.route("/voice/incoming", methods=["POST"]) |
| 238 | @validate_twilio_request |
| 239 | def incoming_call(): |
| 240 | """Handle incoming call with IVR menu.""" |
| 241 | response = VoiceResponse() |
| 242 | |
| 243 | # Gather digits with timeout |
| 244 | gather = Gather( |
| 245 | num_digits=1, |
| 246 | action="/voice/menu-selection", |
| 247 | method="POST", |
| 248 | timeout=5 |
| 249 | ) |
| 250 | gather.say( |
| 251 | "Welcome to Acme Corp. " |
| 252 | "Press 1 for sales. " |
| 253 | "Press 2 for support. " |
| 254 | "Press 3 to leave a message." |
| 255 | ) |
| 256 | response.append(gather) |
| 257 | |
| 258 | # If no input, repeat |
| 259 | response.redirect("/voice/incoming") |
| 260 | |
| 261 | return Response(str(response), mimetype="text/xml") |
| 262 | |
| 263 | @app.route("/voice/menu-selection", methods=["POST"]) |
| 264 | @validate_twilio_request |
| 265 | def menu_selection(): |
| 266 | """Route based on menu selection.""" |
| 267 | response = VoiceResponse() |
| 268 | digit = request.form.get("Digits", "") |
| 269 | |
| 270 | if digit == "1": |
| 271 | # Transfer to sales |
| 272 | response.say("Connecting you to sales.") |
| 273 | response.dial(os.environ["SALES_PHONE"]) |
| 274 | |
| 275 | elif digit == "2": |
| 276 | # Transfer to support |
| 277 | response.say("Connecting you to support.") |
| 278 | response.dial(os.environ["SUPPORT_PHONE"]) |
| 279 | |
| 280 | elif digit == "3": |
| 281 | # Voicemail |
| 282 | response.say("Please leave a message after |
| 283 | |
| 284 | |
| 285 | ## ⚠️ Sharp Edges |
| 286 | |
| 287 | | Issue | Severity | Solution | |
| 288 | |-------|----------|----------| |
| 289 | | Issue | high | ## Track opt-out status in your database | |
| 290 | | Issue | medium | ## Implement retry logic for transient failures | |
| 291 | | Issue | high | ## Register for A2P 10DLC (US requirement) | |
| 292 | | Issue | critical | ## ALWAYS validate the signature | |
| 293 | | Issue | high | ## Track session windows per user | |
| 294 | | Issue | critical | ## Never hardcode credentials | |
| 295 | | Issue | medium | ## Implement application-level rate limiting too | |
| 296 |
Details
- License
- MIT
- Stars on the repo
- ★ 32,299
- Checked
- Source
- davila7/claude-code-templates ↗
- Creator
- davila7 · 212 listings
Similar skills
See all →
SkillAPI Interface Design
AgentVersion-Specific Docs Lookup
SkillAdaptyv Bio Foundry API
Skill.NET Backend Developer
SkillGemini Agent API Guide
SkillGemini Notebook AutomationMore from davila7
Skill2D Game Development
Skill3D Game Development Principles
Skill3D Web Experience Builder
SkillSplit Test Setup
AgentResearch Literature Synthesizer
AgentAcademic Research Specialist