# SMS

Send and receive SMS and MMS from your Pozy numbers, register US numbers with carriers, send from a brand name abroad, and handle opt-outs and delivery webhooks.

SMS in Pozy sends from phone numbers you buy or port into Pozy. Outbound messages go through `POST /v1/sms/messages`, replies land in the same [Inbox](https://pozy.ai/docs/inbox) as every other channel, and delivery arrives as webhooks. The platform id is `sms`.

This page is the summary. Number buying, porting, US carrier registration and sender IDs each have a full page in [Phone, SMS & voice](https://pozy.ai/docs/telephony).

## What you can do

| Feature | Supported | Where |
| --- | --- | --- |
| Send SMS and MMS | Yes | [Sending messages](#sending-messages) |
| Schedule a single SMS | Yes, with `sendAt` | [Sending messages](#sending-messages) |
| Two-way conversations in the inbox | Yes | [Inbox](#inbox) |
| Bulk sends | Yes, as a broadcast | [Broadcasts](https://pozy.ai/docs/automations/broadcasts) |
| US 10DLC and toll-free registration | Not available yet | [10DLC](https://pozy.ai/docs/telephony/10dlc) |
| Send from a brand name (outside the US) | Yes, one way | [Sender IDs](https://pozy.ai/docs/telephony/sender-ids) |
| Carrier and line-type lookup | Yes | [Look up a number](#look-up-a-number) |
| One-time passcodes | Not available yet | [Verify endpoints](https://pozy.ai/docs/api/createVerification) |
| Publishing, analytics | No | Messaging only |

## Connect

There is no OAuth. SMS needs usage-based billing and an SMS-capable number:

### Get a number

[Search available numbers](https://pozy.ai/docs/api/searchAvailablePhoneNumbers) with `sms=true` and [purchase](https://pozy.ai/docs/api/purchasePhoneNumber) one with `wantsSms: true`, or [port in](https://pozy.ai/docs/telephony/porting) a number you already have. See [Buy a number](https://pozy.ai/docs/telephony/buy-a-number).

### Turn on SMS

Call [Enable SMS on a number](https://pozy.ai/docs/api/enableSmsOnNumber). The number can receive texts from this moment.

### Register it (US only)

US numbers stay silent until their carrier registration, 10DLC or toll-free, is approved. US carrier registration is not available yet, so for now US numbers can receive texts but not send them. When it opens, begin with [Start a carrier registration](https://pozy.ai/docs/api/startSmsRegistration) as early as you can, because approval is slow. Extra numbers can join a registration you already have approved, without paying again, through [Add number to SMS registration](https://pozy.ai/docs/api/reuseSmsRegistrationForNumber). Outside the US, numbers send as soon as SMS is enabled.

## Sending messages

```bash cURL
curl -X POST https://api.pozy.ai/v1/sms/messages \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reminder-appt-88213" \
  -d '{
    "from": "+14155550123",
    "to": "+13105551234",
    "text": "Reminder: your appointment is tomorrow at 10:00. Reply C to confirm."
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/sms/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Idempotency-Key": "reminder-appt-88213",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "+14155550123",
    to: "+13105551234",
    text: "Reminder: your appointment is tomorrow at 10:00. Reply C to confirm.",
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.id, data.conversationId);
```
```python Python
import os, requests

resp = requests.post(
    "https://api.pozy.ai/v1/sms/messages",
    headers={
        "Authorization": f"Bearer {os.environ['POZY_API_KEY']}",
        "Idempotency-Key": "reminder-appt-88213",
    },
    json={
        "from": "+14155550123",
        "to": "+13105551234",
        "text": "Reminder: your appointment is tomorrow at 10:00. Reply C to confirm.",
    },
)
print(resp.json())
```

| Field | Notes |
| --- | --- |
| `from` | A number you own with SMS enabled, or a [sender ID](https://pozy.ai/docs/telephony/sender-ids). Normalized to E.164, so any formatting works |
| `to` | The recipient, normalized to E.164 |
| `text` | Up to 10 segments. Optional when you send media |
| `mediaUrls` | Up to 10 public URLs. Sends an MMS |
| `sendAt` | ISO 8601 time with an offset, in the future, to schedule the message |

The response has the message `id` and the inbox `conversationId`. Send an `Idempotency-Key` so a retry replays the original response instead of texting twice. Reusing a key with a different body returns `422`; a key that is still in flight returns `409`.

To text many people, create an SMS [broadcast](https://pozy.ai/docs/automations/broadcasts). Its attachments go out as MMS media.

## Media requirements

| Item | Requirement |
| --- | --- |
| MMS media | Up to 10 public `mediaUrls` per message |
| Broadcast attachments | Up to 10 public http(s) URLs with no redirects. JPEG, PNG, GIF, WEBP, MP4 or 3GPP, under 1 MB each |
| Sender IDs | No MMS |

## Limits

| Limit | Value |
| --- | --- |
| Text length | 10 segments: 1,530 GSM-7 characters or 670 Unicode characters |
| Segment size | GSM-7: 160 characters, or 153 per part when split. Unicode (emoji, non-Latin scripts): 70, or 67 per part |
| US sending | Only after the number's carrier registration is `approved` |
| Destinations | Only countries on the SMS price list. Others are blocked at send time |
| Sender ID names | 3 to 11 characters, letters, digits and spaces, at least one letter. Up to 1,000 active per workspace |
| Sender ID daily limit | Resets at midnight UTC. Write to support@pozy.ai to raise your sender ID limit |
| Opt-out list | `limit` defaults to 500, up to 5,000, newest first |

Sender IDs cannot receive replies, cannot send MMS, and cannot send to the US, Canada or Puerto Rico. Prices are per segment and set by the destination carrier after sending, so you cannot get a quote before a send.

### Opt-outs

A recipient who replies STOP is opted out by the carrier. Further sends to them return `409`; they are never dropped silently. Only the recipient can opt back in, by replying START. Keep your own list in sync with [List SMS opt-outs](https://pozy.ai/docs/api/listSmsOptOuts) (add `format=csv` for a file).

### Look up a number

[Look up carrier and line type](https://pozy.ai/docs/api/lookupSmsNumber) returns `carrierName`, `lineType` (`mobile`, `landline`, `voip`, `toll-free` or `unknown`) and `smsReachable`. Each lookup is billed by the carrier-data provider, so run it when you clean a list, not before every send.

## Analytics

There are no post analytics for SMS. Each message reports its outcome as a webhook. Conversation volume and response times are in [Inbox analytics](https://pozy.ai/docs/analytics/inbox-analytics).

## Inbox

Replies thread into one conversation per contact and number in the [Inbox](https://pozy.ai/docs/inbox/messages), with `platform: "sms"`.

| Event | When |
| --- | --- |
| `message.received` | An inbound text arrives |
| `conversation.started` | The first message of a new thread |
| `message.delivered` | The carrier confirmed delivery |
| `message.failed` | Delivery failed after the message was accepted, with the carrier's error code |

You can also reply from the inbox with [Send message](https://pozy.ai/docs/api/sendInboxMessage).

## Pricing

SMS is billed per segment by destination, plus the monthly price of each number and, in the US, the 10DLC brand and campaign fees. See [Pricing](https://pozy.ai/docs/billing) for the current table.

## Errors you may see

| Status | Code | Cause | Fix |
| --- | --- | --- | --- |
| `403` | | The US number's registration is still pending | Wait until it is `approved`; incoming texts already arrive |
| `404` | | `from` is not an SMS-enabled number you own | Send from one of your numbers |
| `409` | | The recipient replied STOP, or the same `Idempotency-Key` is in flight | Remove the recipient, or wait for the first request |
| `422` | | The same `Idempotency-Key` came with a different body | Every new message needs its own key |
| `403` | `alpha_destination_not_supported` | Sender IDs cannot text the US, Canada or Puerto Rico | Use a phone number as the sender |
| `403` | `mms_not_supported` | Sender IDs cannot send `mediaUrls` | Drop the media, or send from a phone number |
| `429` | `alpha_daily_limit_reached` | Today's sender ID limit is reached | Try after midnight UTC, or write to support@pozy.ai to raise your sender ID limit |
| `400` | `missing_required_field` | A registration is missing a field, named in `param` | Add it. [Pre-check](https://pozy.ai/docs/api/preflightSmsRegistration) catches every gap at once |
| `402` | | No payment method on file | Add one |

[Errors](https://pozy.ai/docs/getting-started/errors) covers the error format.

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`POST /v1/sms/messages`](https://pozy.ai/docs/api/sendSms) | Send an SMS/MMS |
| [`GET /v1/sms/lookup`](https://pozy.ai/docs/api/lookupSmsNumber) | Look up carrier + line type |
| [`GET /v1/sms/opt-outs`](https://pozy.ai/docs/api/listSmsOptOuts) | List SMS opt-outs |
| [`GET /v1/sms/sender-ids`](https://pozy.ai/docs/api/listSmsSenderIds) | List alphanumeric sender IDs |
| [`POST /v1/sms/sender-ids`](https://pozy.ai/docs/api/createSmsSenderId) | Create an alphanumeric sender ID |
| [`POST /v1/sms/sender-ids/limit-request`](https://pozy.ai/docs/api/requestSmsSenderIdLimitIncrease) | Request a higher sender ID daily limit (not available yet) |
| [`DELETE /v1/sms/sender-ids/{id}`](https://pozy.ai/docs/api/deleteSmsSenderId) | Delete an alphanumeric sender ID |
| [`GET /v1/sms/registrations`](https://pozy.ai/docs/api/listSmsRegistrations) | List carrier registrations |
| [`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/{id}`](https://pozy.ai/docs/api/getSmsRegistration) | Get a carrier registration |
| [`DELETE /v1/sms/registrations/{id}`](https://pozy.ai/docs/api/deactivateSmsRegistration) | Deactivate a brand/campaign 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}/appeal`](https://pozy.ai/docs/api/appealSmsRegistration) | Appeal a rejected campaign |
| [`POST /v1/sms/registrations/{id}/respond`](https://pozy.ai/docs/api/respondToSmsRegistrationReview) | Reply to a change request |
| [`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`](https://pozy.ai/docs/api/enableSmsOnNumber) | Enable SMS on a number |
| [`DELETE /v1/phone-numbers/{id}/sms`](https://pozy.ai/docs/api/disableSmsOnNumber) | Disable SMS on a number |
| [`POST /v1/phone-numbers/{id}/sms/reuse-registration`](https://pozy.ai/docs/api/reuseSmsRegistrationForNumber) | Add number to SMS registration (not available yet) |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`message.received`](https://pozy.ai/docs/webhooks/events#message-received) | Message received. |
| [`conversation.started`](https://pozy.ai/docs/webhooks/events#conversation-started) | Conversation started. |
| [`message.delivered`](https://pozy.ai/docs/webhooks/events#message-delivered) | Message delivered. |
| [`message.failed`](https://pozy.ai/docs/webhooks/events#message-failed) | Message delivery failed. |
| [`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. |
