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

# SMS registrations

> 10DLC brands and campaigns for local US numbers and toll-free registrations for US toll-free numbers, the carrier records a number needs to send SMS

US carriers require a compliance record before a number can send SMS. For a local number, the [organization](/concepts/credentials) registers as a brand with The Campaign Registry (TCR) through the carrier, registers each messaging use case as a campaign under that brand, and links the number to one campaign; a US toll-free number instead carries its own toll-free registration. These routes create, read, update, link, and sync those records. Statuses come from the carrier and a `sync` route refreshes them; a rule marked "carrier" is enforced by the carrier, which answers `400` with its own message. None of these routes has a CLI equivalent. Buying the numbers themselves is on [Phone numbers](/reference/rest/phone-numbers); the end-to-end setup is [SMS and phone numbers](/channels/sms-and-phone-numbers).

*Verified against lua-cli 3.33.0.*

## Base URL and authentication

Every route sits under `https://api.heylua.ai/developer/orgs/:orgId`, takes `Authorization: Bearer <<YOUR_API_KEY>>`, and checks a `telephony` scope on the organization in the path: `telephony:read` reads records and events, `telephony:write` edits them, `telephony:manage` unlinks a number, and `telephony:provision` creates brands, campaigns, and registrations, syncs them, and links a number. `telephony:provision` is a sensitive scope: a wildcard grant never covers it, so a scoped key must hold it by name. `msisdn` path parameters are E.164 digits without the leading `+`. A field that fails validation answers `400` with `message` as an array of the failed rules. Keys, scopes, and the error envelope are on the [REST API overview](/reference/rest/overview).

## Brand object

<ResponseField name="id" type="string">Brand ID; use it on the plural routes.</ResponseField>
<ResponseField name="orgId" type="string">Owning organization.</ResponseField>
<ResponseField name="vonageBrandId" type="string">Carrier-side brand ID, once registered.</ResponseField>
<ResponseField name="brandStatus" type="string">Carrier status: `UNVERIFIED` (initial), `VERIFIED`, `VETTED_VERIFIED`, `ACTIVE`, `INACTIVE`, `SUSPENDED`, or `DELETED`.</ResponseField>
<ResponseField name="entity_type … verification_email" type="string">The submitted fields, under their request names.</ResponseField>
<ResponseField name="brandRelationship, … , verifiedDate" type="mixed">Carrier-side fields (vetting, account, and date stamps), refreshed by `sync`.</ResponseField>
<ResponseField name="lastSyncedAt, createdAt, updatedAt" type="string">Timestamps.</ResponseField>

## Campaign object

<ResponseField name="id" type="string">Campaign ID; use it on the plural routes and when linking a number.</ResponseField>
<ResponseField name="orgId" type="string">Owning organization.</ResponseField>
<ResponseField name="brandId" type="string">Brand the campaign belongs to.</ResponseField>
<ResponseField name="vonageBrandId, vonageCampaignId, tcrCampaignId" type="string">Carrier-side and TCR IDs, once registered.</ResponseField>
<ResponseField name="campaignStatus" type="string">Carrier status: `ACTIVE`, `EXPIRED`, `DEACTIVATED`, `SUSPENDED`, or `DELETED`.</ResponseField>
<ResponseField name="usecase … vertical" type="mixed">The submitted fields, under their request names.</ResponseField>
<ResponseField name="tcrStatus, trafficEnabled, … , billedDate" type="mixed">Carrier-side fields (TCR status, traffic and renewal flags, keywords, and date stamps), refreshed by `sync`.</ResponseField>
<ResponseField name="lastSyncedAt, createdAt, updatedAt" type="string">Timestamps.</ResponseField>

## Registration object

<ResponseField name="id" type="string">Registration ID; use it on the `tfn-registrations` routes.</ResponseField>
<ResponseField name="orgId" type="string">Owning organization.</ResponseField>
<ResponseField name="msisdn" type="string">The toll-free number.</ResponseField>
<ResponseField name="vonageRegistrationId" type="string">Carrier-side registration ID, assigned at create, `DRAFT` included.</ResponseField>
<ResponseField name="submissionStatus" type="string">`DRAFT` or `SUBMITTED`.</ResponseField>
<ResponseField name="numberStatus" type="string">Carrier review status: `DRAFT`, `PENDING_REVIEW`, `CARRIERS_REVIEW`, `REGISTERED`, `REJECTED`, `UPDATES_REQUIRED`, or `BLOCKED`.</ResponseField>
<ResponseField name="rejectedReason" type="string">Set when `numberStatus` is `REJECTED`.</ResponseField>
<ResponseField name="business, useCase, messageContent, optIn" type="object">The submitted sections.</ResponseField>
<ResponseField name="estimatedMonthlyVolume, additionalInformation" type="string">The submitted values.</ResponseField>
<ResponseField name="lastSyncedAt, createdAt, updatedAt" type="string">Timestamps.</ResponseField>

## Endpoints

Brand routes come first, then campaigns, the number link, and toll-free registrations. The singular `/10dlc/brand` and `/10dlc/campaign` routes are deprecated aliases that resolve to the organization's most recent non-terminal brand or campaign; the plural routes address a record by ID and allow several per organization.

### POST /developer/orgs/:orgId/10dlc/brand

Registers the organization's brand with TCR through the carrier. Scope `telephony:provision`.

<ParamField body="entity_type" type="string" required>`SOLE_PROPRIETOR`, `PRIVATE_PROFIT`, `PUBLIC_PROFIT`, `NON_PROFIT`, or `GOVERNMENT`.</ParamField>
<ParamField body="display_name" type="string" required>Brand name shown to recipients; at most 100 characters.</ParamField>
<ParamField body="company_name" type="string" required>Legal company name; at most 100 characters.</ParamField>
<ParamField body="ein" type="string" required>EIN or tax ID; at most 50 characters.</ParamField>
<ParamField body="vertical" type="string" required>One of `REAL_ESTATE`, `HEALTHCARE`, `ENERGY`, `ENTERTAINMENT`, `RETAIL`, `AGRICULTURE`, `INSURANCE`, `EDUCATION`, `HOSPITALITY`, `FINANCIAL`, `GAMBLING`, `CONSTRUCTION`, `NGO`, `MANUFACTURING`, `GOVERNMENT`, `TECHNOLOGY`, `COMMUNICATION`.</ParamField>
<ParamField body="phone" type="string" required>Contact number in E.164 digits without `+`; at most 20 characters.</ParamField>
<ParamField body="email" type="string" required>Contact email; at most 100 characters.</ParamField>
<ParamField body="street, city" type="string" required>At most 100 characters each.</ParamField>
<ParamField body="state" type="string" required>At most 20 characters.</ParamField>
<ParamField body="postal_code" type="string" required>At most 10 characters.</ParamField>
<ParamField body="country" type="string" required>ISO 3166-1 alpha-2 code, 2 characters.</ParamField>
<ParamField body="website" type="string">URL; at most 100 characters.</ParamField>
<ParamField body="first_name, last_name, mobile_phone" type="string">Carrier: required for `SOLE_PROPRIETOR`. At most 50, 50, and 20 characters.</ParamField>
<ParamField body="stock_symbol, stock_exchange, verification_email" type="string">Carrier: required for `PUBLIC_PROFIT`. At most 10, 50, and 100 characters.</ParamField>

**Response**

`201` with the [brand object](#brand-object).

### GET /developer/orgs/:orgId/10dlc/brands

Lists the organization's brands as `Brand[]`. Scope `telephony:read`.

### GET /developer/orgs/:orgId/10dlc/brands/:brandId

Returns one [brand object](#brand-object), or `200` with an empty body when the ID is unknown. Scope `telephony:read`.

### PATCH /developer/orgs/:orgId/10dlc/brands/:brandId

Updates mutable brand fields at the carrier and locally. Scope `telephony:write`. Accepts any subset of the create body except `entity_type`. Answers `204`; `404` `10DLC brand not found` when the brand is unknown.

### POST /developer/orgs/:orgId/10dlc/brands/:brandId/sync

Refreshes the brand's status from the carrier and answers `200` with the updated [brand object](#brand-object). Scope `telephony:provision`.

### POST /developer/orgs/:orgId/10dlc/brands/:brandId/campaigns/sync-from-vonage

Imports the brand's campaigns from the carrier and upserts local records by carrier campaign ID; safe to call repeatedly. Scope `telephony:provision`. Answers `200` with `Campaign[]` for the brand; `404` `10DLC brand not found for this org or not linked to Vonage.` when the brand is unknown or not yet registered.

### GET /developer/orgs/:orgId/10dlc/brand

Deprecated singular alias of `GET …/10dlc/brands/:brandId`, resolved to the organization's most recent non-terminal brand. Answers the brand, or `200` with an empty body when there is none. Scope `telephony:read`.

### PATCH /developer/orgs/:orgId/10dlc/brand

Deprecated singular alias of `PATCH …/10dlc/brands/:brandId`, resolved to the most recent non-terminal brand. Answers `204` and is a no-op when there is none. Scope `telephony:write`.

### POST /developer/orgs/:orgId/10dlc/brand/sync

Deprecated singular alias of `POST …/10dlc/brands/:brandId/sync`, resolved to the most recent non-terminal brand. Answers the brand, or `200` with an empty body when there is none. Scope `telephony:provision`.

### POST /developer/orgs/:orgId/10dlc/campaign

Registers a messaging campaign under a brand. Scope `telephony:provision`. `404` `10DLC brand not found for this org. Create a brand first.` when no registered brand matches.

<ParamField body="brandId" type="string">Brand to register under. When omitted, the most recent non-terminal brand is used, even when the organization holds several.</ParamField>
<ParamField body="usecase" type="string" required>One of `CUSTOMER_CARE`, `CONVERSATIONAL`, `MARKETING`, `MIXED`, `LOW_VOLUME`, `2FA`, `ACCOUNT_NOTIFICATION`, `DELIVERY_NOTIFICATION`, `FRAUD_ALERT`, `SECURITY_ALERT`, `POLLING_VOTING`, `PUBLIC_SERVICE_ANNOUNCEMENT`, `HIGHER_EDUCATION`, `CHARITY`, `POLITICAL`, `EMERGENCY`, `SOCIAL`.</ParamField>
<ParamField body="sub_usecases" type="string[]">Sub-use cases for `MIXED` campaigns.</ParamField>
<ParamField body="description" type="string" required>What the campaign sends; 40 to 4,096 characters.</ParamField>
<ParamField body="sample_one" type="string" required>Sample message; 20 to 1,024 characters.</ParamField>
<ParamField body="sample_two, sample_three, sample_four, sample_five" type="string">Further samples; 20 to 1,024 characters each.</ParamField>
<ParamField body="opt_in_message, opt_out_message, help_message" type="string" required>Sent when a recipient subscribes, replies STOP, or replies HELP.</ParamField>

<ParamField body="message_flow_details" type="object" required>
  How recipients consent.

  <Expandable title="properties">
    <ParamField body="brand_name" type="string" required>Brand name as shown at opt-in.</ParamField>
    <ParamField body="consent_mechanisms" type="object[]" required>Each `{ method, details, attachment? }`; `method` is one of `ONLINE_FORM`, `LIVE_OPERATOR`, `POINT_OF_SALE`, `TEXT_TO_JOIN`, `OTHER`; `attachment` is a URL.</ParamField>
    <ParamField body="pricing_disclosure" type="boolean" required>Carrier: must be `true`; adds the "Message and Data rates may apply" disclosure.</ParamField>
    <ParamField body="carrier_disclaimer" type="boolean">Optional carrier disclaimer.</ParamField>
    <ParamField body="frequency" type="string" required>`ONE_TIME` or `RECURRING`.</ParamField>
    <ParamField body="privacy_policy, terms_and_conditions" type="string" required>URLs.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="age_gated, embedded_link, embedded_phone" type="boolean" default={false}>Content is age-restricted; messages contain links; messages contain phone numbers.</ParamField>
<ParamField body="tc_agreed" type="boolean" required>Carrier: must be `true` to accept the 10DLC terms.</ParamField>
<ParamField body="vertical" type="string">Overrides the brand vertical for this campaign; same values as the brand.</ParamField>

**Response**

`201` with the [campaign object](#campaign-object).

### GET /developer/orgs/:orgId/10dlc/campaigns

Lists the organization's campaigns as `Campaign[]`. Scope `telephony:read`.

### GET /developer/orgs/:orgId/10dlc/campaigns/:campaignId

Returns one [campaign object](#campaign-object), or `200` with an empty body when the ID is unknown. Scope `telephony:read`.

### PATCH /developer/orgs/:orgId/10dlc/campaigns/:campaignId

Updates mutable campaign fields at the carrier and locally: `description`, the samples, the three messages, `message_flow_details`, `age_gated`, `embedded_link`, `embedded_phone`. Scope `telephony:write`. Answers `204`; `404` `10DLC campaign not found` when the campaign is unknown.

### POST /developer/orgs/:orgId/10dlc/campaigns/:campaignId/sync

Refreshes the campaign's status from the carrier and answers `200` with the updated [campaign object](#campaign-object). Scope `telephony:provision`.

### GET /developer/orgs/:orgId/10dlc/campaign

Deprecated singular alias of `GET …/10dlc/campaigns/:campaignId`, resolved to the organization's most recent non-terminal campaign. Answers the campaign, or `200` with an empty body when there is none. Scope `telephony:read`.

### PATCH /developer/orgs/:orgId/10dlc/campaign

Deprecated singular alias of `PATCH …/10dlc/campaigns/:campaignId`, resolved to the most recent non-terminal campaign. Answers `204` and is a no-op when there is none. Scope `telephony:write`.

### POST /developer/orgs/:orgId/10dlc/campaign/sync

Deprecated singular alias of `POST …/10dlc/campaigns/:campaignId/sync`, resolved to the most recent non-terminal campaign. Answers the campaign, or `200` with an empty body when there is none. Scope `telephony:provision`.

### POST /developer/orgs/:orgId/phone-numbers/:msisdn/10dlc/link

Links a local US number to a campaign at the carrier. Scope `telephony:provision`. The body is optional.

<ParamField body="campaignId" type="string">Campaign to link. When omitted, the most recent non-terminal campaign is used, even when there are several.</ParamField>

**Response**

`200` with `{ linked: true, warnings: string[] }`. Warnings are non-blocking, for example that the number is not yet bound to an agent. `404` `10DLC campaign not found for this org` when no registered campaign matches. The number is not checked locally; a number the carrier does not hold is rejected by the carrier with its own message.

### DELETE /developer/orgs/:orgId/phone-numbers/:msisdn/10dlc/link

Removes the number's campaign link. Scope `telephony:manage`. Answers `204`; `404` `10DLC campaign not found for this org` when neither the number nor the organization has a campaign.

### POST /developer/orgs/:orgId/phone-numbers/:msisdn/tfn

Creates a registration for a toll-free number at the carrier: the business, use case, sample message, opt-in evidence, and expected volume for one US toll-free number. The carrier requires an approved registration before the number can send SMS. Save it as `DRAFT` to keep editing, or submit it immediately; the carrier then moves `numberStatus` through review. Scope `telephony:provision`. The number is not checked locally.

<ParamField body="business" type="object" required>
  <Expandable title="properties">
    <ParamField body="name" type="string" required>Legal name; at most 500 characters.</ParamField>
    <ParamField body="address" type="object" required>`{ street, city, state, postal_code, country }`; `state` is the two-letter code for US, `postal_code` at most 10 characters, `country` ISO 3166-1 alpha-2.</ParamField>
    <ParamField body="company_website" type="string" required>URL.</ParamField>
    <ParamField body="contact" type="object" required>`{ first_name, last_name, email, phone }`; `phone` in E.164 digits without `+`.</ParamField>
    <ParamField body="dba" type="string">Trading name.</ParamField>
    <ParamField body="terms_and_conditions_url, privacy_policy_url" type="string">URLs.</ParamField>
    <ParamField body="entity_type" type="string" required>One of `SOLE_PROPRIETOR`, `PRIVATE_PROFIT`, `PUBLIC_PROFIT`, `NON_PROFIT`, `GOVERNMENT`.</ParamField>
    <ParamField body="tax_id_type" type="string" required>Carrier: one of `EIN`, `CBN`, `CRN`, `NEQ`, `PROVINCIAL_NUMBER`, `VAT`, `ACN`, `ABN`, `BRN`, `SIREN`, `SIRET`, `NZBN`, `UST-IDNR`, `CIF`, `NIF`, `CNPJ`, `UID`, `OTHER`.</ParamField>
    <ParamField body="tax_id" type="string">Tax ID value.</ParamField>
    <ParamField body="tax_id_issuing_country" type="string" required>ISO 3166-1 alpha-2 code.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="use_case" type="object" required>`{ category, description, campaign_verify_auth_token? }`; `description` at most 500 characters.</ParamField>
<ParamField body="message_content" type="object" required>`{ content, age_gated? }`; `content` is a sample message of at most 1,000 characters, `age_gated` defaults to `false`.</ParamField>

<ParamField body="opt_in" type="object" required>
  <Expandable title="properties">
    <ParamField body="workflow" type="string" required>One of `Online (website, Mobile app/browser)`, `Text-to-join`, `Point of sale`, `Other`.</ParamField>
    <ParamField body="workflow_description" type="string">At most 494 characters.</ParamField>
    <ParamField body="images" type="object[]" required>Each `{ url }`: a screenshot of the opt-in flow.</ParamField>
    <ParamField body="keywords" type="string[]">Opt-in keywords, for example `["JOIN", "START"]`.</ParamField>
    <ParamField body="confirmation_message, help_message" type="string">At most 160 characters each.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="estimated_monthly_volume" type="string" required>One of `10`, `100`, `1,000`, `10,000`, `100,000`, `250,000`, `500,000`, `750,000`, `1,000,000`, `5,000,000`, `10,000,000+`.</ParamField>
<ParamField body="additional_information" type="string" required>Context for the reviewer; at most 350 characters.</ParamField>
<ParamField body="status" type="string" default="DRAFT">`DRAFT` saves without submitting; `SUBMITTED` submits immediately.</ParamField>

**Response**

`201` with the [registration object](#registration-object).

### GET /developer/orgs/:orgId/phone-numbers/:msisdn/tfn

Returns the number's [registration object](#registration-object), refreshing its status from the carrier first, or `200` with an empty body when the number has no registration. Scope `telephony:read`.

### PATCH /developer/orgs/:orgId/tfn-registrations/:id

Updates a registration, or submits it by setting `status` to `SUBMITTED`. Scope `telephony:write`. Accepts any subset of the create body; `status` accepts only `SUBMITTED`. The carrier accepts updates only while the registration is `DRAFT` or its number is `UPDATES_REQUIRED`, and answers `400` otherwise. Answers `204`; `404` `TFN registration not found` when the registration does not belong to the organization.

### GET /developer/orgs/:orgId/tfn-registrations/:id/events

Returns the carrier's event history for the registration, newest first, as `{ id, status, reason?, created_at }[]`. Scope `telephony:read`. `404` `TFN registration not found`.

### POST /developer/orgs/:orgId/tfn-registrations/:id/sync

Refreshes the registration's status from the carrier and answers `201` with the updated [registration object](#registration-object), or `201` with an empty body when the ID is unknown. Scope `telephony:provision`.

## Errors

| Status | Message                                                                                                                                                                                   | Meaning                                                                                                                      |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Validation messages, or a carrier message                                                                                                                                                 | A field failed a rule named in `message`, or the carrier refused the record; fix the field                                   |
| `400`  | A carrier message on a toll-free update                                                                                                                                                   | The carrier accepts updates only while the registration is `DRAFT` or its number is `UPDATES_REQUIRED`; check `numberStatus` |
| `403`  | —                                                                                                                                                                                         | The key lacks the scope on this organization; `telephony:provision` must be granted by name                                  |
| `404`  | `10DLC brand not found for this org. Create a brand first.`                                                                                                                               | Campaign registration with no registered brand; create the brand                                                             |
| `404`  | `10DLC brand not found`, `10DLC brand not found for this org or not linked to Vonage.`, `10DLC campaign not found`, `10DLC campaign not found for this org`, `TFN registration not found` | Unknown record on update, sync, link, unlink, or events; list the records and retry                                          |

## Example

Link a purchased local number to a registered campaign. No CLI command does this, so there is no CLI tab.

<CodeGroup>
  ```ts TypeScript theme={null}
  const response = await fetch(
    'https://api.heylua.ai/developer/orgs/<<YOUR_ORG_ID>>/phone-numbers/12025550100/10dlc/link',
    {
      method: 'POST',
      headers: { Authorization: 'Bearer <<YOUR_API_KEY>>', 'Content-Type': 'application/json' },
      body: JSON.stringify({ campaignId: '<<CAMPAIGN_ID>>' }),
    },
  );
  if (!response.ok) throw new Error(`Link failed: ${response.status} ${await response.text()}`);
  const result: { linked: boolean; warnings: string[] } = await response.json();
  for (const warning of result.warnings) console.warn(warning);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.heylua.ai/developer/orgs/<<YOUR_ORG_ID>>/phone-numbers/12025550100/10dlc/link" \
    -H "Authorization: Bearer <<YOUR_API_KEY>>" \
    -H "Content-Type: application/json" \
    -d '{ "campaignId": "<<CAMPAIGN_ID>>" }'
  ```
</CodeGroup>

## See also

* [Phone numbers](/reference/rest/phone-numbers) — search, purchase, suspend, and release the numbers these records attach to
* [SMS and phone numbers](/channels/sms-and-phone-numbers) — register a number for US messaging, end to end
* [RCS agents](/reference/rest/rcs-agents) — the carrier registration for RCS Business Messaging
* [`lua channels`](/reference/cli/channels) — the interactive number menu
