Twilio Credential Security: API Key Management and Best Practices
In this tutorial, you will learn about Twilio Credential Security: API Key Management and Best Practices. We cover key concepts, practical examples, and best practices to help you master this topic.
Twilio credential security involves managing API keys and Auth Tokens, creating restricted keys per environment, isolating traffic with subaccounts, rotating credentials regularly, and monitoring for unauthorized API access.
What You'll Learn
How to create and manage Twilio API keys, use restricted API keys with granular permissions, isolate environments with subaccounts, rotate credentials on a schedule, configure IP access control, detect unauthorized use, and follow security best practices.
Why It Matters
A leaked Auth Token gives attackers full control of your Twilio account — sending SMS on your dime, modifying Webhooks, and accessing logs. DodaTech follows strict credential management with per-service keys and automated rotation.
Real-World Use
A developer accidentally commits an API key to GitHub. DodaTech's credential scanner detects the exposure within 5 minutes, automatically rotates the compromised key, revokes access, and alerts the security team — preventing any unauthorized usage.
flowchart LR
A["Create API\nKey"] --> B{"Key Type"}
B -->|Main| C["Full Access\nAuth Token"]
B -->|Restricted| D["Scoped\nAPI Key"]
D --> E["Service A\nSMS Only"]
D --> F["Service B\nVerify Only"]
C --> G["Subaccount\nIsolation"]
G --> H["Dev\nSubaccount"]
G --> I["Prod\nSubaccount"]
style A fill:#f22f46,color:#fff
style D fill:#dbeafe,stroke:#2563eb
style G fill:#bbf7d0,stroke:#16a34a
Creating API Keys
import os
from twilio.rest import Client
client = Client(
os.environ["TWILIO_ACCOUNT_SID"],
os.environ["TWILIO_AUTH_TOKEN"]
)
# Create a standard API key
def create_api_key(name, key_type="standard"):
"""Create a new API key.
Types: standard (full access), restricted (scoped)
"""
key = client.new_keys.create(
friendly_name=name,
key_type=key_type
)
print(f"Key SID: {key.sid}")
print(f"Name: {key.friendly_name}")
print(f"Type: {key.key_type}")
print(f"Secret: {key.secret[:10]}...") # Only shown once!
print("SAVE THIS SECRET - it will not be shown again.")
return key
# key = create_api_key("DodaTech SMS Service", "standard")
# Expected output:
# Key SID: SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Name: DodaTech SMS Service
# Type: standard
# Secret: GSb3c4d5e...
# SAVE THIS SECRET - it will not be shown again.
Restricted API Keys
# Create restricted API key with granular permissions
def create_restricted_key(name, permissions, subaccount_sid=None):
"""Create an API key restricted to specific services.
permissions: list of allowed services
Example: ["sms:send", "verify:create", "calls:make"]
"""
params = {
"friendly_name": name,
"key_type": "restricted",
"restricted_data_transfer": {
"direction": "inbound,outbound"
}
}
if subaccount_sid:
params["subaccount_sid"] = subaccount_sid
key = client.new_keys.create(**params)
print(f"Restricted Key: {key.sid}")
print(f"Name: {key.friendly_name}")
print(f"Permissions: {permissions}")
return key
# restricted_key = create_restricted_key(
# "Verify Service Only",
# ["verify:create", "verify:check"]
# )
# Expected output:
# Restricted Key: SKyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
# Name: Verify Service Only
# Permissions: ['verify:create', 'verify:check']
# Using restricted key instead of Auth Token
restricted_client = Client(
os.environ["TWILIO_ACCOUNT_SID"],
"GS_restricted_key_secret_here"
)
# Test restricted access
try:
result = restricted_client.verify.v2.services("VAxxx").verifications.create(
to="+14155551234",
channel="sms"
)
print(f"Verify API works: {result.sid}")
except Exception as e:
print(f"Verify API error: {e}")
Subaccounts for Environment Isolation
# Create a subaccount for development
def create_subaccount(name):
subaccount = client.api.accounts.create(
friendly_name=name
)
print(f"Subaccount SID: {subaccount.sid}")
print(f"Name: {subaccount.friendly_name}")
print(f"Status: {subaccount.status}")
return subaccount
# dev_acct = create_subaccount("DodaTech Dev")
# Expected output:
# Subaccount SID: ACyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
# Name: DodaTech Dev
# Status: active
# Create API key for subaccount
def create_subaccount_key(subaccount_sid, name):
"""Create a key that operates only within the given subaccount."""
subaccount_client = Client(
subaccount_sid,
os.environ["TWILIO_AUTH_TOKEN"]
)
key = subaccount_client.new_keys.create(
friendly_name=name
)
print(f"Subaccount Key: {key.sid}")
print(f"Scope: {subaccount_sid}")
return key
# List all subaccounts
def list_subaccounts():
accounts = client.api.accounts.list()
print("All accounts:")
for acct in accounts:
print(f" {acct.friendly_name}: {acct.sid[:20]}... ({acct.status})")
return accounts
# list_subaccounts()
# Expected output:
# All accounts:
# DodaTech Main: ACxxxxxxxxxxxxxxxx... (active)
# DodaTech Dev: ACyyyyyyyyyyyyyyyy... (active)
Credential Rotation
import schedule
import time
def rotate_credentials():
"""Rotate API keys: create new, switch apps, delete old."""
print(f"[{time.ctime()}] Starting credential rotation...")
# 1. Create new key
new_key = client.new_keys.create(
friendly_name=f"DodaTech SMS {time.strftime('%Y-%m')}"
)
print(f" Created: {new_key.sid}")
# 2. In production, deploy new key to environment
# (This is handled by your CI/CD system)
print(f" Deploy new key secret to production")
# 3. After confirming new key works, delete old key
old_key_sid = "SK_old_key_to_delete"
try:
client.new_keys(old_key_sid).delete()
print(f" Deleted old key: {old_key_sid}")
except Exception as e:
print(f" Could not delete old key: {e}")
print(" Rotation complete.")
return new_key
# Schedule monthly rotation
# schedule.every().month.do(rotate_credentials)
# rotate_credentials()
# Expected output:
# [Mon Jun 28 10:00:00 2026] Starting credential rotation...
# Created: SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Deploy new key secret to production
# Deleted old key: SK_old_key_to_delete
# Rotation complete.
Monitoring for Unauthorized Use
def check_unauthorized_activity(hours_back=24):
"""Check for suspicious API activity."""
from datetime import datetime, timedelta
start_time = datetime.utcnow() - timedelta(hours=hours_back)
# Check for failed auth attempts
events = client.monitor.events.list(
start_date=start_time,
level="ERROR",
limit=50
)
suspicious = []
for event in events:
if event.description and "authentication" in event.description.lower():
suspicious.append(event)
print(f"SUSPICIOUS: {event.date_created}")
print(f" {event.description}")
print(f" SID: {event.sid}")
print()
print(f"Found {len(suspicious)} suspicious events in {hours_back}h")
return suspicious
# check_unauthorized_activity()
# Expected output:
# Found 0 suspicious events in 24h
# Check API key usage
def list_api_keys_and_usage():
keys = client.new_keys.list()
print(f"Active API keys: {len(keys)}")
for k in keys:
print(f" {k.friendly_name}: {k.sid[:20]}... ({k.date_created})")
print(f" Type: {k.key_type}")
return keys
Common Mistakes
1. Using the Main Auth Token in Every Service
Your main Auth Token has full account access. Use restricted API keys per service. If a key leaks, only that service is compromised, not your entire account.
2. Hardcoding Credentials in Source Code
Credentials in source code end up in version control. Use environment variables in development, secrets manager (AWS Secrets Manager, HashiCorp Vault) in production.
3. Never Rotating Keys
Without rotation, a leaked key works indefinitely. Rotate API keys every 90 days minimum. Use automation to rotate without downtime.
4. Sharing Auth Tokens Across Environments
Use separate subaccounts for dev, staging, and production. Each has its own keys and billing. A dev mistake won't affect production data or costs.
5. Not Monitoring for Anomalies
You won't know about leaked credentials until the bill arrives. Monitor for unusual sending patterns, new key creation, and failed auth attempts. Set up alerts.
Practice Questions
- What is the difference between an Auth Token and an API Key?
- What are restricted API keys and why use them?
- What is a subaccount and how does it improve security?
- How often should you rotate credentials?
Answers:
- Auth Token (starts with no prefix, or you get it from Console) has full account access. API Keys (prefix SK) can be restricted per service. API Keys are safer for individual service use.
- Restricted API Keys limit access to specific services (SMS only, Verify only). If the key leaks, attackers can only use that specific service, minimizing Blast Radius.
- A subaccount is an isolated Twilio account under your main account. Use separate subaccounts for dev, staging, and production. Each has independent keys, usage, and billing.
- Every 90 days minimum. Automate rotation with a scheduled job that creates a new key, deploys it, and deletes the old one. Rotate immediately if you suspect a leak.
Challenge: Build a credential management system: create restricted API keys for 3 services (SMS, Verify, Voice), set up subaccounts for dev and production, implement a credential rotation script with zero-downtime key switching, configure IP access control for production keys, set up alerting for new key creation and failed auth events, and write a credential leak Incident Response procedure.
FAQ
Mini Project
Build a credential lifecycle management system: create 3 restricted API keys for different services, set up subaccounts for dev/prod isolation, implement a credential rotation script with automated deployment, configure IP access control, create a monitoring dashboard for API key usage and anomalies, and write a security incident response plan.
What's Next
Complete Twilio Project — build a full notification system combining everything you learned.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro