> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vivi.bot/llms.txt
> Use this file to discover all available pages before exploring further.

# Twilio Errors

This page contains reference material to assist you with troubleshooting Twilio rejection reasons, error codes, and good practices (opt-in collection, double opt-in, STOP/HELP handling) that affect whether your submission gets approved in the first place.

<CardGroup cols={3}>
  <Card title="Approval Patterns" color="#2563eb" icon="compass" href="#approval-patterns">
    How to design your messaging program so it passes carrier review.
  </Card>

  <Card title="Common Rejection Reasons" color="#2563eb" icon="ban" href="#common-rejection-reasons">
    The exact text strings Twilio returns and how to fix each.
  </Card>

  <Card title="Error Code Reference" color="#2563eb" icon="hashtag" href="#error-code-reference">
    The numeric `30xxx` codes you'll see in `failureReason`.
  </Card>
</CardGroup>

***

## Approval Patterns

### Consent

Gathering user opt-in consent is the most critical portion of the approval flow. There are a few ways to gather user consent that are approved by Twilio:

<Tabs>
  <Tab title="Web Form">
    A checkbox next to a phone field. The form discloses what the recipient will receive, who's sending, that msg & data rates apply, and how to opt out. The checkbox **must not be pre-checked**. You retain a record (timestamp, IP, form version).
  </Tab>

  <Tab title="In-Person / Verbal">
    A point-of-sale clerk asks at checkout; a service tech asks during a job intake. The record goes into your CRM with a timestamp and the staff member's ID.
  </Tab>

  <Tab title="Reply-In (Text-to-Join)">
    The contact texts a keyword (`JOIN`, `START`) to your number. This is its own opt-in.
  </Tab>
</Tabs>

<Warning>
  **Single-source rule of thumb:** if you can't show a Twilio reviewer **where** consent was collected (URL of the form, photo of the in-person clipboard, or the inbound `JOIN` text), don't include those contacts in your audience.
</Warning>

### Opt-In Strategies

<Tabs>
  <Tab title="Double Opt-In (Safest Default)">
    After the recipient consents via your collection source, VIVI's consent middleware sends a confirmation message ("Reply YES to confirm you want to receive..."). The recipient must reply `YES` (or `START`) before they're marked `OPTED_IN`. This is the default VIVI behavior.

    <CardGroup cols={1}>
      <Card title="Why?" color="#2563eb" icon="circle-check">
        Pre-empts disputes ("I never signed up"), reduces complaint rates, and is required for marketing campaigns in some jurisdictions.
      </Card>

      <Card title="How to Format" color="#2563eb" icon="eye">
        One extra reply at the start of the conversation. Most users do it.
      </Card>

      <Card title="Fallback" color="#2563eb" icon="triangle-exclamation">
        If the user never replies, the conversation never happens. The middleware queues your agent's first message until consent lands — nothing is lost on your side, but the user-side opt-in might just never occur.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="Single Opt-In">
    For purely **transactional** use cases (delivery notifications, appointment reminders, 2FA codes) on a TFV-verified or A2P-approved number, single opt-in is acceptable in most jurisdictions. The middleware still respects STOP/HELP, but doesn't require the user to text YES back before the first message goes out.

    <Note>
      Single opt-in is configured by setting `optInType = "VERBAL"` or `"WEB_FORM"` on the channel and providing proof of consent at TFV/A2P submission time. Carriers expect you to have the actual records on file.
    </Note>
  </Tab>
</Tabs>

### STOP and HELP

Every CTIA-compliant SMS program must respond to:

<CardGroup cols={2}>
  <Card title="STOP" color="#2563eb" icon="hand">
    Aliases: `UNSUBSCRIBE`, `END`, `CANCEL`, `QUIT` — immediate opt-out.
  </Card>

  <Card title="HELP" color="#2563eb" icon="circle-question">
    Alias: `INFO` — return identifying information about the program and how to contact support.
  </Card>
</CardGroup>

<Info>
  VIVI handles this automatically via the consent middleware. The opt-out and help-response templates you configured in the [SMS channel setup](sms-channel.md#consent-middleware) are what get sent. Your agent does not see STOP/HELP messages — the middleware intercepts them.
</Info>

### Re-Engagement After STOP

<Warning>
  Once a user has texted STOP, you cannot legally text them again for that program — even if a year passes. The only way they can re-enter the program is to text `START` or `YES` themselves, which flips them to `OPTED_IN` and triggers the **resubscribe confirmation** template.

  You cannot "convert" a STOPped user back via your web form, even if they fill it out again.
</Warning>

### Best Practices Summary

A summary that passes review hits four points:

<Steps>
  <Step title="Who is sending?">
    Your brand name.
  </Step>

  <Step title="Who is receiving?">
    A specific audience, not "the public". E.g. "the franchise's own staff and on-call technicians".
  </Step>

  <Step title="What kind of message?">
    Transactional / customer care / 2FA / etc., explicitly named.
  </Step>

  <Step title="How consent was collected?">
    The source of consent, i.e. "Recipients opt in by texting JOIN to our number." or "Recipients opt in on our online intake form."
  </Step>
</Steps>

Add the following template to make unsubsciption clear: msg & data rates may apply, STOP to unsubscribe, HELP for help.

<Accordion title="Passing Example" icon="circle-check">
  KSG operates the VIVI messaging platform for a restoration franchise. When the franchise's 24/7 line logs an emergency restoration job, KSG texts the franchise's staff and on-call technicians from this toll-free number that a dispatch is ready. Recipients reply OK to confirm; unacknowledged alerts re-send until confirmed. Internal transactional dispatch alerts only — no marketing. Technicians opt in by texting to join. Reply STOP to opt out, HELP for help.
</Accordion>

<Accordion title="Rejected Examples" icon="xmark">
  | Bad pattern                                                       | Why it gets rejected                                                                                                               |
  | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
  | "We send SMS to customers."                                       | Too vague. No use case, no consent source, no brand.                                                                               |
  | "Marketing messages about our weekly specials."                   | "Marketing" requires a Marketing-category submission with stricter consent records. Either declare it properly or remove the word. |
  | "...visit [https://example.com](https://example.com) for details" | URLs in the summary itself are a red flag. Put the URL in the **business website** field instead.                                  |
  | 800-character version of the example above                        | Twilio caps this field at 500 chars and returns the opaque `Invalid use case summary` on overage.                                  |
</Accordion>

### Sample Messages

The **sample messages** field on A2P campaigns and the **production message sample** field on TFV both require a **realistic example of what your agent will send in production**, not a template or placeholder.

| Don't                                      | Do                                                                                   |
| ------------------------------------------ | ------------------------------------------------------------------------------------ |
| `Hi [Name], your appointment is at [Time]` | `Hi Jane, your appointment is confirmed for Tue Jul 2 at 9:00am. Reply C to cancel.` |
| `Your code is XXXXXX`                      | `Your KSG verification code is 749281. Code expires in 5 minutes. Do not share.`     |
| `Reply Y for offers`                       | This is marketing on a transactional channel and will be rejected                    |

<Warning>
  Carriers and Twilio reviewers will flag a sample that contains `[brackets]` as "template-style" and reject the submission.
</Warning>

***

## Common Rejection Reasons

These are the exact strings you'll see in `failureReason` (or, for live channels, in the rejection banner on the form), grouped by what VIVI receives back from Twilio.

<AccordionGroup>
  <Accordion title="&#x22;Invalid use case summary&#x22;" icon="triangle-exclamation">
    **Where you see it:** Toll-free verification rejection banner.

    1. **Length over 500 characters.** Twilio's hard length cap. The VIVI form now blocks typing past 500 with a live counter; if you're seeing this error from a pre-existing submission, the row has stale data over cap. Open the form, shorten it, and save.
    2. **Summary doesn't match `useCaseCategories`.** Categories say `TWO_FACTOR_AUTHENTICATION` but the summary describes appointment reminders, or vice versa.
    3. **URLs, emails, or phone numbers in the summary.** Put URLs in `business_website` / `privacy_policy_url`. Don't put a contact phone in the summary.
    4. **Marketing language on a non-MARKETING category.** Words like "promotions", "offers", "deals", "sale" combined with `ACCOUNT_NOTIFICATIONS` or `2FA` triggers it.
    5. **Placeholder text** — "test", "TBD", "N/A", a category name copied verbatim.
    6. **Prohibited content keywords** — cannabis, CBD, firearms, gambling, payday/debt collection in the content.

    **Fix:** rewrite using the [example pattern above](#best-practices-summary).
  </Accordion>

  <Accordion title="&#x22;Verification already exists for this phone number&#x22;" icon="clone">
    **Where you see it:** Backend log error. VIVI catches this and prevents it from reaching the user.

    **Root cause:** the form was saved while a TFV is already on file for this phone number's SID. Twilio enforces one active verification per phone number SID.

    **VIVI's behavior:** the backend pre-flights Twilio's status before submitting:

    * If the existing verification is `PENDING_REVIEW` / `DRAFT` / `TWILIO_REJECTED` → VIVI patches it with the new values (preserves audit trail).
    * If it's `IN_REVIEW` / `TWILIO_APPROVED` → VIVI blocks the save with a clear 409 and a "fields locked" toast.
    * If a race produces this error despite the pre-flight, VIVI recovers by refreshing from Twilio's truth instead of writing a spurious local "failed" state.

    <Note>
      If you see this in old logs, those rows will self-heal on the next save.
    </Note>
  </Accordion>

  <Accordion title="&#x22;Address is invalid&#x22; / &#x22;Could not verify business address&#x22;" icon="location-dot">
    **Root cause:** the physical address on the brand or TFV form failed Twilio's address validator. Usually:

    * PO box (not allowed for brand or TFV business address).
    * Typo in street name.
    * Mismatched ZIP / state (e.g. `94105` paired with `CA` is fine, but `94105` paired with `TX` is rejected).
    * Building / suite number stuck on the street line without a separator.

    **Fix:** copy the address from a USPS lookup or Google Maps and paste in. Use the address line 2 field for suite/apartment.
  </Accordion>

  <Accordion title="&#x22;EIN not found&#x22; / &#x22;Business registration not verifiable&#x22;" icon="id-card">
    **Root cause:** the IRS database doesn't show your EIN as active, or the legal name doesn't exactly match what's on file with the IRS.

    **Fix:**

    * Verify the EIN at the IRS's [EIN online verification](https://www.irs.gov/businesses/small-businesses-self-employed/employer-id-numbers).
    * The **legal name** field must match the EIN paperwork *exactly* — including LLC/Inc/Corp suffixes and capitalisation. If your IRS letter says "Acme Restoration LLC", `Acme Restoration` (no LLC) will fail.
  </Accordion>

  <Accordion title="Sample message contains URLs but embedded links is off" icon="link">
    **Root cause:** you toggled **Embedded links** off but a sample message contains `http://` or `https://`. Carriers reject samples that contain URLs when embedded links isn't declared.

    **Fix:** either toggle Embedded links on (and accept the tougher review), or remove the URL from the sample.
  </Accordion>

  <Accordion title="Sample message contains template placeholders" icon="code">
    **Root cause:** you pasted a sample such as `[Hi [Name], your appointment is at [Time]`. Carriers reject samples with `[bracketed]` placeholders.

    **Fix:** render the placeholders before pasting. Submit a realistic example with real names and times.
  </Accordion>

  <Accordion title="&#x22;Carrier rejected campaign&#x22; / generic carrier rejection on A2P" icon="tower-broadcast">
    **Where you see it:** A2P campaign row flips to `FAILED` and the `failureReason` field shows a carrier-supplied reason (often vague — "did not meet messaging guidelines").

    **Common root causes:**

    * **Use case category mismatch** — you picked `CUSTOMER_CARE` but the sample messages look like marketing.
    * **Insufficient opt-in evidence** — the opt-in description doesn't name the consent source.
    * **Embedded links to an unverified domain** — your privacy policy URL points at a domain different from your business website with no clear connection.

    **Fix:** read the carrier reason carefully (which carrier? AT\&T, T-Mobile, Verizon have different sensitivities), revise the relevant fields, click **Resubmit**. VIVI patches the existing campaign; you don't lose your slot.
  </Accordion>
</AccordionGroup>

***

## Error Code Reference

Twilio surfaces numeric error codes for SMS-platform errors. The most common ones you'll see in VIVI's logs or rejection banners:

| Code      | Meaning                                        | What to do                                                                                                                                        |
| --------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **30001** | Queue overflow                                 | Your sending rate spiked above your account's queue depth. Slow down outbound bursts or upgrade Twilio capacity.                                  |
| **30002** | Account suspended                              | Twilio admin action. Open a Twilio support ticket.                                                                                                |
| **30003** | Unreachable destination handset                | The recipient's phone is off / out of coverage. Will retry briefly then permanent-fail.                                                           |
| **30004** | Message blocked                                | Carrier blocked the message. Often due to content (URL, prohibited keyword).                                                                      |
| **30005** | Unknown destination handset                    | Number is unallocated or no longer in service. Stop trying.                                                                                       |
| **30006** | Landline / unreachable carrier                 | Recipient is on a landline (no SMS support) or a small carrier Twilio can't reach.                                                                |
| **30007** | Carrier violation                              | Carrier filtered the message. Content violated policy. VIVI surfaces the carrier's reason in `failureReason`.                                     |
| **30008** | Unknown error                                  | Generic. Read `failureReason`.                                                                                                                    |
| **30032** | Toll-free number not yet verified              | You tried to send before TFV review completed. Submit the TFV form, wait for approval.                                                            |
| **30034** | A2P campaign not registered                    | You tried to send from a local 10DLC number whose A2P campaign isn't `APPROVED`. Finish A2P registration first.                                   |
| **30037** | Permanent carrier failure                      | The carrier won't deliver, period. Often a spam-filter decision. Review content, opt-in evidence, and (if necessary) escalate via Twilio support. |
| **30445** | Toll-free verification rejected                | Your TFV submission was rejected. Open the channel, read the rejection reason, fix, resubmit.                                                     |
| **30450** | Toll-free verification update rejected         | Your *update* to a TFV was rejected (e.g. PATCH from a resubmit). Same fix flow.                                                                  |
| **21610** | Recipient has unsubscribed (STOP)              | The recipient previously texted STOP. VIVI's middleware catches this and flips the state to `OPTED_OUT`. Your agent shouldn't try to send again.  |
| **21614** | 'To' phone number is not a valid mobile number | Bad number (landline, malformed). Check the recipient list.                                                                                       |

### Reading a Twilio Rejection in Detail

When a TFV is rejected, the `failureReason` field on `toll_free_channel_compliance` will look something like:

```text theme={null}
30445: Toll-free Verification rejected. Use Case Information / Message Volume.
The estimated monthly volume does not match the use case described.
```

<Steps>
  <Step title="Numeric code">
    `30445` maps to the Twilio docs URL: `https://www.twilio.com/docs/api/errors/30445`.
  </Step>

  <Step title="Rejection reason category">
    `Use Case Information / Message Volume` tells you which form section to focus on.
  </Step>

  <Step title="Specific note">
    "estimated monthly volume does not match..." is the actual feedback. This is what you act on.
  </Step>
</Steps>

***

## When to Escalate

Most rejections are self-serve — fix the form, resubmit, wait. Escalate to Twilio support directly when:

<CardGroup cols={1}>
  <Card title="Stalled review" color="#2563eb" icon="clock">
    A campaign or TFV has been in `IN_REVIEW` for more than **5 business days** with no movement.
  </Card>

  <Card title="Permanent carrier failure on an approved number" color="#2563eb" icon="circle-exclamation">
    You're getting **30037 (Permanent carrier failure)** on a verified, A2P-approved number — only Twilio can negotiate with the carrier.
  </Card>

  <Card title="Suspected throughput throttling" color="#2563eb" icon="gauge">
    You suspect your **brand trust score** is artificially low and your throughput is throttled. Brand trust appeals go through Twilio support.
  </Card>
</CardGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="SMS Channel Creation" color="#2563eb" icon="comment-sms" href="sms-channel.md">
    The channel-level flow that triggers compliance.
  </Card>

  <Card title="Compliance Hub Overview" color="#2563eb" icon="clipboard-check" href="compliance.md">
    Where you submit and track A2P and TFV registrations.
  </Card>
</CardGroup>

* Twilio's canonical error code reference: [twilio.com/docs/api/errors](https://www.twilio.com/docs/api/errors)
* Twilio's TFV documentation: [twilio.com/docs/messaging/compliance/toll-free-verification](https://www.twilio.com/docs/messaging/compliance/toll-free-verification)
* Twilio's A2P 10DLC documentation: [twilio.com/docs/messaging/compliance/a2p-10dlc](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc)
