Phone, SMS & voice
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.
US carrier registration is not available yet. POST /v1/sms/registrations and adding a number to a registration 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
- Buy or port an SMS-capable US number.
- Enable SMS on it with
POST /v1/phone-numbers/{id}/sms. The response tells you whether the numberneedsRegistrationand whether areusableapproval already covers it. See Send SMS.
#Register a company (standard 10DLC)
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"
}
}'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"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())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.
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.
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.
{
"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.
curl -X POST https://api.pozy.ai/v1/phone-numbers/6650d2e1b3c4d5e6f7081924/sms/reuse-registration \
-H "Authorization: Bearer $POZY_API_KEY"{ "registrationId": "67a7b8c9d0e1f2a3b4c5d6e7", "status": "approved" }Enable SMS on the number first. Its response shows the reusable registration.
#Let your customer fill it in
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. |
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 |
SMS registration action required. |
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 |
Start a carrier registration (not available yet) |
POST /v1/sms/registrations/preflight |
Pre-check a carrier registration |
GET /v1/sms/registrations |
List carrier registrations |
GET /v1/sms/registrations/{id} |
Get a carrier registration |
POST /v1/sms/registrations/{id}/verify-otp |
Submit the sole-prop OTP |
POST /v1/sms/registrations/{id}/resend-otp |
Re-send the sole-prop OTP |
POST /v1/sms/registrations/{id}/respond |
Reply to a change request |
POST /v1/sms/registrations/{id}/appeal |
Appeal a rejected campaign |
POST /v1/sms/opt-in-proof |
Upload opt-in form proof |
POST /v1/sms/registrations/{id}/opt-in-proof |
Upload opt-in form proof for an appeal |
POST /v1/sms/registrations/share |
Create a registration share link (not available yet) |
POST /v1/phone-numbers/{id}/sms/reuse-registration |
Add number to SMS registration (not available yet) |
DELETE /v1/sms/registrations/{id} |
Deactivate a brand/campaign registration |