Platforms
Register a verified RCS agent, send rich cards, carousels and buttons to Android and iPhone, fall back to SMS, and receive replies and receipts in the inbox.
RCS is rich messaging for phones: Android users get it in Google Messages, and iPhone users in Messages on iOS 18 and later. Your messages arrive under a checked business identity with your name, logo, banner and a verified badge, not from an anonymous number. They can carry cards, carousels and buttons, and you see when they are read. In Pozy you register an RCS agent, then send with POST /v1/rcs/messages. Replies land in the Inbox with the platform id rcs.
#What you can do
| Feature | Supported | Where |
|---|---|---|
| Register a brand and agent with the carriers | Yes, through the API | Connect |
| Test on your own phones before launch | Yes (US) | Connect |
| Text with buttons | Yes | Sending messages |
| Media, rich cards, carousels | Yes | Sending messages |
| SMS fallback for phones without RCS | Yes, with smsFallbackFrom |
SMS fallback |
| Check which numbers can receive RCS | Up to 100 at a time | SMS fallback |
| Replies, button taps, locations, files | Yes | Inbox |
| Delivery and read receipts | Yes | Inbox |
| Publishing, analytics | No | Messaging only |
#Connect
Carriers approve two things before an agent can send:
- Brand: the company that is legally responsible for what is sent, with its registered name, tax id, address and a contact person. Several agents can share one brand.
- Agent: the sender identity on the phone, with a display name, description, logo, banner, colour and contact buttons. An agent sits in one profile, which gets an inbox account for it.
The workspace needs a plan with the inbox, usage-based billing and a saved card. Pozy reviews every request first; carriers see nothing and you pay nothing before that review is done.
#Upload the logo and banner
The carriers' sizes are strict: the logo is 224 x 224 and at most 50 KB, the banner 1440 x 448 and at most 200 KB. You do not have to hit them yourself. Send a PNG, JPEG or WebP to Upload an RCS logo or banner with kind: "logo" or kind: "banner"; Pozy crops and compresses it and returns the URL to use.
#Request the agent
Then call Request an RCS agent. Send the profile, the agent's details, and the brand: a full brand object for a new one, or the brandId of an earlier one, but not both. A profile can have only one agent in progress or live at a time.
{
"profileId": "66a1f0c2a4b9d3e8f1a2b3c4",
"displayName": "Acme Deliveries",
"useCase": "TRANSACTIONAL",
"brand": {
"displayName": "Acme",
"legalName": "Acme Logistics LLC",
"legalEntityType": "LIMITED_LIABILITY_COMPANY",
"organizationType": "PRIVATE_PROFIT",
"websiteUrl": "https://acme.example.com",
"taxId": "12-3456789",
"address": { "line1": "100 Market St", "city": "San Francisco", "state": "CA", "postalCode": "94105", "country": "US" },
"contact": { "firstName": "Dana", "lastName": "Lee", "email": "dana@acme.example.com", "phone": "+14155550100" }
},
"profile": {
"description": "Delivery updates from Acme",
"logoUrl": "https://pozy.ai/media/acme-logo.png",
"heroUrl": "https://pozy.ai/media/acme-banner.png",
"brandColor": "#1A3D8F",
"privacyPolicyUrl": "https://acme.example.com/privacy",
"termsUrl": "https://acme.example.com/terms",
"phone": { "number": "+14155550100", "label": "Call us" }
}
}#Fix anything the review flags
If something needs changing, the agent moves to changes_requested with a note in reviewNote. Send only the changed fields with Update an RCS agent and it goes back into review. Once details are filed with the carriers they are locked; only smsFallbackFrom can still change.
#Test on your phones (US)
When a US agent reaches testing, it can message phones you invite, and no one else. Invite one with Invite an RCS test phone and accept the invite in that phone's messaging app. T-Mobile and AT&T numbers cannot be test phones.
#Send the launch details
Send the launch filing tells the carriers what the agent will send and how people agree to it. It needs three or more messageExamples; the consent block, with how people opt in and the replies to opt-in, HELP and opt-out (for an opt-in on a website, add the page in callToActionUrl and a screenshot of it in callToActionMediaUrl); and testVideoUrl, a public or unlisted recording of a phone texting START, STOP and HELP to the agent and having one real exchange. For US agents, file it after the agent works on your test phones. In other countries, file it straight after the request.
Follow progress with the rcs.agent.status_updated webhook rather than polling.
| Market | Status path | Typical time |
|---|---|---|
| United States | requested, brand_vetting, agent_review, testing, launch_review, launching, live |
4 to 6 weeks of carrier review |
| Other markets | requested, brand_vetting, launching, live |
Varies by carrier |
Side exits are changes_requested, rejected (with declineReason) and deactivated. The launch market is the country of the brand's address. Supported countries include the US, UK, Austria, Bangladesh, Brazil, DR Congo, France, Germany, India, Italy, Mexico, the Netherlands, Nigeria, Norway, Portugal, Singapore, South Africa, Spain and Sweden. Spanish carriers have paused new launches for now; Spanish agents are filed once they reopen.
#Sending messages
You can send once the agent is testing (to accepted test phones) or live (to any RCS phone). Pass text for a plain message or content for anything richer.
curl -X POST https://api.pozy.ai/v1/rcs/messages \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: delivery-A10423-out" \
-d '{
"agentId": "66c4d2e1f0a9b8c7d6e5f4a3",
"to": "+13105551234",
"content": {
"type": "text",
"text": "Your parcel A-10423 is out for delivery today.",
"suggestions": [
{ "type": "reply", "text": "Track it", "postbackData": "track:A-10423" },
{ "type": "openUrl", "text": "Change delivery", "url": "https://acme.example.com/d/A-10423" },
{ "type": "dial", "text": "Call driver", "phoneNumber": "+14155550142" }
]
},
"fallbackText": "Your parcel A-10423 is out for delivery today. Track: https://acme.example.com/d/A-10423"
}'const res = await fetch("https://api.pozy.ai/v1/rcs/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Idempotency-Key": "delivery-A10423-out",
"Content-Type": "application/json",
},
body: JSON.stringify({
agentId: "66c4d2e1f0a9b8c7d6e5f4a3",
to: "+13105551234",
content: {
type: "text",
text: "Your parcel A-10423 is out for delivery today.",
suggestions: [
{ type: "reply", text: "Track it", postbackData: "track:A-10423" },
{ type: "openUrl", text: "Change delivery", url: "https://acme.example.com/d/A-10423" },
{ type: "dial", text: "Call driver", phoneNumber: "+14155550142" },
],
},
fallbackText: "Your parcel A-10423 is out for delivery today. Track: https://acme.example.com/d/A-10423",
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.id, data.conversationId);import os, requests
resp = requests.post(
"https://api.pozy.ai/v1/rcs/messages",
headers={
"Authorization": f"Bearer {os.environ['POZY_API_KEY']}",
"Idempotency-Key": "delivery-A10423-out",
},
json={
"agentId": "66c4d2e1f0a9b8c7d6e5f4a3",
"to": "+13105551234",
"content": {
"type": "text",
"text": "Your parcel A-10423 is out for delivery today.",
"suggestions": [
{"type": "reply", "text": "Track it", "postbackData": "track:A-10423"},
{"type": "openUrl", "text": "Change delivery", "url": "https://acme.example.com/d/A-10423"},
{"type": "dial", "text": "Call driver", "phoneNumber": "+14155550142"},
],
},
"fallbackText": "Your parcel A-10423 is out for delivery today. Track: https://acme.example.com/d/A-10423",
},
)
print(resp.json())content.type picks the layout. Every type takes up to 11 suggestions (buttons) under the message.
content.type |
What it sends | Options |
|---|---|---|
text |
Text, up to 3,072 characters | suggestions |
media |
An image or video on its own | media.url, thumbnailUrl, height (SHORT, MEDIUM, TALL) |
card |
Media, a title (200 characters), a description (2,000) and up to 4 buttons on the card | orientation (VERTICAL, HORIZONTAL), thumbnailAlignment |
carousel |
2 to 10 cards to swipe through | cardWidth (SMALL, MEDIUM) |
Button type |
On tap | Also send |
|---|---|---|
reply |
The person replies with the button's label | postbackData |
openUrl |
A web page opens | url; optionally application (BROWSER or WEBVIEW) and webviewViewMode |
dial |
Starts a call | phoneNumber in E.164 |
viewLocation |
Opens a map pin | latitude, longitude, optional label |
shareLocation |
The phone offers to send its location | Nothing |
calendarEvent |
The phone offers to save an event | startTime, endTime, title (100 characters); optionally description (500) |
Keep labels to 25 characters. postbackData is your own string of up to 2,048 characters (JSON works) that Pozy hands back as is on a tap; without it you get the label. Set ttlSeconds if a message that has not been delivered by then should be dropped.
#SMS fallback
Set smsFallbackFrom on the agent to one of your numbers that can text, and phones without RCS still get the message, as an SMS from that number. The SMS body is your fallbackText if you send one; if not, Pozy uses the message text, or for a card its title, description and media link. With no fallback number, phones without RCS get nothing.
To check ahead of time, call Check RCS capability with up to 100 numbers. Each result has rcsCapable and the rich features the phone supports.
#Media requirements
| Item | Requirement |
|---|---|
| Media | A public URL for the image or video, with an optional thumbnailUrl |
| Agent logo | 224 x 224 px, under 50 KB (uploads are cropped and compressed to fit) |
| Agent banner | 1440 x 448 px, under 200 KB |
| Brand colour | A six-digit hex colour that reaches a 4.5:1 contrast ratio against white |
#Limits
| Limit | Value |
|---|---|
| Buttons | 11 per message, labels up to 25 characters |
| Card | Title 200 characters, description 2,000, 4 buttons |
| Carousel | 2 to 10 cards |
| Capability check | 100 numbers per call |
| Agent profile | displayName up to 40 characters, description up to 100 |
| Agents | One open agent per profile |
A few things are refused. You cannot send while the agent is in neither testing nor live. The brand contact cannot be a Gmail address or a generic info@ or support@ mailbox. The contact buttons need more than a website. Replies sent from the inbox are plain RCS text only; for cards and buttons, use POST /v1/rcs/messages.
#Analytics
RCS has no post analytics. Each message reports delivery and reads as webhooks, and conversation metrics are in Inbox analytics.
#Inbox
Replies thread into the agent's inbox conversation, keyed by the person's phone number, with platform: "rcs". Every inbound message fires message.received; the first message of a new thread also fires conversation.started.
| Incoming | Where it shows up |
|---|---|
| A text | text |
A tapped reply button |
The label in both text and metadata.postbackTitle, and the button's postbackData in metadata.postbackPayload |
| A shared location | latitude and longitude under metadata.location |
| A photo, video or file | attachments, stored by Pozy |
Match on postbackPayload, not the label: labels are for people and may change. Your sends report message.delivered, message.read and message.failed, including messages that went out through the SMS fallback. STOP (and STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT) opts the person out and later sends return 409. Only their START opts them back in.
#Pricing
An RCS agent has a setup fee when it is submitted, a one-time fee on US carrier approval, and a monthly fee from the day it goes live (never while it is in review). Messages are billed per message, sent and received, by destination. See Pricing for the current amounts.
#Errors you may see
| Status | Code | Why | What to do |
|---|---|---|---|
403 |
Before launch, only test phones that accepted the invite can receive | Invite that phone as a tester, or wait until the agent is live |
|
403 |
Your plan has no inbox | Upgrade to a plan that includes it | |
409 |
recipient_opted_out |
The person texted STOP | Only their START lets you message them again |
409 |
invalid_resource_state |
Carriers have not created the agent yet | Retry once it reaches testing or live |
409 |
A reused brand was rejected, or the change is no longer allowed | Send a new brand. Once filed, only smsFallbackFrom can change |
|
404 |
The agent is not in your workspace | Check agentId |
|
422 |
USAGE_BILLING_REQUIRED |
Billing is not usage-based | Turn on usage-based billing |
402 |
No card on file | Add a card |
Errors covers the error format.
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/rcs/brands |
List RCS brands |
GET /v1/rcs/agents |
List RCS agents |
POST /v1/rcs/agents |
Request an RCS agent |
GET /v1/rcs/agents/{agentId} |
Get an RCS agent |
PATCH /v1/rcs/agents/{agentId} |
Update an RCS agent |
DELETE /v1/rcs/agents/{agentId} |
Deactivate an RCS agent |
POST /v1/rcs/agents/{agentId}/launch-request |
Send the launch filing |
GET /v1/rcs/agents/{agentId}/test-devices |
List RCS test phones |
POST /v1/rcs/agents/{agentId}/test-devices |
Invite an RCS test phone |
DELETE /v1/rcs/agents/{agentId}/test-devices/{testDeviceId} |
Remove an RCS test phone |
POST /v1/rcs/assets |
Upload an RCS logo or banner |
POST /v1/rcs/messages |
Send an RCS message |
GET /v1/rcs/capabilities |
Check RCS capability |
#Webhook events
| Event | When it fires |
|---|---|
message.received |
Message received. |
conversation.started |
Conversation started. |
message.delivered |
Message delivered. |
message.read |
Message read. |
message.failed |
Message delivery failed. |
rcs.agent.status_updated |
RCS agent status updated. |