log in
consulting hosting industries the daily tools about contact

Twilio A2P 10DLC: Why Your SMS Messages Are Silently Disappearing

A2P 10DLC registration is supposed to protect users from spam. In practice, it's a minefield of silent filtering that will make you think your code is broken.

I spent three days last year convinced there was a bug in a Laravel notification channel before I figured out the messages were being delivered — to Twilio — and then silently dropped by the carriers before they ever reached the end user's phone. No error. No webhook. Just gone. That's the A2P 10DLC experience in a nutshell, and if you're building SMS into a production app right now, you need to understand this before you ship.

What A2P 10DLC Actually Is

A2P stands for Application-to-Person. 10DLC stands for 10-digit long code — a regular local phone number, as opposed to a short code (like 55555) or a toll-free number. The carriers (AT&T, T-Mobile, Verizon) got tired of SMS spam routing through local numbers and built a registration system. The idea: if you register your brand, your campaign (the use case for your messages), and link your Twilio number to that campaign, the carriers trust you more and your messages get through.

In theory it's reasonable. In practice, the system has no real feedback loop. A carrier filters your message and nobody tells you. Twilio shows the message as "delivered" to their system. The carrier eats it. The user never sees it. Your logs look clean.

I've integrated SMS for clients in healthcare appointment reminders, e-commerce order updates, and a print management workflow tool. Every single one of them hit some version of this problem.

The Registration Layers You Must Get Right

This is where most people go wrong: they think registration is a single step. It's not. There are three distinct layers, and if any one of them is wrong or mismatched, you're in silent-filter territory.

1. Brand registration — who you are as a business. EIN, legal name, address, vertical. This gets vetted against third-party data sources (Twilio uses Campaign Registry). If your EIN doesn't match what's on file with the IRS or your business address is inconsistent, your brand gets a low trust score.

2. Campaign registration — what you're sending and why. This is the use case: appointment reminders, account notifications, marketing, 2FA, etc. The campaign description, sample messages, and opt-in language all get reviewed. This is where I see the most rejections and mismatches.

3. Number-to-campaign linking — your actual Twilio phone numbers must be explicitly linked to an approved campaign. Having an approved campaign means nothing if your numbers aren't attached.

All three have to be consistent with each other and with what you're actually sending.

What Silently Gets Your Messages Filtered

Carriers don't publish their full filter rules, but from digging through Twilio support threads, testing with real numbers, and a few painful client incidents, here's what I've seen cause silent filtering:

Mismatched campaign use case. You registered a "transactional notifications" campaign and then sent a promotional discount code. Carriers run content analysis. If your messages don't match the declared use case, they get dropped. This is the most common one.

Missing or weak opt-in language in your campaign registration. You have to describe how users opted in to receive your messages. Vague descriptions like "users agree to terms" aren't enough. Be specific: "Users check a box on the account creation form at example.com/register that reads: 'I agree to receive SMS notifications about my account.'" Weak opt-in descriptions tank your trust score.

Sample messages that don't reflect what you're actually sending. The sample messages in your campaign registration are taken seriously. If your samples are "Your appointment is at 3pm" and you're actually sending "SAVE 20% this weekend only — reply STOP to opt out," that mismatch is a filter trigger.

Sending from a number not linked to your campaign. Easy to do if you have multiple Twilio numbers and you link some but not all. Check every number you send from.

URLs in messages that aren't whitelisted. If you include a link, the domain needs to match what you registered. Shortened URLs (bit.ly, tinyurl) are heavily filtered. Use your own domain.

Not including STOP/HELP language when required. Marketing campaigns require it on initial messages. Missing it triggers filters.

Low brand trust score due to new EIN or mismatched business data. If your client just formed an LLC last month, their EIN has no history. Trust score will be low. Factor in time for this to stabilize.

How to Diagnose Silent Filtering

This is the part that cost me three days. Here's how to actually figure out what's happening.

First: confirm delivery to Twilio vs. delivery to the handset. Twilio's message status delivered means delivered to the carrier, not to the phone. You want to check for carrier acknowledgment. Set up a status callback and look for delivered versus undelivered or failed — but understand that even delivered can mean the carrier received it and then filtered it.

Here's a basic status callback endpoint in Laravel:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class TwilioStatusCallbackController extends Controller
{
    public function handle(Request $request): \Illuminate\Http\Response
    {
        // Twilio sends form-encoded POST, not JSON
        $sid    = $request->input('MessageSid');
        $status = $request->input('MessageStatus');
        $to     = $request->input('To');
        $error  = $request->input('ErrorCode');

        Log::channel('sms')->info('SMS status update', [
            'sid'        => $sid,
            'status'     => $status,
            'to'         => $to,
            'error_code' => $error,
        ]);

        // Persist to DB for auditing
        \App\Models\SmsLog::where('twilio_sid', $sid)
            ->update([
                'status'     => $status,
                'error_code' => $error,
                'updated_at' => now(),
            ]);

        return response('', 204);
    }
}

Make sure you're passing statusCallback when you send:

<?php

use Twilio\Rest\Client;

$twilio = new Client(
    config('services.twilio.sid'),
    config('services.twilio.token')
);

$message = $twilio->messages->create(
    $toNumber,
    [
        'from'           => config('services.twilio.from'),
        'body'           => $messageBody,
        'statusCallback' => route('twilio.status-callback'),
    ]
);

Second: use Twilio's Message Filtering Insights. In the Twilio Console under Monitor > Messaging > Message Filtering, you can see if a message was flagged. Not all filtered messages show up here, but carrier-level filtering sometimes does. Check it before assuming the problem is your code.

Third: test with numbers on different carriers. Silent filtering is often carrier-specific. AT&T might pass your message while T-Mobile drops it. Send test messages to phones on AT&T, T-Mobile, and Verizon. I keep a SIM card drawer in the office for exactly this. If one carrier is consistently failing, the problem is usually your campaign registration content or use-case mismatch on that carrier's filters.

Fourth: check your campaign status in the Twilio console explicitly. Go to Messaging > Regulatory Compliance > A2P 10DLC. Confirm your brand status is VERIFIED, your campaign status is REGISTERED, and your numbers are attached. I've seen campaigns in PENDING limbo for weeks where Twilio's UI isn't clear about what's blocking it.

Fifth: look at error code 30034. This is Twilio's code for "Message blocked by carrier due to A2P filtering." If you're logging your status callbacks properly, this will show up. It won't always appear — carriers don't always report back — but when it does, it confirms the registration is the issue, not your code.

When I'd Reach for This Setup (and When I Wouldn't)

For transactional SMS — appointment reminders, order confirmations, password resets, shipping updates — A2P 10DLC on a local long code is the right call for most of my clients. It's cheaper than short codes (which run $500-1000/month to lease) and the throughput is adequate for most apps (1 message/second per number, more if you use a messaging service with multiple numbers pooled).

I would not use a local long code for high-volume marketing blasts. For that, a short code is worth the cost and the carrier trust is much higher. For 2FA specifically, I'd seriously look at toll-free numbers — they have their own registration process but carrier filtering on toll-free is generally lighter, and the throughput is better.

For healthcare clients specifically, I always make sure HIPAA considerations are addressed separately from the A2P registration. Registration doesn't make your SMS HIPAA-compliant. Those are two different problems.

One more thing: if your client has a new business with a new EIN, budget 2-4 weeks for the full registration chain to propagate and stabilize. I've started building this lead time into project timelines as a line item because it has burned me on launch dates.

The Bottom Line

A2P 10DLC is a bureaucratic layer that the carriers imposed on everyone because SMS spam got out of control — and honestly, that's fair. But the absence of real-time feedback when your messages are filtered makes it genuinely difficult to debug, and "delivered" in Twilio's API means a lot less than you'd think. Log your status callbacks, test across carriers, and get your campaign registration language right the first time. It's not glamorous work, but it's the difference between an SMS feature that works and one that quietly fails for 30% of your users.

Need help shipping something like this? Get in touch.