# US 10DLC registration

Not available yet. Register a US number with the carriers so it can send SMS, as a company, a sole proprietor or a toll-free number, then reuse the approval on more numbers.

> **Not available yet:**
> 
> US carrier registration is not available yet. [`POST /v1/sms/registrations`](https://pozy.ai/docs/api/startSmsRegistration) and [adding a number to a registration](https://pozy.ai/docs/api/reuseSmsRegistrationForNumber) answer `403` with `code: "feature_not_available"`, so US numbers can receive texts but cannot send them for now. Numbers in other countries send as soon as SMS is on. The rest of this page describes registration as it will work.

US carriers block SMS from numbers that are not registered. A local 10-digit number (10DLC) needs a registered **brand** (who is sending) and **campaign** (what they send and how people opted in). A toll-free number goes through toll-free verification instead. Pozy files both through one endpoint, `POST /v1/sms/registrations`, reviews the filing before it reaches the carriers, and tracks it to approval.

A US number receives texts from the moment SMS is on. It can send only once its registration is `approved`. Numbers in other countries skip all of this.

## Choose the registration type

| `registrationType` | For | Needs |
| --- | --- | --- |
| `standard_10dlc` | Companies, non-profits, government | `brand` (with EIN and business phone) and `campaign` |
| `sole_prop_10dlc` | Individuals without an EIN | `brand` with a `mobilePhone` for a one-time code, and `campaign` |
| `toll_free` | Toll-free numbers | `tollFree` details. No brand, no campaign. |

## What it costs

Registration adds a one-time brand fee and a monthly campaign fee to your invoice. Adding more numbers to an approved registration is free.

| What | Price |
| --- | --- |
| Outbound messages (inbox replies, broadcasts, sequences; not X DMs or SMS) | First 10,000 each month free, then $1 per 10,000 |
| Managed ads (running or in review) | First 500 free, then $0.01 per active ad per month |
| X post | $0.015 per post |
| X post containing a link | $0.20 per post |
| X reads (analytics, lookups) | $0.005 per resource |
| X DMs | $0.01 per DM read, $0.015 per DM sent |
| Phone numbers | From $3 per number per month, by country |
| Calls | From $0.01 per minute by destination. Recording $0.004 per minute, transcription $0.03 per minute |
| Branded calling | $0.10 per branded call |
| SMS | From $0.008 per segment by destination |
| US 10DLC registration | $9 one-time brand fee, then $20 per month per campaign ($4 for a sole proprietor) |
| RCS agent | $150 setup, then $160 per month in the US or $75 elsewhere, plus $750 on US carrier approval |
| Creator payouts | 4% of each transfer, minimum $0.50 |
| Support assistant | At cost per answered question, capped at $100 per month |

## Before you start

1. Buy or port an SMS-capable US number.
2. Enable SMS on it with `POST /v1/phone-numbers/{id}/sms`. The response tells you whether the number `needsRegistration` and whether a `reusable` approval already covers it. See [Send SMS](https://pozy.ai/docs/telephony/sms#turn-sms-on-for-a-number).

## Register a company (standard 10DLC)

```bash cURL
curl -X POST https://api.pozy.ai/v1/sms/registrations \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "registrationType": "standard_10dlc",
    "phoneNumbers": ["+14155550142"],
    "brand": {
      "entityType": "PRIVATE_PROFIT",
      "displayName": "Acme",
      "companyName": "Acme Inc.",
      "ein": "12-3456789",
      "phone": "+14155550199",
      "email": "ops@acme.example.com",
      "website": "https://acme.example.com",
      "vertical": "RETAIL",
      "street": "500 Howard St",
      "city": "San Francisco",
      "state": "CA",
      "postalCode": "94105",
      "country": "US"
    },
    "campaign": {
      "usecase": "CUSTOMER_CARE",
      "description": "Order updates and replies to support questions for Acme customers.",
      "messageFlow": "Customers tick an unchecked SMS consent box at checkout on https://acme.example.com/checkout.",
      "sample1": "Acme: order #1234 has shipped and arrives Thursday. Reply STOP to opt out.",
      "sample2": "Acme: a support agent answered your question. Reply HELP for help.",
      "optinKeywords": "START",
      "optoutKeywords": "STOP",
      "helpKeywords": "HELP"
    }
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/sms/registrations", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    registrationType: "standard_10dlc",
    phoneNumbers: ["+14155550142"],
    brand: {
      entityType: "PRIVATE_PROFIT",
      displayName: "Acme",
      companyName: "Acme Inc.",
      ein: "12-3456789",
      phone: "+14155550199",
      website: "https://acme.example.com",
      vertical: "RETAIL",
      street: "500 Howard St",
      city: "San Francisco",
      state: "CA",
      postalCode: "94105",
      country: "US",
    },
    campaign: {
      usecase: "CUSTOMER_CARE",
      description: "Order updates and replies to support questions for Acme customers.",
      messageFlow: "Customers tick an unchecked SMS consent box at checkout on https://acme.example.com/checkout.",
      sample1: "Acme: order #1234 has shipped and arrives Thursday. Reply STOP to opt out.",
      sample2: "Acme: a support agent answered your question. Reply HELP for help.",
      optinKeywords: "START",
      optoutKeywords: "STOP",
      helpKeywords: "HELP",
    },
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.registrationId, data.status); // "pending"
```
```python Python
import os, requests

resp = requests.post(
    "https://api.pozy.ai/v1/sms/registrations",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={
        "registrationType": "standard_10dlc",
        "phoneNumbers": ["+14155550142"],
        "brand": {
            "entityType": "PRIVATE_PROFIT", "displayName": "Acme", "companyName": "Acme Inc.",
            "ein": "12-3456789", "phone": "+14155550199", "website": "https://acme.example.com",
            "vertical": "RETAIL", "street": "500 Howard St", "city": "San Francisco",
            "state": "CA", "postalCode": "94105", "country": "US",
        },
        "campaign": {
            "usecase": "CUSTOMER_CARE",
            "description": "Order updates and replies to support questions for Acme customers.",
            "messageFlow": "Customers tick an unchecked SMS consent box at checkout on https://acme.example.com/checkout.",
            "sample1": "Acme: order #1234 has shipped and arrives Thursday. Reply STOP to opt out.",
            "sample2": "Acme: a support agent answered your question. Reply HELP for help.",
            "optinKeywords": "START", "optoutKeywords": "STOP", "helpKeywords": "HELP",
        },
    },
    timeout=60,
)
print(resp.json())
```

> **Always send phoneNumbers:**
> 
> If you leave out `phoneNumbers`, the registration enrolls every active SMS-enabled US local number in the workspace that no other registration covers. That is rarely what one campaign should cover.

## Write a filing that passes

Carriers reject thin filings. These fields decide most outcomes:

| Field | What reviewers look for |
| --- | --- |
| `brand.website` | A live site for the business. Sole proprietors can use a social profile URL. |
| `brand` address | A full street address: `street`, `city`, `state`, `postalCode` |
| `brand.phone` | A business phone, required for company, non-profit and government brands |
| `campaign.messageFlow` | How people opt in, with a link to the page where it happens. Up to 2,048 characters. |
| `campaign.sample1`, `sample2` | Two different, realistic messages (20 characters or more) that match the use case and include opt-out wording |
| Keywords | `optinKeywords`, `optoutKeywords` and `helpKeywords` are all required |

`optinMessage`, `optoutMessage` and `helpMessage` are optional: Pozy writes compliant auto-replies when you leave them out. `subUsecases`, `embeddedLink`, `embeddedPhone`, `ageGated`, `privacyPolicyLink` and `termsAndConditionsLink` describe the campaign in more detail.

> **Prove the opt-in:**
> 
> Upload a screenshot of your opt-in form with `POST /v1/sms/opt-in-proof` (PNG, JPG, WebP, GIF or PDF, up to 4 MB) and put the returned URL inside `messageFlow`. Reviewers reject campaigns whose consent they cannot see.

### Check before you submit

`POST /v1/sms/registrations/preflight` takes the same body, runs the same checks plus a compliance review, and creates nothing. It returns a `verdict` (`pass`, `warn`, `fail` or `unreviewed`) and a list of `advisories`, each with a `severity` of `block` or `warn`.

## Register a sole proprietor

Use `registrationType: "sole_prop_10dlc"` and `brand.entityType: "SOLE_PROPRIETOR"`, with a `mobilePhone` on the brand. The response has `awaitingOtp: true`, and a code is texted to that mobile.

```bash
curl -X POST https://api.pozy.ai/v1/sms/registrations/67a7b8c9d0e1f2a3b4c5d6e7/verify-otp \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "otpPin": "482913" }'
```

If the code expired or never arrived, `POST /v1/sms/registrations/{id}/resend-otp` sends a new one (at most once a minute).

## Verify a toll-free number

Toll-free numbers are verified separately, so the body takes a `tollFree` object instead of `brand` and `campaign`. Every field in it is required except `businessAddr2`.

```json
{
  "registrationType": "toll_free",
  "phoneNumbers": ["+18445550100"],
  "tollFree": {
    "businessName": "Acme Inc.",
    "corporateWebsite": "https://acme.example.com",
    "phoneNumbers": ["+18445550100"],
    "useCase": "Customer care",
    "useCaseSummary": "Order updates and support replies for Acme customers.",
    "productionMessageContent": "Acme: order #1234 has shipped. Reply STOP to opt out.",
    "optInWorkflow": "Customers tick the SMS updates box at checkout on https://acme.example.com/checkout.",
    "optInWorkflowImageUrls": ["https://pozy.ai/media/opt-in-proof/acme-checkout.png"],
    "messageVolume": "10,000",
    "additionalInformation": "Transactional messages only.",
    "businessAddr1": "500 Howard St",
    "businessCity": "San Francisco",
    "businessState": "CA",
    "businessZip": "94105",
    "businessContactFirstName": "Dana",
    "businessContactLastName": "Reyes",
    "businessContactEmail": "dana@acme.example.com",
    "businessContactPhone": "+14155550199",
    "businessRegistrationNumber": "12-3456789",
    "businessRegistrationType": "EIN",
    "businessRegistrationCountry": "US"
  }
}
```

`optInWorkflowImageUrls` needs at least one screenshot of the opt-in form (upload it with `POST /v1/sms/opt-in-proof`). `messageVolume` is a monthly tier: `10`, `100`, `1,000`, `10,000`, `100,000`, `250,000`, `500,000`, `750,000`, `1,000,000`, `5,000,000` or `10,000,000+`. Toll-free verification has no code step and cannot be appealed.

## Reuse an approval on more numbers

Once one registration is approved, add more numbers to it instead of registering again. There is no new brand fee and no extra campaign.

```bash
curl -X POST https://api.pozy.ai/v1/phone-numbers/6650d2e1b3c4d5e6f7081924/sms/reuse-registration \
  -H "Authorization: Bearer $POZY_API_KEY"
```

```json
{ "registrationId": "67a7b8c9d0e1f2a3b4c5d6e7", "status": "approved" }
```

Enable SMS on the number first. Its response shows the `reusable` registration.

## Let your customer fill it in

> **Not available yet:**
> 
> Hosted registration forms are not available yet: `POST /v1/sms/registrations/share` answers `403` with `code: "feature_not_available"`.

When your customer holds the legal business details, `POST /v1/sms/registrations/share` with a `numberId` returns a single-use link (valid 7 days). Your customer completes the form without a Pozy login, and the registration is created under your workspace.

## Track the registration

`GET /v1/sms/registrations/{id}` returns the current state. Branch on `status`, not on the raw carrier fields (`brandStatus`, `campaignStatus`).

| `status` | Meaning | What to do |
| --- | --- | --- |
| `requested` | New or resubmitted, in Pozy's review | Wait |
| `changes_requested` | The review needs answers. `reviewRequest.points` lists them. | Answer with `POST /v1/sms/registrations/{id}/respond` |
| `pending` | With the carriers. On a sole proprietor, `awaitingOtp: true` means it waits for the code. | Wait, or submit the code |
| `approved` | Live. Every attached number can send. | Send, or add more numbers |
| `rejected` | Declined. `declineReason` says why. | Fix and appeal |
| `deactivated` | Ended. Hidden from the list unless `includeDeactivated=true`. | |

> **Note:**
> 
> Status can go backwards. An approved registration drops back to `pending` if the carriers suspend the campaign. Treat `status` as the current state, not a milestone you passed once.

### Answer a change request

`POST /v1/sms/registrations/{id}/respond` takes `answers`, one per point (`pointId` plus `text`, `url` or `files`), and an optional `note`. The registration goes back to `requested`.

### Appeal a rejected campaign

`POST /v1/sms/registrations/{id}/appeal` takes an `appealReason` (up to 2,000 characters) that answers `declineReason` directly, plus any corrected `messageFlow`, `sample1` or `sample2`. The content on file comes back as `campaignContent` on a rejected registration. A rejection at brand level cannot be appealed: correct the brand and file again.

### Stop a registration

`DELETE /v1/sms/registrations/{id}` deactivates a brand or campaign registration. Its numbers can no longer send.

## Webhook events

| Event | When |
| --- | --- |
| `sms.registration.status_updated` | Every status change. On `rejected`, `reason` has the carriers' words. |
| `sms.registration.action_required` | Only when you must act. `reason` is `changes_requested`, `otp_required` or `carrier_info_required` (a toll-free information request, which expires after 7 days). |

Both carry `registration.id` and `registration.profileId`, not an account id.

| Event | When it fires |
| --- | --- |
| [`sms.registration.action_required`](https://pozy.ai/docs/webhooks/events#sms-registration-action-required) | SMS registration action required. |
| [`sms.registration.status_updated`](https://pozy.ai/docs/webhooks/events#sms-registration-status-updated) | SMS registration status updated. |

## Errors

| Status | Code | Cause | Fix |
| --- | --- | --- | --- |
| `400` | `missing_required_field` | A required field is missing. `param` names it, for example `campaign.sample2`. | Add it. Run preflight to catch every gap at once. |
| `422` | | The carrier registry rejected a field | Correct the field named in `param` and resubmit |
| `403` | | You tried to send before `approved` | Wait for approval |

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`POST /v1/sms/registrations`](https://pozy.ai/docs/api/startSmsRegistration) | Start a carrier registration (not available yet) |
| [`POST /v1/sms/registrations/preflight`](https://pozy.ai/docs/api/preflightSmsRegistration) | Pre-check a carrier registration |
| [`GET /v1/sms/registrations`](https://pozy.ai/docs/api/listSmsRegistrations) | List carrier registrations |
| [`GET /v1/sms/registrations/{id}`](https://pozy.ai/docs/api/getSmsRegistration) | Get a carrier registration |
| [`POST /v1/sms/registrations/{id}/verify-otp`](https://pozy.ai/docs/api/verifySmsRegistrationOtp) | Submit the sole-prop OTP |
| [`POST /v1/sms/registrations/{id}/resend-otp`](https://pozy.ai/docs/api/resendSmsRegistrationOtp) | Re-send the sole-prop OTP |
| [`POST /v1/sms/registrations/{id}/respond`](https://pozy.ai/docs/api/respondToSmsRegistrationReview) | Reply to a change request |
| [`POST /v1/sms/registrations/{id}/appeal`](https://pozy.ai/docs/api/appealSmsRegistration) | Appeal a rejected campaign |
| [`POST /v1/sms/opt-in-proof`](https://pozy.ai/docs/api/uploadSmsOptInProofFile) | Upload opt-in form proof |
| [`POST /v1/sms/registrations/{id}/opt-in-proof`](https://pozy.ai/docs/api/uploadSmsOptInProof) | Upload opt-in form proof for an appeal |
| [`POST /v1/sms/registrations/share`](https://pozy.ai/docs/api/shareSmsRegistration) | Create a registration share link (not available yet) |
| [`POST /v1/phone-numbers/{id}/sms/reuse-registration`](https://pozy.ai/docs/api/reuseSmsRegistrationForNumber) | Add number to SMS registration (not available yet) |
| [`DELETE /v1/sms/registrations/{id}`](https://pozy.ai/docs/api/deactivateSmsRegistration) | Deactivate a brand/campaign registration |
