10DLC Campaign Rejected With Error 806: How We Fixed It

A 10DLC campaign we registered was rejected with error 806 — then accepted, then rejected again with the same code by a different reviewer, then approved about seven weeks after the first rejection. Along the way we learned that 806 is less a single problem than a category, and that the fastest path out is diagnosis, not resubmission.
This is what we found, in the order we found it. If you want the background on what A2P 10DLC is and why carriers require it, start with our 10DLC compliance overview.
What does 10DLC error 806 mean?
Error 806 means the reviewer could not verify how people opt in to your texts. The official rejection text reads: "Unable to verify, needs compliant and accurate CTA information. Update with specific path for mobile opt-in." Your campaign's call to action (CTA) or message flow didn't give them a path they could check.
The same failure has other names depending on your provider. Twilio reports it as error 30909, "Campaign rejected: Message Flow or Call to Action incomplete/unverified", which it describes as the message flow not giving reviewers "enough information to verify how end users consent to receive messages."
The operative word in both is verify. Having a compliant opt-in is necessary but not sufficient. A reviewer has to be able to find it and confirm it.
Rejection one: an opt-in the reviewer couldn't see
Our opt-in was compliant on paper. The disclosure — brand, message purpose, frequency, "message and data rates may apply", STOP and HELP, links to terms and privacy — was all there. The problem was where it was.
The sign-up flow was a multi-step wizard rendered in the browser. The SMS consent language only appeared once a user had progressed several steps in. So the page at the URL we'd submitted as the opt-in location didn't contain the disclosure when it first loaded, and its initial HTML didn't contain it at all — the text only existed after JavaScript ran and a user clicked forward.
A human reviewer is not going to fill out your sign-up form to go looking for it. And if any part of a review fetches the page without running scripts, a client-rendered disclosure simply isn't there.
The fix had four parts:
- Render the disclosure on the server, visible the moment the opt-in page loads, on every page we cited as an opt-in location — not only inside the wizard step.
- Publish a static SMS terms page at a stable URL, and link it from the site footer so it's reachable from anywhere.
- Rewrite the campaign's message flow to name the exact URL a reviewer should visit and exactly what they'll see there.
- Update the campaign record with the new message flow and terms link. On Telnyx we
did this through the campaign API (
PUT /10dlc/campaign/{id}, updatingmessageFlowandtermsAndConditionsLink) rather than opening a new campaign.
The campaign moved to TCR_ACCEPTED, with every carrier showing as qualified. We
thought we were done.
Rejection two: same code, different reviewer
Four days later the campaign went TCR_ACCEPTED → MNO_PENDING → MNO_REJECTED, with
the same error 806.
This is the part that is easy to misread. Registry acceptance and carrier approval are
separate gates. Passing The Campaign Registry does not mean each mobile carrier has
approved the campaign. In our case the provider's per-carrier status showed T-Mobile as
the carrier that hadn't registered it (isTMobileRegistered: false).
It's also where we learned that 806 is generic. The second rejection carried exactly the same code and text as the first, even though we had fixed the first cause. The code tells you the category of failure. It doesn't tell you which thing a particular reviewer couldn't verify.
Rule out the boring cause first: is your site blocking the reviewer?
Before changing anything else, confirm that your opt-in page is actually reachable by something other than your own browser. CDNs, web application firewalls, and bot protection routinely challenge or block non-browser traffic — and a blocked page looks, to a checker, exactly like a missing opt-in.
This fetches the page with a browser user agent, a scripting user agent, and plain curl, then reports the HTTP status and whether the disclosure text is in the HTML that came back:
URL="https://example.com/sms-opt-in"
BROWSER="Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/124.0"
for UA in "$BROWSER" "python-requests/2.31" "curl/8.5.0"; do
code=$(curl -s -o page.html -w '%{http_code}' -A "$UA" "$URL")
if grep -qi "data rates may apply" page.html; then
found="disclosure present"
else
found="DISCLOSURE MISSING"
fi
echo "$code $found <- $UA"
done
Read the results like this:
| Result | What it means |
|---|---|
200 + disclosure present for every agent | The page is reachable and the text is in the HTML. Look elsewhere. |
403, 429, or a challenge page for some agents | Your CDN or WAF is blocking non-browser traffic. Allow those paths. |
200 but disclosure missing for every agent | Your disclosure is rendered client-side. Move it into the server HTML. |
When we ran the same check, every agent got a 200 with the disclosure present, which ruled out
blocking cleanly and told us the problem was the content of the page, not access to it.
Don't resubmit blind
With the obvious causes ruled out and a generic code in hand, the tempting move is to tweak the wording and resubmit. We didn't, and we'd recommend against it.
Each resubmission restarts a review that takes days. If you guess wrong, you've spent that time learning nothing, and the code you get back will very likely be 806 again. So we chose to get the specific reason from our provider's support team behind the carrier's rejection before changing anything else. A precise reason is worth more than several blind attempts.
Our leading theory at that point was a subtler version of the first problem. One of the URLs we'd cited as an opt-in location showed the descriptive disclosure when it loaded, but the actual consent checkbox still sat several steps into the booking flow. A reviewer landing on that URL could read about consent without being able to see the mechanism that collects it.
Twilio's own guidance for 30909 points at the same remedy: if opt-in happens on a website, the URL has to be publicly accessible — and if it isn't, provide a URL with hosted screenshots of the opt-in. For any consent step behind a login or deep in a multi-step flow, screenshots are the honest way to show a reviewer what they can't reach.
A CTA a reviewer can actually verify
This combines the published requirements with what the two rejections taught us. Treat the first three as the ones most likely to be quietly missing.
| Check | Why it matters |
|---|---|
| The message flow names every opt-in path — website, keyword, paper, verbal, QR | Reviewers verify the paths you declare. An undeclared path is an unverifiable one. |
| Each cited opt-in URL is public, with no login — or you supply hosted screenshots | A reviewer can't verify a page they can't open. |
| The consent mechanism itself is visible where the reviewer lands, not steps deep | Describing consent isn't the same as showing where it's collected. |
| The disclosure is in the server-rendered HTML, not injected by JavaScript | A fetch that doesn't run scripts sees nothing. |
| Brand name and what messages people will receive | The reviewer checks the disclosure matches the campaign. |
| Message frequency | A standard required disclosure. |
| "Message and data rates may apply" | A standard required disclosure. |
| STOP to opt out and HELP for help | Both are required. |
| Links to your privacy policy and your terms | Both are expected in the flow. |
| Privacy policy states mobile numbers aren't shared with third parties | Commonly checked; easy to miss in a generic policy. |
After approval: why your first text can still fail with 40010
The campaign was eventually approved — status MNO_PROVISIONED, with T-Mobile now
registered. We attached the sending number, sent a test message, and it failed.
On Telnyx the error was 40010: the sending number is not attached to a 10DLC
campaign. It was attached — the API had accepted the assignment — but the assignment
was still PENDING_ASSIGNMENT while the number-to-campaign mapping propagated to the
carriers. Telnyx documents that assignment can take anywhere from minutes to days, with
around two hours being normal.
It cleared on its own. Once the assignment showed ASSIGNED, the same test message was
delivered. If you hit 40010 in the first hours after attaching a number to a newly
approved campaign, check the assignment status before assuming something else is wrong.
Since February 2025, unregistered 10DLC traffic isn't just filtered — it's blocked, so a
number sending before its assignment completes will fail outright.
What we'd tell ourselves at the start
- 806 is a category, not a diagnosis. Fixing one cause doesn't mean the next rejection has the same one.
- Registry acceptance isn't carrier approval. Watch the per-carrier status, not just the campaign status.
- Make verification effortless. Put the disclosure and the consent mechanism where a reviewer lands, in plain HTML, at a public URL.
- Test reachability with something that isn't your browser. It takes a minute and rules out an entire class of cause.
- Get the specific reason before resubmitting. A generic code plus a guess costs days per attempt.
We can't honestly attribute the final approval to one single change, because we made several and the rejection code never became more specific. What we can say is that each change made the opt-in easier to verify, and that the approach above got the campaign through.
If you'd rather not run this loop yourself, intSignal handles 10DLC registration and rejection remediation as part of managed business texting — where there's also a free four-step checker for spotting these gaps before you submit.
Frequently asked
Is error 806 the same as Twilio error 30909?
They describe the same failure: the reviewer couldn't verify how people consent to receive your messages. 806 is the rejection code carried with the campaign; 30909 is how Twilio surfaces that category of rejection. The fixes are the same.
How long does a 10DLC resubmission take?
Each resubmission restarts review, which in our experience took days rather than hours. Ours went from first rejection to approval in roughly seven weeks across multiple review cycles. That's why getting the specific rejection reason before resubmitting matters.
My campaign was accepted, then rejected. How?
Acceptance by The Campaign Registry and approval by each mobile carrier are separate. A campaign can clear the registry and still be rejected by an individual carrier's review. Check your provider's per-carrier registration status to see which one.
Why does my approved campaign still fail with error 40010?
On Telnyx, 40010 means the sending number isn't attached to a 10DLC campaign yet. Right after attaching a number to an approved campaign, the assignment is usually still propagating to carriers. Wait for the assignment status to show assigned, then retest.


