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

# Apply for an SMS short code

> Open a short-code application on Orbit, write a compliant program brief, lint it with the pre-flight checker before submission, and track the multi-week carrier vetting through to a provisioned lease.

# Apply for an SMS short code

A short code is a 3–6 digit number (e.g. `25735`) your subscribers can text and receive texts from. Short codes carry the highest SMS throughput of any sender type and are the most memorable sender you can put in a CTA. The trade-off: a longer, explicitly-vetted acquisition process than 10DLC or toll-free.

This guide covers the full lifecycle on Orbit: opening the application, writing the program brief, linting it with the pre-flight checker, tracking carrier vetting, and sending once the lease is provisioned.

***

## Short code vs 10DLC vs toll-free

Pick the sender type that matches the program:

| Sender type | When it fits                                                                                     | Throughput                                        | Cost                                       | Approval time                                                       |
| ----------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------- |
| Short code  | High-volume national programs (alerts, marketing, 2FA) where recipients must remember the sender | Highest per-sender throughput carriers grant      | Lease billed per term (3, 6, or 12 months) | Weeks of carrier vetting                                            |
| 10DLC       | Standard US A2P traffic on 10-digit local numbers                                                | Per-day, per-number cap set by brand vetting tier | Lowest ongoing cost                        | Days (see [10DLC registration](/guides/10dlc-registration))         |
| Toll-free   | Lower-volume US/Canada traffic, support lines                                                    | Moderate                                          | Low                                        | Days (see [Toll-free verification](/guides/toll-free-verification)) |

Choose a short code when throughput and sender memorability justify the lease cost and the vetting lead time. If a 10DLC campaign at your current vetting tier meets the volume, 10DLC is faster to live.

***

## Application lifecycle

A short-code application moves through five statuses:

`draft` → `submitted` → `carrier_vetting` → `provisioned` (or `rejected` / `cancelled`)

* **draft** — the application exists on Orbit but nothing has gone to a carrier. The program brief is still editable.
* **submitted** — handed to the aggregator/CSCA path. The brief is locked from this point.
* **carrier\_vetting** — each carrier (AT\&T, T-Mobile, Verizon, US Cellular) reviews the program independently. This runs for multiple weeks.
* **provisioned** — approved and live; the lease window is open and the code can send.
* **rejected** / **cancelled** — terminal states. A rejection carries the aggregator or carrier reason; fix the brief and open a new application.

### Step 1: Open the application

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/short-codes \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "countryCode": "US",
    "leaseType": "dedicated",
    "selection": "vanity",
    "requestedCode": "25735",
    "leaseTermMonths": 12,
    "businessName": "Acme",
    "programBrief": {
      "useCase": "Order delivery notifications",
      "description": "Acme sends order confirmations, shipping updates, and delivery alerts to customers who opt in during checkout on acme.com. Messages are transactional and tied to a real order.",
      "sampleMessages": [
        "Acme: Your order #12345 has shipped. Track it at acme.com/track/12345. Reply STOP to opt out.",
        "Acme: Your delivery arrives today between 2-4pm. Reply HELP for help or STOP to opt out."
      ],
      "messageFrequency": "Up to 5 msgs per order",
      "optInDescription": "Customers enter their mobile number at checkout on acme.com and check the SMS consent box. Recurring messages, up to 5 msgs per order. Message and data rates may apply. Reply STOP to cancel. Reply HELP for help.",
      "supportContact": "support@acme.com",
      "privacyPolicyUrl": "https://acme.com/privacy",
      "termsUrl": "https://acme.com/sms-terms"
    }
  }'
```

Field rules:

* `leaseType` — `dedicated` (your brand only) or `shared` (lower cost, sender identity pooled).
* `selection` — `random` (aggregator assigns) or `vanity`. A `vanity` selection requires `requestedCode`, a 3–6 digit string.
* `leaseTermMonths` — one of `3`, `6`, or `12`.
* `programBrief.sampleMessages` — 1–5 messages, each up to 1600 characters. Provide 2–5; a single sample measurably lowers the first-pass approval rate.
* `programBrief.description` and `programBrief.optInDescription` must each be at least 10 characters; carriers reject briefs too short to vet (target 150–300 characters for the description).

**Response (`201 Created`):**

```json theme={null}
{
  "data": {
    "id": "sca_9f3k2",
    "status": "draft",
    "countryCode": "US",
    "leaseType": "dedicated",
    "selection": "vanity",
    "requestedCode": "25735",
    "leaseTermMonths": 12,
    "businessName": "Acme"
  },
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2026-08-28T12:00:00Z"
  }
}
```

While the application is a `draft`, revise the brief any time:

```bash theme={null}
curl -X PATCH https://api.orbit.devotel.io/api/v1/numbers/short-codes/sca_9f3k2/brief \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "programBrief": { "useCase": "...", "...": "..." } }'
```

List every application on the tenant with `GET /api/v1/numbers/short-codes`, or fetch one with `GET /api/v1/numbers/short-codes/:id`. Cancel a draft or relinquish a lease with `DELETE /api/v1/numbers/short-codes/:id`.

***

## Lint the program brief before you submit

Carriers reject roughly a third of first-round program briefs on deterministic grounds — SHAFT-C content, ambiguous CTAs, missing opt-out or HELP wording — and every rejection restarts the multi-week queue. Run the pre-flight linter before submitting so the deterministic failures surface in seconds, not weeks.

Score a brief ad-hoc, before any application exists:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/short-codes/preflight \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "businessName": "Acme",
    "programBrief": {
      "useCase": "Promos",
      "description": "Deals.",
      "sampleMessages": ["Great deals, click here: bit.ly/acme-deals"],
      "messageFrequency": "",
      "optInDescription": "",
      "supportContact": "support@acme.com"
    }
  }'
```

Or lint the brief already stored on a draft application:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/short-codes/sca_9f3k2/preflight \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

### Worked example: a failing brief

The ad-hoc call above returns a `block` verdict — the brief would be rejected at vetting:

```json theme={null}
{
  "data": {
    "score": 34,
    "verdict": "block",
    "findings": [
      {
        "ruleId": "R-SC-SAMPLE-SHORTENER",
        "severity": "error",
        "field": "programBrief.sampleMessages[0]",
        "message": "URL shortener \"bit.ly\" detected. Carriers auto-reject public shorteners — links must be on the program's own domain.",
        "match": "bit.ly",
        "suggestion": "Replace the public shortener with a branded-domain link (e.g. links.yourbrand.com/...)."
      },
      {
        "ruleId": "R-SC-SAMPLE-OPTOUT",
        "severity": "error",
        "field": "programBrief.sampleMessages",
        "message": "No sample message includes opt-out language. Carriers reject programs whose samples never show how a subscriber stops messages.",
        "suggestion": "Add \"Reply STOP to opt out.\" to at least one sample message."
      },
      {
        "ruleId": "R-SC-DESC-LEN",
        "severity": "error",
        "field": "programBrief.description",
        "message": "Program description is too short to vet. Carriers reject briefs that don't explain who consents and what messages send.",
        "suggestion": "Describe (1) who opts in, (2) what messages the program sends, (3) the business value. ~150-300 characters is the approval sweet spot."
      },
      {
        "ruleId": "R-SC-OPTIN-LEN",
        "severity": "error",
        "field": "programBrief.optInDescription",
        "message": "Opt-in description is too short. The consent flow (where and how the subscriber agrees to messages) is the single most-vetted field.",
        "suggestion": "Describe the exact opt-in moment, e.g. \"Subscriber enters their mobile number on yourbrand.com/join and checks the SMS consent box.\""
      },
      {
        "ruleId": "R-SC-SAMPLE-CLICK-HERE",
        "severity": "warn",
        "field": "programBrief.sampleMessages[0]",
        "message": "Ambiguous call-to-action: \"click here\" is flagged by carrier filters. Name the destination instead.",
        "match": "click here",
        "suggestion": "Replace \"click here\" with the destination noun (\"view your order\", \"open the form\")."
      },
      {
        "ruleId": "R-SC-SAMPLE-BRAND",
        "severity": "warn",
        "field": "programBrief.sampleMessages",
        "message": "No sample message identifies the program brand. Carriers cite brand ambiguity (recipient can't tell who is texting) as a top rejection reason.",
        "match": "Acme",
        "suggestion": "Include the program / brand name in at least one sample so recipients recognise the sender."
      },
      {
        "ruleId": "R-SC-FREQ-MISSING",
        "severity": "warn",
        "field": "programBrief.messageFrequency",
        "message": "Message frequency is blank. Carriers require a declared frequency for recurring short-code programs.",
        "suggestion": "State the expected cadence, e.g. \"Up to 5 msgs/week\" or \"Msg frequency varies\"."
      }
    ],
    "engine": "shortcode-preflight/v1"
  }
}
```

Each finding names the field, quotes the offending fragment in `match`, and gives the rewrite that resolves it. Fix the brief:

* Replace the shortener with a link on your own domain.
* Add `Reply STOP to opt out.` to at least one sample.
* Expand the description to explain who opts in and what the program sends.
* Write out the opt-in moment — where the subscriber enters their number and agrees.
* Name the destination instead of "click here".
* Put the brand name in a sample so recipients recognise the sender.
* Declare the frequency.

Re-run the pre-flight. A clean brief returns:

```json theme={null}
{
  "data": {
    "score": 98,
    "verdict": "pass",
    "findings": [],
    "engine": "shortcode-preflight/v1"
  }
}
```

### What the linter checks

The pre-flight scores against the carrier rejection-pattern catalog drawn from the CTIA Short Code Monitoring Handbook:

| Rule                            | Severity    | What it catches                                                                                         |
| ------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------- |
| SHAFT-C content                 | error       | Sex, hate, alcohol, firearms, tobacco, cannabis promotional content in samples — auto-reject categories |
| Carrier content blocklist       | error       | Phrases on the shared carrier content-filter list                                                       |
| Public URL shorteners           | error       | bit.ly, t.co, tinyurl, and similar domains in samples — links must live on your own domain              |
| Missing opt-out language        | error       | No sample shows STOP / UNSUBSCRIBE / equivalent                                                         |
| Description or opt-in too short | error       | Either field too short for a carrier to vet                                                             |
| Ambiguous CTA                   | warn        | "click here" and similarly unnamed destinations                                                         |
| Missing brand identifier        | warn        | The business name appears in no sample                                                                  |
| CTA disclosures                 | warn / info | Missing message-frequency, "Message and data rates may apply", STOP, or HELP wording in the opt-in CTA  |
| Sample count                    | warn        | Fewer than 2 samples                                                                                    |
| Privacy / Terms links           | warn        | No public Privacy Policy or Terms & Conditions URL                                                      |

The score runs 0–100 (each error deducts 25, each warn 8, each info 2). The verdict is `block` when any error-severity finding is present, `warn` below a score of 75, otherwise `pass`.

<Warning>
  A `pass` verdict means "no known rejection pattern matched" — it is not a carrier approval guarantee. The program still goes through the normal submission and per-carrier vetting.
</Warning>

### Submit

With a passing brief, submit the application:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/short-codes/sca_9f3k2/submit \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

The status moves to `submitted` and the brief locks.

***

## Carrier vetting: the multi-week wait

After submission the application enters `carrier_vetting`. Each carrier reviews the program independently — Verizon and AT\&T are typically the long poles — and the end-to-end review runs multiple weeks. There is nothing to action during the wait unless a rejection comes back.

Track progress two ways:

* `GET /api/v1/numbers/short-codes/:id` — the current `status` plus a per-carrier vetting snapshot (each carrier `pending`, `approved`, or `rejected`).
* `GET /api/v1/numbers/short-codes/:id/timeline` — a structured per-stage timeline (draft, submitted, carrier vetting, provisioned) with the reached stages, per-carrier progress, and the lease renew-by date once provisioned. This is the feed the dashboard detail panel renders.

If a carrier rejects the program, the application lands in `rejected` with the aggregator/carrier `rejectionReason`. Read the reason, open a new application, fix the brief against the pre-flight linter, and resubmit.

Once the carriers approve, an operator records the vetting outcome and provisions the lease; the application moves to `provisioned` with the final `assignedCode`, a `provisionedAt` timestamp, and a `leaseEndsAt` renew-by date (provisioned date plus the lease term).

***

## Sending on a short code

No extra configuration is needed after provisioning. Orbit's sender resolution already recognises short codes as a sender type — pass the assigned code as the `from` on a send and it routes down the numeric path, same as a long code. See [Sender resolution](/concepts/sender-resolution) for how `from` values are validated and routed, and [SMS](/channels/sms) for the send endpoints themselves.

Inbound keywords still apply: Orbit processes STOP, CANCEL, and UNSUBSCRIBE replies automatically, so opt-out handling from your approved brief holds with no further setup.

***

## Audit events

Every lifecycle transition writes to the tenant audit log against the `short_code_application` resource:

| Event                              | When                                |
| ---------------------------------- | ----------------------------------- |
| `short_code.application_created`   | Application opened (draft)          |
| `short_code.brief_updated`         | Program brief edited while in draft |
| `short_code.application_submitted` | Submitted to the aggregator path    |
| `short_code.vetting_recorded`      | A carrier vetting outcome recorded  |
| `short_code.provisioned`           | Lease opened with the assigned code |
| `short_code.application_rejected`  | Rejected, with the reason           |
| `short_code.application_cancelled` | Cancelled or lease relinquished     |

Query them in the dashboard audit log or over the audit API — see the [audit log guide](/guides/audit-log).

***

## How this differs from 10DLC

If you have run the [10DLC registration](/guides/10dlc-registration) flow, the shape is familiar — both are "register the program, wait for review, then send" — with three practical differences:

1. **Per-carrier, multi-week vetting.** 10DLC maps to a single registry decision plus per-carrier statuses; a short code goes through independent carrier reviews that take weeks, tracked per carrier.
2. **A lease, not a registration.** Approval opens a time-boxed lease (3, 6, or 12 months) with a renew-by date; 10DLC registration has no lease window.
3. **Rejection restarts the queue.** A rejected 10DLC campaign resubmits in days; a rejected short-code program returns to the back of a multi-week queue. The pre-flight linter exists to keep that loop from starting.
