11 min readGuideSMS

SMS Marketing Compliance Guide for Developers

Building SMS features into your app? Here's what you need to know about TCPA, 10DLC, A2P registration, and consent management — with code examples to get it right.

Disclaimer: This guide is for informational purposes and is not legal advice. SMS compliance laws vary by jurisdiction and change frequently. Consult with a legal professional for your specific use case.

Why SMS Compliance Matters for Developers

If you're building an app that sends text messages — follow-ups, notifications, marketing, reminders — you are on the hook for compliance. Not your users. Not the carrier. You, the developer who built the system.

The penalties are real. TCPA violations carry fines of $500-$1,500 per message. A single campaign to 1,000 numbers without proper consent could result in a $500,000-$1.5M liability. And carriers will shut down your number without warning if they detect non-compliant traffic.

The good news: compliance is straightforward if you build it into your system from day one.

The Regulatory Landscape

TCPA (Telephone Consumer Protection Act)

The federal law governing SMS marketing in the United States. Key requirements:

  • Prior express written consent is required before sending marketing messages
  • Consent must be clear and conspicuous — not buried in a terms-of-service page
  • Every message must include an opt-out mechanism (reply STOP to unsubscribe)
  • You must honor opt-outs immediately — no "processing your request" delays
  • Transactional messages (order confirmations, appointment reminders) have different rules than marketing messages

10DLC (10-Digit Long Code) Registration

Since 2024, all carriers (AT&T, T-Mobile, Verizon) require businesses to register their 10-digit phone numbers for A2P (Application-to-Person) messaging. Without registration:

  • Messages will be silently filtered (they don't bounce, they just disappear)
  • Your throughput is capped at 1 message per second or less
  • Carriers may block your number entirely

A2P Registration Process

Here's what you need to register through your provider (Twilio, Vonage, etc.):

  1. Brand registration — Register your company with The Campaign Registry (TCR)
  2. Campaign registration — Register each messaging use case (marketing, notifications, etc.)
  3. Sample messages — Provide example messages for each campaign
  4. Approval — Wait 1-7 business days for carrier approval

Cost is typically $4/month for brand registration + $10/month per campaign through Twilio or similar.

Building a Compliant Messaging System

Step 1: Consent Collection

Your system must track consent at the individual level. Here's a schema and implementation:

SQL — Consent tracking schema
CREATE TABLE sms_consent (
  id            SERIAL PRIMARY KEY,
  phone_number  VARCHAR(20) NOT NULL,      -- E.164 format: +15125551234
  consent_given BOOLEAN DEFAULT FALSE,
  consent_type  VARCHAR(50),               -- 'marketing', 'transactional', 'both'
  consent_source VARCHAR(100),             -- 'web_form', 'sms_keyword', 'paper'
  consent_text  TEXT,                       -- Exact disclosure text shown at opt-in
  consented_at  TIMESTAMP,
  opted_out     BOOLEAN DEFAULT FALSE,
  opted_out_at  TIMESTAMP,
  ip_address    VARCHAR(45),               -- For web form opt-ins
  created_at    TIMESTAMP DEFAULT NOW()
);

-- Index for fast lookups before sending
CREATE INDEX idx_consent_phone ON sms_consent(phone_number, opted_out);
Python — Consent check before sending
import re
from datetime import datetime

def normalize_phone(phone: str) -> str:
    """Convert any phone format to E.164."""
    digits = re.sub(r'\D', '', phone)
    if len(digits) == 10:
        return f"+1{digits}"
    elif len(digits) == 11 and digits[0] == '1':
        return f"+{digits}"
    return phone  # Return as-is if can't normalize

def has_consent(phone: str, message_type: str = "marketing") -> bool:
    """Check if a phone number has valid consent for this message type."""
    phone = normalize_phone(phone)

    # Query your database
    consent = db.query(
        "SELECT * FROM sms_consent WHERE phone_number = %s AND opted_out = FALSE",
        [phone]
    )

    if not consent:
        return False

    # Marketing requires explicit consent
    if message_type == "marketing":
        return consent.consent_type in ("marketing", "both") and consent.consent_given

    # Transactional messages have looser requirements
    if message_type == "transactional":
        return consent.consent_given

    return False

def send_sms(phone: str, message: str, message_type: str = "marketing"):
    """Send an SMS with compliance checks."""
    phone = normalize_phone(phone)

    # Check consent
    if not has_consent(phone, message_type):
        raise ValueError(f"No {message_type} consent for {phone}")

    # Ensure opt-out language is included in marketing messages
    if message_type == "marketing" and "STOP" not in message.upper():
        message += "\nReply STOP to unsubscribe"

    # Send via your provider (Twilio example)
    twilio_client.messages.create(
        body=message,
        from_=YOUR_TWILIO_NUMBER,
        to=phone
    )

    # Log the send
    db.execute(
        "INSERT INTO sms_log (phone, message, type, sent_at) VALUES (%s, %s, %s, %s)",
        [phone, message, message_type, datetime.utcnow()]
    )

Step 2: Opt-Out Handling

You must process opt-outs automatically and immediately. The standard keywords are STOP, UNSUBSCRIBE, CANCEL, END, and QUIT:

Python — Webhook handler for incoming SMS (opt-out processing)
OPT_OUT_KEYWORDS = {"stop", "unsubscribe", "cancel", "end", "quit"}
OPT_IN_KEYWORDS = {"start", "yes", "unstop"}

def handle_incoming_sms(from_number: str, body: str):
    """Process incoming SMS for opt-in/opt-out keywords."""
    normalized = body.strip().lower()
    phone = normalize_phone(from_number)

    if normalized in OPT_OUT_KEYWORDS:
        # Immediately opt out
        db.execute(
            """UPDATE sms_consent
               SET opted_out = TRUE, opted_out_at = NOW()
               WHERE phone_number = %s""",
            [phone]
        )
        # Send confirmation (required by TCPA)
        send_raw_sms(phone, "You've been unsubscribed and will no longer receive messages from us.")
        return {"action": "opted_out"}

    if normalized in OPT_IN_KEYWORDS:
        # Re-opt-in
        db.execute(
            """UPDATE sms_consent
               SET opted_out = FALSE, opted_out_at = NULL
               WHERE phone_number = %s""",
            [phone]
        )
        send_raw_sms(phone, "You've been re-subscribed. Reply STOP anytime to opt out.")
        return {"action": "opted_in"}

    return {"action": "message_received", "body": body}

Step 3: Message Content Requirements

Every marketing SMS must include:

  • Business identification — The recipient must know who is messaging them
  • Opt-out instructions — "Reply STOP to unsubscribe" or similar
  • No deceptive content — No fake urgency, no misleading claims
  • Sent during appropriate hours — 8 AM to 9 PM in the recipient's local time zone
Python — Message validation
from datetime import datetime
import pytz

def validate_sms(message: str, business_name: str, recipient_timezone: str = "US/Eastern"):
    """Validate an SMS message for compliance before sending."""
    errors = []

    # Check message length (SMS limit is 160 chars, or 1600 for long SMS)
    if len(message) > 1600:
        errors.append("Message exceeds 1600 character limit")

    # Check for business identification
    if business_name.lower() not in message.lower():
        errors.append(f"Message must include business name: {business_name}")

    # Check for opt-out language
    opt_out_phrases = ["reply stop", "text stop", "opt out", "unsubscribe"]
    has_opt_out = any(phrase in message.lower() for phrase in opt_out_phrases)
    if not has_opt_out:
        errors.append("Marketing messages must include opt-out instructions (e.g., 'Reply STOP to unsubscribe')")

    # Check sending hours (8 AM - 9 PM in recipient's timezone)
    tz = pytz.timezone(recipient_timezone)
    local_hour = datetime.now(tz).hour
    if local_hour < 8 or local_hour >= 21:
        errors.append(f"Outside sending hours in {recipient_timezone} (current: {local_hour}:00, allowed: 8:00-21:00)")

    return {"valid": len(errors) == 0, "errors": errors}

Using Rebirth API for Compliant SMS Generation

Rebirth API's SMS Generate endpoint creates industry-aware messages. But it generates the content — compliance (consent, opt-out handling, delivery) is your responsibility. Here's how to use it in a compliant workflow:

Python — Compliant SMS workflow with Rebirth API
import requests

REBIRTH_HEADERS = {
    "Authorization": "Bearer rb_live_YOUR_KEY",
    "Content-Type": "application/json"
}

def send_compliant_follow_up(phone, industry, context, business_name, timezone="US/Eastern"):
    """Generate and send a compliant follow-up SMS."""

    # Step 1: Check consent
    if not has_consent(phone, "marketing"):
        return {"error": "No consent", "sent": False}

    # Step 2: Generate message with Rebirth API
    gen_resp = requests.post(
        "https://rebirthapi.com/api/v1/sms-generate",
        headers=REBIRTH_HEADERS,
        json={
            "type": "follow_up",
            "industry": industry,
            "context": context,
            "business_name": business_name
        }
    ).json()

    message = gen_resp["message"]

    # Step 3: Ensure compliance elements are present
    if "STOP" not in message.upper():
        message += " Reply STOP to opt out."

    # Step 4: Validate before sending
    validation = validate_sms(message, business_name, timezone)
    if not validation["valid"]:
        return {"error": validation["errors"], "sent": False}

    # Step 5: Send via Twilio
    send_sms(phone, message, message_type="marketing")

    return {"message": message, "sent": True}

# Usage
result = send_compliant_follow_up(
    phone="+15125551234",
    industry="hvac",
    context="Customer asked about AC repair pricing",
    business_name="CoolAir HVAC"
)
print(result["message"])
# "Hey! This is CoolAir HVAC following up on your AC question.
#  We're running a spring tune-up special — want a free estimate?
#  Reply YES or call (512) 555-0000. Reply STOP to opt out."

Compliance Checklist

Use this checklist before launching any SMS feature:

  • 10DLC Registration — Brand and campaign registered with your SMS provider
  • Consent collection — Clear opt-in form with explicit consent language
  • Consent storage — Database with timestamp, source, and exact disclosure text
  • Opt-out handling — Automatic processing of STOP, UNSUBSCRIBE, CANCEL, END, QUIT
  • Opt-out confirmation — Confirmation message sent after opt-out
  • Business identification — Every message includes the business name
  • Opt-out instructions — Every marketing message includes "Reply STOP to unsubscribe"
  • Sending hours — Messages only sent 8 AM-9 PM in recipient's time zone
  • Phone number format — All numbers stored and sent in E.164 format (+15125551234)
  • Audit log — Every sent message logged with timestamp, content, and recipient

Common Mistakes Developers Make

1. Assuming "They Gave Us Their Number" Is Consent

A customer calling your business is NOT consent to send marketing SMS. Consent must be explicit, in writing, and specific to SMS marketing. A checkbox on a web form that says "I agree to receive promotional text messages from [Business]" qualifies. An implied agreement doesn't.

2. Not Using E.164 Format

Phone numbers should always be stored in E.164 format: +15125551234. Without the country code and proper formatting, messages silently fail or get routed incorrectly. Twilio's error 30034 is usually a formatting issue.

3. Skipping 10DLC Registration

Without 10DLC registration, your messages will be silently filtered by carriers. They won't bounce. They won't error. They just won't arrive. This is the #1 reason developers think "SMS doesn't work" when it's actually a registration issue.

4. Not Handling the STOP Keyword

Most providers (Twilio, Vonage) handle STOP automatically at the platform level. But if you're building custom logic, you must handle it yourself. And you must stop sending within seconds, not days.

5. Sending at 2 AM

If your system runs on UTC and your recipients are in Pacific Time, a 10 AM UTC send arrives at 2 AM PT. Always convert to the recipient's local time zone before sending.

FAQ

Do transactional messages need consent?

Transactional messages (order confirmations, appointment reminders, shipping updates) require consent, but it can be implied through the business relationship. You don't need explicit written consent for these. However, you still need opt-out capability.

How long is consent valid?

There is no hard expiration, but best practice is to re-confirm consent if you haven't messaged someone in 18 months. If someone gave consent 3 years ago and has never heard from you, sending a marketing blast could be a problem.

Can I buy phone number lists?

No. Purchased lists do not constitute consent. Every number you message must have individually opted in to receive messages from your specific business. This is non-negotiable under TCPA.

What about Canada and the EU?

Canada follows CASL (Canadian Anti-Spam Legislation) which is similar but stricter than TCPA. The EU follows GDPR, which requires explicit consent and data processing agreements. If you message internationally, comply with the strictest applicable law.

Generate Compliant SMS Content

Rebirth API's SMS Generate endpoint creates industry-aware messages. Free tier with 100 calls/month.