Twilio communications skill

Build communication features with Twilio: SMS messaging, voice calls, WhatsApp Business API, and user verification (2FA).

by davila7·MIT license·★ 32,299 Stars on the repo·GitHub ↗

Use now

Files of Twilio communications

davila7/main1 file shown
SKILL.md
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---
2name: twilio-communications
3description: "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."
4source: vibeship-spawner-skills (Apache 2.0)
5---
6 
7# Twilio Communications
8 
9## Patterns
10 
11### SMS Sending Pattern
12 
13Basic pattern for sending SMS messages with Twilio.
14Handles the fundamentals: phone number formatting, message delivery,
15and delivery status callbacks.
16 
17Key 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```python
27from twilio.rest import Client
28from twilio.base.exceptions import TwilioRestException
29import os
30import re
31 
32class 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 
101Use Twilio Verify for phone number verification and 2FA.
102Handles code generation, delivery, rate limiting, and fraud prevention.
103 
104Key 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 
110Google found SMS 2FA blocks "100% of automated bots, 96% of bulk
111phishing 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```python
117from twilio.rest import Client
118from twilio.base.exceptions import TwilioRestException
119import os
120from enum import Enum
121from typing import Optional
122 
123class VerifyChannel(Enum):
124 SMS = "sms"
125 CALL = "call"
126 EMAIL = "email"
127 WHATSAPP = "whatsapp"
128 
129class 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 
194Build Interactive Voice Response (IVR) systems using TwiML.
195TwiML (Twilio Markup Language) is XML that tells Twilio what to do
196when receiving calls.
197 
198Core 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 
206Key insight: Twilio makes HTTP request to your webhook, you return
207TwiML, 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```python
213from flask import Flask, request, Response
214from twilio.twiml.voice_response import VoiceResponse, Gather
215from twilio.request_validator import RequestValidator
216import os
217 
218app = Flask(__name__)
219 
220def 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
239def 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
265def 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 

Discussion

Alternatives

API and interface designGuides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.Coding · MITContext7Pulls up-to-date, version-specific library docs and code examples into the prompt so the AI stops inventing old APIs.Coding · MITContext7 Documentation LookupFetch up-to-date documentation and code examples for any library, framework, SDK, CLI tool, or cloud service. Use whenever the user asks about a specific library — even well-known ones like React, Next.js, Prisma, Express, Tailwind, Django, or Spring Boot — because training data may not reflect recent API changes or version updates. Always use for: API syntax questions, configuration options, version migration issues, "how do I" questions mentioning a library name, debugging that involves library-specific behavior, setup instructions, and CLI tool usage. Use even when you think you know the answer. Do not rely on training data for API details, signatures, or configuration options — they are frequently out of date. Prefer this over web search for library documentation.Coding · MITAdaptyv Bio Foundry APIHow to use the Adaptyv Bio Foundry API and Python SDK for protein experiment design, submission, and results retrieval. Use this skill whenever the user mentions Adaptyv, Foundry API, protein binding assays, protein screening experiments, BLI/SPR assays, thermostability assays, or wants to submit protein sequences for experimental characterization. Also trigger when code imports `adaptyv`, `adaptyv_sdk`, or `FoundryClient`, or references `foundry-api-public.adaptyvbio.com`.Science · MIT