DEV Community

Hamza Chaudhary
Hamza Chaudhary

Posted on • Originally published at Medium

๐Ÿš€ Integrating Twilio with Django for OTP Authentication via SMS

Intro

๐Ÿ‘‹ Introduction

You know that moment when you try to log into a site and it says:

"We've sent a code to your phone. Please enter it below."

That little 4โ€“6 digit number is a One-Time Password (OTP) โ€” your app's way of saying:

"Hey, prove you're really you."

In this guide, we'll make your Django app send OTPs over SMS using Twilio.
We'll also see how to reuse the same setup for sending other types of messages, like deep links.

By the end, you'll have:
โœ… A Twilio client class you can reuse anywhere
โœ… A background task that sends OTPs without slowing down your app
โœ… Proper error handling so your app doesn't freak out when a number is unverified


Step 0: Twilio Setup โ€” What You Need

Before writing any code, you'll need three pieces of information from your Twilio account:

TWILIO_ACCOUNT_SID

  • Your Twilio account's unique identifier.
  • Found in the Twilio Console under the Account Info section.

TWILIO_AUTH_TOKEN

  • A secret key that authorizes API requests.
  • Also in the Twilio Console, right next to your Account SID.

TWILIO_PHONE_NUMBER

  • An SMS-capable phone number you've purchased or verified in Twilio.

Found under **Phone Numbers โ†’ Manage โ†’ Active Numbers** in the console.

๐Ÿ’ก Tip: Store these values in your .env file and load them into settings.py with django-environ or a similar library, so you don't expose secrets in your code.


๐Ÿ“ฆ Step 1: Install Twilio SDK

pip install twilio==9.4.*
Enter fullscreen mode Exit fullscreen mode

Or in requirements.txt:

twilio==9.4.*
Enter fullscreen mode Exit fullscreen mode

โš™๏ธ Step 2: Configure Twilio in Django Settings

TWILIO_ACCOUNT_SID = "your_account_sid_here"
TWILIO_AUTH_TOKEN = "your_auth_token_here"
TWILIO_PHONE_NUMBER = "+1234567890"
Enter fullscreen mode Exit fullscreen mode

๐Ÿค Step 3: Create a Twilio Client Helper Class

This is our "middleman" โ€” it knows how to talk to Twilio and can send both OTPs and custom messages.

from twilio.base.exceptions import TwilioRestException
from twilio.rest import Client
from django.conf import settings

class TwilioClient:
    """Handles Twilio SMS sending for OTPs and other messages."""

    def __init__(self):
        self.account_sid = settings.TWILIO_ACCOUNT_SID
        self.auth_token = settings.TWILIO_AUTH_TOKEN
        self.twilio_phone_number = settings.TWILIO_PHONE_NUMBER
        self.client = Client(self.account_sid, self.auth_token)

    def _send_message(self, phone_number, body):
        try:
            self.client.messages.create(
                to=phone_number,
                from_=self.twilio_phone_number,
                body=body.strip()
            )
            return {"message": "Message sent successfully.", "status": 201}
        except TwilioRestException as e:
            if e.code == 21606:
                return {"message": "Phone number is unverified.", "status": 400}
            return {"message": str(e.msg), "status": 400}

    def send_otp(self, otp, phone_number):
        body = f"OTP Verification: Please use the following One-Time Password for verification: {otp}"
        return self._send_message(phone_number, body)

    def send_message(self, message, phone_number):
        body = f"Please find the message here: {message}"
        return self._send_message(phone_number, body)
Enter fullscreen mode Exit fullscreen mode

โณ Step 4: Generate and Send OTP with Celery

Why Celery?

Because sending SMS can be slow, and we don't want users waiting while Twilio does its thing.
Celery runs the task in the background, so your app stays snappy.

from celery import shared_task
from myapp.services.twilio_client import TwilioClient
import logging

logger = logging.getLogger(__name__)

@shared_task
def send_sms_otp(phone_number, otp):
    try:
        twilio_client = TwilioClient()
        twilio_client.send_otp(otp, phone_number)
    except Exception as e:
        logger.exception(f"Unable to send OTP SMS to {phone_number} due to {e}")
        raise e
Enter fullscreen mode Exit fullscreen mode

OTP Generator

import random

def get_otp():
    digits = list(range(0, 10))
    random.shuffle(digits)
    while True:
        if digits[0] == 0:
            random.shuffle(digits)
        else:
            break
    return int("".join(map(str, digits[:4])))
Enter fullscreen mode Exit fullscreen mode

How to use it

from myapp.tasks import send_sms_otp
from myapp.utils import get_otp
from django.utils import timezone
from myapp.constants import constance_config

otp = get_otp()
phone_number = "+1234567890"

# Send OTP asynchronously
send_sms_otp.delay(phone_number, otp)

# Optional: store OTP in DB for verification later
# user.otp = otp
# user.otp_expires_at = timezone.now() + timezone.timedelta(
#     seconds=constance_config.OTP_EXPIRATION_TIME_SECONDS
# )
# user.save(update_fields=["otp", "otp_expires_at"])
Enter fullscreen mode Exit fullscreen mode

๐Ÿงช Step 5: Testing the Flow

  1. Trigger the Celery task from your Django view:
otp = get_otp()
phone_number = "+1234567890"
send_sms_otp.delay(phone_number, otp)
Enter fullscreen mode Exit fullscreen mode
  1. Check your Twilio dashboard for message logs.

Twilio dashboard

  1. Test with both verified and unverified phone numbers to see error handling in action.

๐Ÿ“ฉ Wrapping Up

With Twilio + Django + Celery working together, you've given your app a voice (wellโ€ฆ text).
Now it can ping users instantly, whether it's for OTPs, appointment reminders, or even "your coffee order is ready" alerts.

Remember:

  • Keep your Twilio credentials safe.
  • Test with both verified and unverified numbers.
  • Handle errors gracefully โ€” nothing kills trust faster than a broken OTP flow.

If this guide helped you, give it a โญ on GitHub or share it with your dev friends.
And if your phone just buzzed, don't panic โ€” it's probably your app saying hello. ๐Ÿ“ฑโœจ


๐Ÿ’ก TL;DR

Goal: Send OTPs from Django via Twilio

Stack: Django + Twilio SDK + Celery

Steps:
1๏ธโƒฃ Get Twilio credentials (SID, Token, Number)
2๏ธโƒฃ Install twilio Python package
3๏ธโƒฃ Create TwilioClient helper class
4๏ธโƒฃ Generate OTP with get_otp()
5๏ธโƒฃ Send via Celery background task

Why Celery? No waiting for SMS API โ€” keeps your app fast

Bonus: Same setup works for deep links, reminders, and custom messages

Top comments (1)

Collapse
 
noman_akram_a3bf2939061e0 profile image
Noman Akram

How do I test OTP expiration and retry scenarios?