Skip to content

nicolasguzca/twilio-sms-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Twilio SMS Config Tool

A lightweight Django web app for configuring and testing Twilio SMS credentials. Supports both Auth Token and API Key authentication methods. Enter your credentials, save them, then fire a test message to any number — all from a single-page UI. All secrets are encrypted at rest using Fernet symmetric encryption.

Python Django Twilio


Features

  • Two auth methods — Auth Token (quick setup) and API Keys (recommended for production)
  • Credential management — Both credential sets stored independently in local SQLite
  • Encrypted at rest — Auth token and API key secret are Fernet-encrypted before being written to the database; never stored in plaintext
  • Prefilled forms — Saved credentials load and prefill on every page visit
  • Test SMS — Send a real message and see the full Twilio API response inline
  • Debug output — Full Twilio response surfaced in the UI: status, error code, error message, price, direction, auth method used, and more
  • Single-page UI — No full-page reloads; all interactions use fetch() with inline status banners

Auth Method Comparison

Auth Token API Keys
Credentials needed Account SID + Auth Token Account SID + API Key SID + API Key Secret
Recommended for Quick testing / local dev Production, CI/CD, team environments
Can be revoked individually No — rotating the auth token affects everything Yes — each key is independent
Scoped permissions No Yes (Standard or ReadOnly)
Twilio recommendation Preferred

Recommendation: Use Auth Token to get started quickly. Switch to API Keys once you're integrating with an application or sharing access with a team.


Tech Stack

Layer Technology
Backend Python 3.11+, Django 5.2
Database SQLite (Django default)
SMS Twilio Python SDK 9.x
Encryption cryptography — Fernet (AES-128-CBC + HMAC-SHA256)
Frontend Tailwind CSS (CDN), Vanilla JS

Prerequisites


Setup

1. Clone the repository

git clone https://github.com/nicolasguzca/twilio-sms-python.git
cd twilio-sms-python

2. Create a virtual environment and install dependencies

python3 -m venv .venv
source .venv/bin/activate       # On Windows: .venv\Scripts\activate
pip install -r requirements.txt

Dependencies (requirements.txt):

Package Version Purpose
Django 5.2 Web framework
twilio 9.10.6 Official Twilio Python SDK
cryptography 44.0.0 Fernet encryption for secrets at rest
python-dotenv 1.0.1 Loads .env into the environment

3. Configure environment variables

cp .env.example .env

Open .env and set the following:

DJANGO_SECRET_KEY=your-django-secret-key
FIELD_ENCRYPTION_KEY=your-fernet-encryption-key
DEBUG=True

Generate a Django secret key:

python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

Generate a Fernet encryption key:

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

These two keys are app secrets — keep them in .env only. The file is already in .gitignore and must never be committed.

4. Run database migrations

python manage.py migrate

This creates a local db.sqlite3 with two tables — one for Auth Token credentials and one for API Key credentials.

5. Start the development server

source .venv/bin/activate && python manage.py runserver
Screen URL
Auth Token http://localhost:8000
API Keys http://localhost:8000/api-key/

Subsequent runs: only source .venv/bin/activate && python manage.py runserver — no need to repeat the setup steps.


Usage

Both screens are reachable via the tab nav at the top of the app. They work independently — credentials are saved and tested separately for each auth method.


Auth Token Screen — http://localhost:8000

Where to find your credentials: Twilio Console homepage — Account SID and Auth Token are shown at the top.

Fields:

Field Format Description
Account SID ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Your Twilio account identifier
Auth Token 32-char hex string Master credential — keep it secret
From Phone Number +15551234567 Your Twilio number (E.164, US only)
  1. Enter your credentials and click Save Credentials
  2. A green banner confirms the save; the form prefills on every subsequent visit
  3. Enter a recipient number and click Send Test SMS

API Keys Screen — http://localhost:8000/api-key/

API Keys are Twilio's recommended authentication method for applications. Unlike the Auth Token, each key can be revoked independently, scoped to read-only access, and rotated without affecting other integrations.

Creating an API key:

  1. Go to Twilio Console → API keys & tokens
  2. Click Create API key
  3. Give it a friendly name, choose Standard type (or ReadOnly if you only need to read — but sending SMS requires Standard)
  4. Copy the SID (SK...) and Secret — the secret is only shown once at creation time

Fields:

Field Format Description
Account SID ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Still required — used in Twilio API URL paths even with key auth
API Key SID SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx The key identifier, starts with SK
API Key Secret 32-char string The secret shown once on creation
From Phone Number +15551234567 Your Twilio number (E.164, US only)
  1. Enter your credentials and click Save API Key Credentials
  2. A green banner confirms the save; the API Key SID and Account SID prefill on every visit (the secret prefills too if saved)
  3. Enter a recipient number and click Send Test SMS

Debug Output

After sending a test SMS, a Twilio Response panel appears with the full API response:

{
  "sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "status": "queued",
  "to": "+15559876543",
  "from": "+15551234567",
  "direction": "outbound-api",
  "error_code": null,
  "error_message": null,
  "price": null,
  "price_unit": "USD",
  "date_created": "2026-05-07 18:00:00+00:00",
  "auth_method": "api_key",
  "api_key_sid": "SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

The auth_method and api_key_sid fields appear only on the API Keys screen, confirming which key was used.

Message statuses:

Status Meaning
queued Twilio accepted the message; delivery is in progress
sent Passed to the carrier
delivered Confirmed delivered (requires status callbacks to be configured)
failed Twilio rejected it before sending — check error_code
undelivered Carrier rejected it — check error_code

Common error codes:

Code Meaning
21211 Invalid To phone number
21614 To number is not a mobile number capable of receiving SMS
21408 Permission to send to this region not enabled on your account
20003 Authentication failed — wrong credentials
30003 Unreachable destination handset
30005 Unknown destination handset

Project Structure

twilio-sms-python/
├── .env                              # Local secrets (gitignored)
├── .env.example                      # Template for .env
├── .gitignore
├── manage.py
├── requirements.txt
├── config/                           # Django project package
│   ├── settings.py                   # App settings, loads from .env
│   ├── urls.py                       # Root URL config
│   └── wsgi.py
└── sms/                              # Main Django app
    ├── crypto.py                     # Fernet encrypt/decrypt helpers
    ├── models.py                     # TwilioConfig + TwilioApiKeyConfig singletons
    ├── views.py                      # 6 views — 3 per auth method
    ├── urls.py                       # App URL routes
    ├── migrations/
    │   ├── 0001_initial.py           # TwilioConfig table
    │   └── 0002_twilioapikeyconfig.py # TwilioApiKeyConfig table
    └── templates/
        └── sms/
            ├── index.html            # Auth Token screen
            └── index_api_key.html    # API Keys screen

Key files

sms/models.py — Two singleton models, each enforced via pk=1. TwilioConfig stores Auth Token credentials; TwilioApiKeyConfig stores API Key credentials. Saving always upserts the single row.

sms/crypto.py — Fernet encrypt/decrypt helpers. The FIELD_ENCRYPTION_KEY env var must be a valid 32-byte URL-safe base64 key. Both the auth token and API key secret are encrypted on every write and decrypted on every read.

sms/views.py — Six views:

Method Endpoint Description
GET / Auth Token screen — prefills saved credentials
POST /api/credentials/ Save Auth Token credentials
POST /api/send-test/ Send test SMS via Auth Token
GET /api-key/ API Keys screen — prefills saved credentials
POST /api/credentials-api-key/ Save API Key credentials
POST /api/send-test-api-key/ Send test SMS via API Key auth

API Reference

POST /api/credentials/

Saves Auth Token credentials.

Request:

{
  "account_sid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "auth_token": "your_auth_token",
  "from_number": "+15551234567"
}

Response (success): { "success": true }

Response (error): { "success": false, "error": "Account SID must start with 'AC'" }


POST /api/send-test/

Sends a test SMS using saved Auth Token credentials.

Request: { "to_number": "+15559876543" }

Response (success):

{
  "success": true,
  "debug": {
    "sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "status": "queued",
    "to": "+15559876543",
    "from": "+15551234567",
    "direction": "outbound-api",
    "error_code": null,
    "error_message": null,
    "price": null,
    "price_unit": "USD",
    "date_created": "2026-05-07 18:00:00+00:00"
  }
}

Response (Twilio error):

{
  "success": false,
  "error": "The 'To' number is not a valid phone number.",
  "debug": { "twilio_code": 21211, "status": 400, "details": {} }
}

POST /api/credentials-api-key/

Saves API Key credentials.

Request:

{
  "account_sid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "api_key_sid": "SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "api_key_secret": "your_api_key_secret",
  "from_number": "+15551234567"
}

Validation rules:

  • account_sid must start with AC
  • api_key_sid must start with SK
  • api_key_secret must be non-empty
  • from_number must be E.164 format (+1XXXXXXXXXX)

Response (success): { "success": true }

Response (error): { "success": false, "error": "API Key SID must start with 'SK'" }


POST /api/send-test-api-key/

Sends a test SMS using saved API Key credentials. Initializes the Twilio client as Client(api_key_sid, api_key_secret, account_sid).

Request: { "to_number": "+15559876543" }

Response (success):

{
  "success": true,
  "debug": {
    "sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "status": "queued",
    "to": "+15559876543",
    "from": "+15551234567",
    "direction": "outbound-api",
    "error_code": null,
    "error_message": null,
    "price": null,
    "price_unit": "USD",
    "date_created": "2026-05-07 18:00:00+00:00",
    "auth_method": "api_key",
    "api_key_sid": "SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}

Security Notes

  • Secrets encrypted at rest — Both the Auth Token and API Key Secret are encrypted with Fernet (AES-128-CBC + HMAC-SHA256) before being stored in SQLite. The encryption key lives only in .env.
  • CSRF protection — All POST endpoints are protected by Django's CSRF middleware. The JS reads the csrftoken cookie and sends it as the X-CSRFToken header on every request.
  • Local use onlyALLOWED_HOSTS is restricted to localhost and 127.0.0.1. Do not expose this app to the public internet without adding authentication.
  • API Key Secret shown once — Twilio only displays the API Key Secret at creation time. Save it before closing the console window.

Development

Run Django system checks:

python manage.py check

Verify Auth Token credentials are encrypted:

sqlite3 db.sqlite3 "SELECT account_sid, from_number, auth_token_encrypted FROM sms_twilioconfig;"

Verify API Key credentials are encrypted:

sqlite3 db.sqlite3 "SELECT account_sid, api_key_sid, from_number, api_key_secret_encrypted FROM sms_twilioapikeyconfig;"

Both _encrypted columns should contain Fernet ciphertext starting with gAAAAA — never the raw secret value.

Reset saved credentials:

# Auth Token
sqlite3 db.sqlite3 "DELETE FROM sms_twilioconfig;"

# API Keys
sqlite3 db.sqlite3 "DELETE FROM sms_twilioapikeyconfig;"

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors