Phone, SMS & voice

Send SMS

Send SMS and MMS from your numbers, handle replies and delivery receipts, respect opt-outs, look up line types and send one-time codes.

Send a text from any SMS-enabled number you own with one request. Replies land in the same inbox as your other channels, and delivery receipts arrive as webhooks. Outside the US a number can send as soon as SMS is on. In the US it must pass 10DLC registration first.

#Turn SMS on for a number

Not every number can text. Buy with wantsSms: true (or search with sms=true) to get one that can, then enable SMS on it:

Enable SMS
curl -X POST https://api.pozy.ai/v1/phone-numbers/6650d2e1b3c4d5e6f7081923/sms \
  -H "Authorization: Bearer $POZY_API_KEY"
Response
{
  "enabled": true,
  "id": "6650e3f2c4d5e6f708192a34",
  "phoneNumber": "+14155550142",
  "isActive": true,
  "country": "US",
  "smsCapable": true,
  "mmsCapable": true,
  "needsRegistration": true,
  "alreadyRegistered": false,
  "registrationStatus": null,
  "reusable": null
}
Field Meaning
isActive SMS is on. The number receives texts right away.
needsRegistration A US number that cannot send until a 10DLC or toll-free registration is approved
reusable You already hold an approved registration this number can join for free
smsCapable: false with enabled: false This number cannot text. Buy an SMS-capable one.
notReady: true Still provisioning at the carrier. Retry in a minute.

DELETE /v1/phone-numbers/{id}/sms turns SMS off again.

#Send a text

POST /v1/sms/messages takes from, to and text. Both numbers are normalized to E.164, so formatting does not matter.

curl -X POST https://api.pozy.ai/v1/sms/messages \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Idempotency-Key: order-1234-shipped" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+14155550142",
    "to": "+13125550188",
    "text": "Acme: order #1234 has shipped. Reply STOP to opt out."
  }'
Response
{ "id": "67c9d0e1f2a3b4c5d6e7f8a9", "conversationId": "66c3d2ae7b4f6c8d0e1f2a3b", "status": "sent" }

sent means the carrier accepted the message. The final outcome comes by webhook. Send an Idempotency-Key so a retry returns the first response instead of texting twice (see Idempotency).

Body field What it does
from One of your SMS-enabled numbers, or an alphanumeric sender ID
to Recipient number
text Message body, up to 10 segments. Optional when you send media.
mediaUrls Up to 10 public URLs. Turns the message into an MMS.
sendAt ISO 8601 time with offset, in the future, to schedule the message

#Segments and encoding

Carriers bill and deliver SMS in segments. The encoding decides how many characters fit in each one.

Encoding Used when One segment Per segment of a longer message
GSM-7 Plain Latin letters, digits and common symbols 160 characters 153 characters
UCS-2 Any emoji, accented letters outside GSM-7, non-Latin scripts 70 characters 67 characters

One emoji switches the whole message to UCS-2. A message is capped at 10 segments: 1,530 GSM-7 characters or 670 UCS-2 characters. Prices are per segment, by destination, and the carrier rates each message after it is sent, so there is no price quote endpoint for SMS.

#Send MMS

Add mediaUrls to attach images or video. text becomes the caption, or leave it out to send media only.

Send an MMS
curl -X POST https://api.pozy.ai/v1/sms/messages \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+14155550142",
    "to": "+13125550188",
    "text": "Here is your receipt",
    "mediaUrls": ["https://acme.example.com/receipts/1234.png"]
  }'

MMS is not available from sender IDs. To send the same message to many people, use an SMS broadcast instead of looping over this endpoint.

#Replies and delivery

What happens Event
The carrier delivered your message message.delivered
Delivery failed (carrier error code included) message.failed
Someone texts your number message.received with platform: "sms"
The first message of a new thread conversation.started

Replies thread into the same inbox conversation as the message you sent (conversationId). Answer them with the inbox endpoints or another POST /v1/sms/messages. See Messages and Webhooks.

#Opt-outs

When a recipient replies STOP, the carrier opts them out. Further sends to them return 409: nothing is dropped silently. Only the recipient can opt back in, by replying START.

Keep your own suppression list in sync with GET /v1/sms/opt-outs:

List opt-outs
curl "https://api.pozy.ai/v1/sms/opt-outs?limit=1000" \
  -H "Authorization: Bearer $POZY_API_KEY"
Response
{
  "optOuts": [
    { "phoneNumber": "+13125550199", "optedOutAt": "2026-10-28T09:30:00Z", "keyword": "STOP", "from": "+14155550142" }
  ],
  "count": 1
}

The list is most recent first. limit defaults to 500 and goes up to 5,000. There is no cursor, so raise limit instead of paging. Add format=csv for a file.

Tip

Put "Reply STOP to opt out" in the first message you send to a new contact. US carriers expect it, and it is one of the things 10DLC reviewers check.

#Look up a number

GET /v1/sms/lookup?number=%2B13125550188 returns the carrier, the line type (mobile, landline, voip, toll-free or unknown) and smsReachable. Each lookup is billed by the carrier-data source, so run it when you clean an opt-in list, not before every send.

#Send one-time codes

Not available yet

The Verify API is not available yet. Its endpoints answer 403 with code: "feature_not_available". Until it opens, send your own codes with POST /v1/sms/messages and check them on your side.

The Verify API sends a one-time code by SMS or WhatsApp and checks it for you. You never store the code.

curl -X POST https://api.pozy.ai/v1/verify/verifications \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "sms", "to": "+13125550188", "brandName": "Acme", "codeLength": 6, "ttlMinutes": 10 }'
Field Default Notes
channel required sms or whatsapp
codeLength 6 Up to 8 digits
ttlMinutes 10 Up to 15 minutes
brandName none Up to 30 characters, shown in the message
from auto A number of yours to send from

A verification moves through pending, then approved, expired, max_attempts_reached, canceled or delivery_failed. verification.approved fires when the right code is checked, and verification.failed (with reason: "max_attempts_reached") when attempts run out.

#Limits

Limit Value
Text length 10 segments (1,530 GSM-7 or 670 UCS-2 characters)
MMS media 10 public URLs per message
Destinations Only countries on the SMS rate card. Others are blocked at send time, not billed.
Opt-out list 500 by default, 5,000 at most, no cursor

#Errors

Status Cause Fix
403 A US number whose registration is not approved yet Wait for approved on the registration
404 No SMS-enabled number you own matches from Send from one of your numbers
409 The recipient replied STOP, or the same Idempotency-Key is still in flight Drop the recipient, or wait for the first request
422 The Idempotency-Key was reused with a different body Use a new key for a new message
402 No payment method on file Add a payment method

#Endpoints

Endpoint What it does
POST /v1/phone-numbers/{id}/sms Enable SMS on a number
DELETE /v1/phone-numbers/{id}/sms Disable SMS on a number
POST /v1/sms/messages Send an SMS/MMS
GET /v1/sms/opt-outs List SMS opt-outs
GET /v1/sms/lookup Look up carrier + line type
POST /v1/verify/verifications Send a verification code (not available yet)
GET /v1/verify/verifications/{verificationId} Get a verification (not available yet)
POST /v1/verify/verifications/{verificationId}/check Check a verification code (not available yet)

#Webhook events

Event When it fires
message.received Message received.
conversation.started Conversation started.
message.delivered Message delivered.
message.failed Message delivery failed.
verification.approved Verification approved.
verification.failed Verification failed.