Introduction
© 2021-2025 Lekab Communication Systems AB. Version 6.0.108, 2025-09-29.
This is a Web Service API with the primary purpose of sending Rich Communication Service (RCS) messages over the Google RCS for Business (formerly RBM Rich Business Messaging) network. Currently, the API contains the basic sending functionality, as well as callback notification of status of messages and callback delivery of incoming messages to a user-defined webhook url.
There are two main advantages of RCS messages over SMS messages. First, the messages are "rich" in the sense that they can also contain media (image, video, sound) and certain combinations of media and text known as cards and carousels of cards. They can also contain suggestion buttons which can be clicked to generate replies or start actions, like opening a url, calling a number or opening the map at a position. Secondly, the messages are always sent from a registered agent, which has been vetted by Google and the user’s operator for use purpose and cannot be easily "spoofed", so that the end-user knows that the message is from a trusted, known source.
Rich Communication Service messages according to the Universal Profile 2.x RCS standard are currently supported, and Universal Profile 3.0 standard features will be implemented as they are rolled out by Google RCS for Business.
Similar to SMS, an RCS message is received in the standard Messages app and does not need the recipient to download a dedicated app (toggling a setting may be needed). Message reception is today limited to Android phones, but iOS phones are expected to get RCS capability soon. With RCS currently only supported on Android/Google handsets, our service has fallback functionality to the SMS channel, where sending goes through our own SMS gateway and not through Google. We have plans to expand to other channels in the future.
User accounts and billing are integrated with our SMS gateway, and we handle communication with Google and facilitate registration of sending agents with the telephone operators.
Each message can only be of one content type. The service supports the message content types offered by Google at the time of writing: a text of up to 3000 bytes, one piece of media, a single card or a carousel of 2 to 10 cards. Each message can also have a number (currently up to 11) of suggestions.
If a message is sent with instructions to first try the RCS channel, and RCS is not available for that agent to that phone, fallback happens, i.e. the next channel on the list of channels is tried (currently, this will be SMS if present). The user may supply a dedicated SMS message text which will be sent instead of the RCS message upon fallback. If an SMS text is not present, the text-type message content will be used, and if that is also not present due to the RCS message not being of the text type, sending will fail. Note that SMS sending requires specification of a sender id (alphanumeric or phone number) or borrowing a sender id from one of our number pools (Two-way SMS).
We also offer automatic "up-lift" of SMS messages where a user’s messages sent from any of our standard SMS APIs with a specific sender id are sent as RCS text messages from the user’s agent, with fallback to the original SMS if RCS did not work, today probably due to an i(ncompatible) Phone. Up-lift setup and terms of sale are outside the scope of this manual.
Google RCS for Business defines "Basic RCS" as a text-type message without any suggestion buttons, and with a text that fits into 160 bytes of UTF-8 encoding. In that universal standard encoding of the Internet, English letters take 1 byte, European umlauts take 2 bytes, Chinese characters 3 bytes and emojis (as well as Ancient Egyptian hieroglyphs) 4 bytes. The main feature of a Basic RCS is that it is delivered at a lower cost than other RCS for Business messages, and is perfect for the kind of applications where an SMS would be used in the old days of yore. The advantage here with RCS is the spoof-proof sender agent, which lends greater trust to security related messages, for instance, a one-time password login sequence where the message content is a short text string.
The implementation so far includes the endpoint /send
, with the obvious purpose, as well as
the endpoint /seturl
which is used to set the urls to which receipts and incoming messages should
be forwarded.
The endpoints support only POST calls where the HTTP
POST
request body is a
JSON document and returns responses in the HTTP
response body as JSON documents.
The standard character encoding of JSON is UTF-8, and the API does not recognize any
other encodings (ISO-8859-1, Windows-1252 or other).
The formats of the input and output json documents are described below.
Authentication methods available for requests
Every request to the service must include authentication, i.e.
username and password (or equivalent). For POST
requests these can be given
in the (automatically HTTPS = SSL/TLS
encrypted) HTTP
headers.
We offer four different alternative ways of supplying these login credentials:
-
Username and password can be given as standard Basic authentication, in which the header
Authorization
should have the valueBasic + token
, where the token is theBase64
encoding of (aUTF-8
byte array representation of)username:password
. Heretestuser:testpass
will be encoded asdGVzdHVzZXI6dGVzdHBhc3M=
, and the headerAuthorization
should have the valueBasic dGVzdHVzZXI6dGVzdHBhc3M=
with a single space between the word Basic and theBase64
token. -
Username and password can be given in the HTTP headers,
X-Lekab-Userid
andX-Lekab-Password
, respectively. The values have to be theBase64
encoding of (aUTF-8
byte array representation of) the username or password to allow non-US-ASCII characters. Here testuser will be encoded asdGVzdHVzZXI=
and testpass asdGVzdHBhc3M=
-
An API key obtained from Lekab or by self-service in the web portal can be used as a query parameter
key
or as the value of the headerX-API-Key
. The length of the key varies depending on the length of the username (which is contained within the key).TUxVOmRHVnpkSFZ6WlhJOjlKUUczTXU2TVZVU1Exd3Y
is a possible example key for the usernametestuser
. The key is independent of the account password. -
A Bearer token obtained from our Oauth2 server can be used, where the header
Authorization
should have the valueBearer token
, with a single space between the word Bearer and the token. See below for instructions how to call our oauth server.
It is possible to disallow the username+password based authentication methods
by setting the role DISALLOW_BASIC
for the API user, forcing the use of
either API-key or Oauth authentication.
If any of the alternative methods of authentication are used, parameter values pertaining to other authentication methods should be omitted or set to the empty string "".
How to obtain an oauth token from our oauth server
The /auth/api/v1/token
endpoint is used to request an OAuth 2.0 Bearer Token.
POST request examples
Using Basic authentication (username and password in a Base64 from UTF-8 encoded header)
curl -X POST --location "https://secure.lekab.com/auth/api/v1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=client_credentials' \
--basic --user username:password
Using username and password in body (client_id and client_secret url-encoded from UTF-8)
curl -X POST --location "https://secure.lekab.com/auth/api/v1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=client_credentials&client_id=username&client_secret=password'
Note that both these uses of username:password authentication are available for the
call even if the user has the role DISALLOW_BASIC
set. We do not support refresh
tokens for the oauth server.
Explanation of parameters
POST param | query param value | Description |
---|---|---|
grant_type |
client_credentials (string) |
The requested grant type. Only |
client_id |
user name (string) |
User name if Basic authentication is not used. Url encoded from utf-8 if necessary |
client_secret |
user password (string) |
Password if Basic authentication is not used. Url encoded from utf-8 if necessary |
1. The /send
endpoint
Used for sending rich messages over the available channels (currently RCS and SMS).
All sending is from the sending user account’s RCS for Business agent, registered and approved by Google and the telephone operators in the destination country. We will assist in the registration and approval process. We can also assign a test agent (where a small number of phone numbers are pre-registered as allowed destinations) for testing. Terms of sale, contracts, approval, credit check etc. are not covered by this manual. We will associate the user account with the RCS agent, so the API sender needs only know the login credentials of the sending user, while we handle all communication with Google.
Channels are specified as a comma (",") separated list starting with the channel to try first. Channels cannot be repeated. Therefore, the currently possible channel specifications are "RCS", "SMS", "RCS,SMS" and "SMS,RCS". It is likely, that "RCS,SMS" (RCS with fallback to SMS) and "RCS" (RCS without fallback) will be of primary interest to API users.
A message can be addressed to a number of recipients, and the channels will be tried
as specified for each recipient until a successful sending is achieved. Recipient addresses
(addresses are phone numbers for both RCS and SMS)
are given separated by semi-colons (";"). In the event that a given recipient has different
numbers for different channels, and to later accommodate channels that do not use phone numbers,
the address to use in each channel can be given separated by commas. If the number of addresses
given for a recipient are fewer than the number of channels specified, the last address is repeated
(This is the most common case, where the RCS number given is also used for SMS). Phone numbers must
include a country code. We allow up to 400 recipients per HTTP POST
call to /send
, but
recommend substantially fewer. Functionality for accommodating large batch sending is under
consideration.
While most parameters for the /send
endpoint need only a short description, the actual rich
message is a JSON
structure with strict rules, and therefore the richMessage
field (in the
top level of the JSON
document which makes up the body of the HTTP POST
call) will be explained
in its own section below.
1.1. POST request example: a text type message without suggestions
https://secure.lekab.com/rime/send
Sending to one recipient with authentication in the headers and the following as contents of the HTTP body. A typical Basic RCS for Business.
{
"channels" : "RCS",
"address" : "46701234567",
"richMessage" : {
"text":"Your access key is 12345678"
}
}
1.2. POST request example: single card type message, suggestions in the card and after
https://secure.lekab.com/rime/send
Sending to two recipients with authentication in the headers and the following as contents of the HTTP body.
{
"channels" : "RCS,SMS",
"address" : "46701234567;46702345678",
"smsText" : "This SMS would have been a RCS card message if your phone allowed it",
"smsSender" : "MYCOMPANY",
"richMessage" : {
"richCard" : {
"standaloneCard" : {
"cardOrientation" : "HORIZONTAL",
"thumbnailImageAlignment" : "LEFT",
"cardContent" : {
"title": "A question of zoology",
"description": "Is this a cat?",
"media": {
"height" : "MEDIUM",
"contentInfo": {
"fileUrl" : "https://upload.wikimedia.org/wikipedia/commons/2/2b/Mainecoon1.png"
}
},
"suggestions": [
{ "reply" : { "text" : "It is a cat!", "postbackData" : "CAT YES"} },
{ "reply" : { "text" : "No, it is not!", "postbackData" : "CAT NO"} }
]
}
}
},
"suggestions":[
{ "action" : {
"text" : "Show famous place on map",
"postbackData" : "TOUR EIFFEL",
"viewLocationAction" : { "latLong" : {"latitude" : 48.858093, "longitude" : 2.294694}, "label" : "Voila"} }
}
]
}
}
1.3. Explanation of parameters for /send
POST json key | json value | |
---|---|---|
appname |
string |
Selects one of several sender agents for a user account. Omit this to use default appname "" if we have not supplied an appname to use. |
channels |
string |
channel try order: "RCS", "SMS", RCS,SMS" or "SMS,RCS" |
address |
string |
Recipient id, for RCS and SMS channels: international phone number (country+area+subscriber) no intl prefix like 00 etc To send to several recipients, separate with semicolons. To use different addresses for different channels, separate with commas within each recipient. |
smsText |
string |
text to use instead of rich message if the SMS channel is used |
smsSender |
string |
SMS sender id, required if not two-way SMS, cannot be reserved for other company |
smsTwoway |
true or false |
SMS sender is taken from user’s number pool for receiving answers, default false |
richMessage |
json object, see below |
message to send through rich channel, see below |
postbackSuffix |
string |
User defined string that is sent back to agent when any of the suggestion buttons in this message is pressed |
trafficType |
string |
May be required in the future when sending messages via a multi-use agent, to define which use is selected. Currently one of "AUTHENTICATION", "TRANSACTION", "PROMOTION", "SERVICEREQUEST", "ACKNOWLEDGEMENT". Subject to change according to Google RCS for Business. |
ttl |
integer |
Message time to live in minutes, forwarded to Google or SMS operator when sending |
conversationid |
string |
Feature of our SMS service, where receipts and two-way answers to this message are marked with this id |
costcenter |
string |
For billing, used to separate bill for one account into portions e.g. by department |
metadata |
string |
identifying data not sent to phone but echoed back with status reports and replies |
properties |
json object/dictionary |
{"key1":"value1", "key2":"value2", …} for future use |
1.4. HTTP response to /send
A successful request will return 200 OK
and a json document of
the following format. Note that the request for sending is successful even if the
send status of the message sending turns out to be failed. A successfully sent
message may sometimes not be delivered due to external factors (phone turned off, phone
subscription expired, no such subscriber, phone lacking capability of receiving channel,
opt-out from channel), but such requests will be reported as successful
by this endpoint, and the later fortune of the message can instead be followed via the
delivery status callbacks to the status webhook url.
The Content-Type
header of the response
is application/json
for all responses.
{
"resultText" : "At least one message sent OK",
"sent" : [ {
"result" : "OK",
"id" : "1225831041592336384",
"channels" : "RCS,SMS",
"addresses" : "46701234567,46701234567"
}, {
"result" : "OK",
"id" : "1225831041684611072",
"channels" : "RCS,SMS",
"addresses" : "46702345678,46702345678"
} ]
}
An unsuccessful request will give an appropriate 4xx
or 5xx
HTTP
result code,
and a json document of the following form. Any result which is not "OK"
is an error and means no message was sent.
{
"result" : "ERROR",
"error" : "Unauthorized"
}
In this example, the result code is 401
, as appropriate for "Unauthorized".
Syntax errors in the message specification will yield 400
and "Validation error".
1.5. Explanation of response to /send
POST json key | json value (strings quoted) | |
---|---|---|
resultText |
string |
"At least one message sent OK" if successful |
sent |
json list |
One list element per recipient |
result |
string |
OK if successful |
id |
string |
id of the message used for future references to this message, e.g. status callbacks |
channels |
string |
Comma separated channels to try sending this message |
addresses |
string |
Comma separated addresses to use for each channel |
error |
string |
error description if not successful |
1.6. Example Python 3 code
import json
import requests
import base64
authstringarray=("testuser" + ":" + "testpass").encode('utf-8')
authbase64=base64.b64encode(authstringarray).decode('utf-8')
headers={'Authorization':'Basic ' + authbase64}
sendreq = {}
sendreq["channels"] = "RCS,SMS"
sendreq["address"] = "46701234567;46702345678"
sendreq["richMessage"] = {
"text":"Can you work extra hours on Christmas Day?",
"suggestions":[
{ "reply" : { "text" : "Yes" } },
{ "reply" : { "text" : "No" } }
]
}
sendreq["metadata"] = 'Xmas 2026 employee 1234'
sendreq_json = json.dumps(sendreq)
url = 'https://secure.lekab.com/rime/send'
response = requests.post(url, data=sendreq_json, headers=headers)
sendresp = response.json()
if sendresp["sent"]:
for m in sendresp["sent"]:
print("Successful message id "+ m['id'] + " to " + m['addresses'] + " via " + m['channels'])
else:
print("Send error: " + sendresp["error"])
will output
Successful message id 1225831041592336384 to 46701234567,46701234567 via RCS,SMS
Successful message id 1225831041684611072 to 46702345678,46702345678 via RCS,SMS
2. Details of how to construct the richMessage
json object
The rules for RCS Business Messages say that each message must have only one content type. This service supports the message content types offered by Google at the time of writing: a text of up to 3000 bytes, one piece of media, a single card or a carousel of 2 to 10 cards. Each message can also have a number (currently up to 11) of suggestions.
This means that the richMessage
object can have a maximum of two top level fields,
where one gives the content type of the message. This field may be "text" for a text
message, "contentInfo" for a media message or "richCard" for a message with a
single card or carousel of cards. The optional second field "suggestions" is a
list of json objects where each object is a suggestion, i.e. a button to press to
reply to the sending agent with a pre-determined text or to start an action on the
recipient’s phone, like opening the browser to a url or opening the phone calling app
with a phone number filled in. The top level list of suggestions can have a maximum
of 11 suggestions, but for most RCS-compatible mobile handsets, we do not recommend more
than about 3-4 suggestions due to graphical awkwardness of longer lists. The maximum
of 11 suggestions does not include suggestions inside cards, where each card can have up
to 4 suggestions.
2.1. Structure of the four message types
2.1.1. Text messages
A text message consists of a text of up to 3000 bytes, and an (optional) list of suggestions. If the message is 160 bytes or shorter and there are no suggestions, it is sent at a lower cost, so called "Basic RCS", if it is longer or has at least one suggestion, the standard message price applies. Note that the text can contain any UTF-8/Unicode characters, including emojis, but the current RCS message standard (Universal Profile 2.x) does not include any formatting (often, confusingly, called "rich text") for specifying font, bold, italic or underline.
{
"text":"Yes or no, that is the question?",
"suggestions":[
{ "reply" : { "text" : "👍 YES", "postbackData" : "YES"} },
{ "reply" : { "text" : "NO 👎", "postbackData" : "NO"} }
]
}
Fields and subfields:
json key | json value | |
---|---|---|
text |
string |
Message text UTF-8, max 3000 bytes (Basic RCS 160 bytes) |
suggestions |
Optional JSON list of replies or actions or mixed |
0 to 11 suggestions, see below |
2.1.2. Media messages
A media message contains a url reference to a server where the piece of media is served. A
local media server will be added to this API in the near future. The RCS for Business standard actually
includes three different ways of specifying a media message, but two of them refer to messages
that are pre-uploaded to Google, and we do not support that uploading process. Therefore, the
media url will be contained in a contentInfo
object. In addition, an (optional) list of
suggestion buttons can be included, but no title text or description text. For such combination
messages, the card types are appropriate.
The media in a media file may be a picture (jpeg, png etc.), a sound file (mp3 etc.), a video clip (mp4, etc.) or a PDF file. See separate documentation for allowed types, data and pixel sizes and other limitations. Note that while RCS sending is fully encrypted, Google can read all text and all files sent, with no way of sending files that are encrypted so that Google cannot read them.
{
"contentInfo" : {
"fileUrl" : "https://pictureserver.com/dogs/German_Shepherd.jpg",
"thumbnailUrl" : "https://pictureserver.com/dogs/thumbnails/German_Shepherd_tn.jpg",
"forceRefresh" : true;
},
"suggestions":[
...
]
}
Fields and subfields:
json key | json value | |
---|---|---|
contentInfo |
json object |
Contains the media reference |
fileUrl |
string |
Url where the media file is served |
thumbnailUrl |
string |
Optional url where a thumbnail file is served |
forceRefresh |
boolean |
Default false, instruct Google to reload the media file if cached from before |
suggestions |
Optional JSON list of replies or actions or mixed |
0 to 11 suggestions, see below |
2.1.3. Single card messages
A single card message contains a single card, and an optional list of suggestions.
The card can have a title string of up to x characters, a description text of up to y characters,
a piece of media specified as a url in a contentInfo
object, and an (optional) list of
up to 4 internal suggestions inside the card. Depending on the size and type
of the media, it is usually displayed in the card as a thumbnail representation which can be clicked
to display the media in full-window mode. Exact display geometry is a function of the Messages app
version on the phone and is also subject to change.
{
"richCard" : {
"standaloneCard" : {
"cardOrientation" : "HORIZONTAL",
"thumbnailImageAlignment" : "LEFT",
"cardContent" : {
"title": "A nice picture",
"description": "Is this a cat?",
"media": {
"height" : "MEDIUM",
"contentInfo": {
"fileUrl" : "https://upload.wikimedia.org/wikipedia/commons/2/2b/Mainecoon1.png"
}
},
"suggestions": [
{ "reply" : { "text" : "It is a cat!", "postbackData" : "CAT YES"} },
{ "reply" : { "text" : "No, it is not!", "postbackData" : "CAT NO"} }
]
}
}
},
"suggestions":[
...
]
}
Fields and subfields:
json key | json value | |
---|---|---|
richCard |
json object |
Wraps the card or carousel |
standaloneCard |
json object |
Wraps the single card |
cardOrientation |
string |
Geometry of card display: "HORIZONTAL" or "VERTICAL" |
thumbnailImageAlignment |
string |
Where in the card is media thumbnail: "LEFT" or "RIGHT" |
cardContent |
json object |
Wraps the content of one card (the only one here) |
title |
string |
Title of the card |
description |
string |
Text of the card |
media |
json object |
Wraps media part of content of this card |
height |
string |
Media height "SHORT" (112 DP), "MEDIUM" (168) or "TALL" (264) |
contentInfo |
json object |
Wraps the media urls like in a media message |
fileUrl |
string |
Url where the media file is served |
thumbnailUrl |
string |
Optional url where a thumbnail file is served |
forceRefresh |
boolean |
Default false, instruct Google to reload the media file if cached from before |
suggestions |
In-card JSON list of replies or actions or mixed |
0 to 4 suggestions, see below |
suggestions |
Optional JSON list of replies or actions or mixed |
0 to 11 suggestions, see below |
2.1.4. Carousel messages
A carousel message contains 2-10 cards which can have the same types of content as the card in the single card message, as well as an (optional) list of suggestions. The carousel displays the cards in a scrollable fashion so that one of the cards is "on top" and visible. Depending on the size and type of the media, it is usually displayed in the card as a thumbnail representation which can be clicked to display the media in full-window mode. Exact display geometry is a function of the Messages app version on the phone and is also subject to change. The Lovecraft-inspired example below has two cards in the list.
{
"richCard" : {
"carouselCard" : {
"cardWidth" : "MEDIUM",
"cardContents" : [
{
"title":"In picturesque Arkham",
"description":"Cthulhu Hotel service on call",
"media":{
"height" : "MEDIUM",
"contentInfo" : {
"fileUrl":"https://upload.wikimedia.org/wikipedia/commons/6/63/Octopus_marginatus.jpg"
}
},
"suggestions":[
{
"action" : {
"text" : "Book a room",
"postbackData" : "BOOK CTHULHU",
"openUrlAction" : { "url" : "https://creepyhotels.com/cthulhu" }
}
},
{
"action" : {
"text" : "Call of Cthulhu",
"postbackData" : "CALL CTHULHU",
"dialAction" : { "phoneNumber" : "+46701234567" } # the called phone does NOT have to be Android
}
}
]
},
{
"title":"Close to Miskatonic U",
"description":"Tentacle Inn creature comforts",
"media":{
"height" : "MEDIUM",
"contentInfo" : {
"fileUrl":"https://upload.wikimedia.org/wikipedia/commons/d/dd/Loligo_vulgaris.jpg"
}
},
"suggestions":[
{
"action" : {
"text" : "Book a room",
"postbackData" : "BOOK TENTACLE",
"openUrlAction" : { "url" : "https://creepyhotels.com/tentacle" }
}
},
{
"action" : {
"text" : "Call the hotel",
"postbackData" : "CALL TENTACLE",
"dialAction" : { "phoneNumber" : "+46702345678" } # the called phone does NOT have to be Android
}
}
]
}
]
}
},
"suggestions" : [
{
"reply" : { "text" : "Andover Area", "postbackData" : "MENU ANDOVER"}
},
{
"reply" : { "text" : "Haverhill Area", "postbackData" : "MENU HAVERHILL"}
},
{
"reply" : { "text" : "Salem Area", "postbackData" : "MENU SALEM"}
}
]
}
Fields and subfields:
json key | json value | |
---|---|---|
richCard |
json object |
Wraps the card or carousel |
carouselCard |
json object |
Wraps the carousel |
cardWidth |
string |
Geometry of card display: "SMALL" or "MEDIUM" |
cardContents |
list of json objects |
List of contents of 2-10 cards in carousel |
title |
string |
Url where the media file is served |
description |
string |
Url where the media file is served |
media |
json object |
Wraps media part of content of this card |
height |
string |
Media height "SHORT" (112 DP), "MEDIUM" (168) or "TALL" (264), where TALL is not allowed for carousels with card width SMALL |
contentInfo |
json object |
Wraps the media urls like in a media message |
fileUrl |
string |
Url where the media file is served |
thumbnailUrl |
string |
Optional url where a thumbnail file is served |
forceRefresh |
boolean |
Default false, instruct Google to reload the media file if cached from before |
suggestions |
In-card JSON list of replies or actions or mixed |
0 to 4 suggestions, see below |
suggestions |
Optional JSON list of replies or actions or mixed |
0 to 11 suggestions, see below |
2.1.5. Available suggestions for the suggestion
list parameters
Suggestions can be either of type reply
or of type action
, and can
be mixed as desired within a suggestions list. A message has a suggestion list of
0 to 11 suggestions at the end of the message, while each card contains a suggestion
list of 0 to 4 suggestions. When a user selects a suggestion, an incoming message is
sent to the sender agent, where it is stored in our database and forwarded to the
sender account by callback to a webhook url (under implementation).
Suggestion reply
Sends an answering message with a preset content to the sender agent.
{
"reply" : {
"text" : "Salem Area",
"postbackData" : "MENU SALEM"
}
}
Fields and subfields:
json key | json value | |
---|---|---|
reply |
json object |
Wraps the reply |
text |
string |
Button text of the reply suggestion. Also incoming message text if postbackData is empty |
postbackData |
string |
Incoming message text if present |
Suggestion dialAction
Opens the telephone app on the recipient’s mobile handset with a given number pre-filled. The phone to call does not have to be Android or even a mobile phone. Also informs the sending agent with an incoming action message.
All actions have the button text
field, the postbackData
field and the fallbackUrl
field
where the action is replaced with opening the mobile handset browser at the given url if the
mobile handset does not support the originally intended action. Probably the fallbackUrl will not
be of much use, since most actions are things mobile handsets usually do. In addition each action
has its own xxxAction
field defining what action it is and containing the parameters for the
specific action.
{
"action" : {
"text" : "👊 Call Erik",
"dialAction" : { "phoneNumber" : "+46701234567" }
}
}
Fields and subfields:
json key | json value | |
---|---|---|
action |
json object |
Wraps an action suggestion |
text |
string |
Button text of the action suggestion. Also incoming message text if postbackData is empty |
postbackData |
string |
Incoming message text if present |
fallbackUrl |
string |
Url to open if mobile phone does not support this action (unlikely) |
dialAction |
json object |
Wraps a dialAction suggestion |
phoneNumber |
string |
Number to present in the dialing app |
Suggestion viewLocationAction
Opens the user’s default map app and selects the specified location. Also informs the sending agent with an incoming action message.
All actions have the button text
field, the postbackData
field and the fallbackUrl
field
where the action is replaced with opening the mobile handset browser at the given url if the
mobile handset does not support the originally intended action. Probably the fallbackUrl will not
be of much use, since most actions are things mobile handsets usually do. In addition each action
has its own xxxAction
field defining what action it is and containing the parameters for the
specific action.
{
"action" : {
"text" : "Famous place",
"postbackData" : "TOUR EIFFEL",
"viewLocationAction" : {
"latLong" : {
"latitude" : 48.858093,
"longitude" : 2.294694
},
"label" : "Voila"
}
}
}
Fields and subfields:
json key | json value | |
---|---|---|
action |
json object |
Wraps an action suggestion |
text |
string |
Button text of the action suggestion. Also incoming message text if postbackData is empty |
postbackData |
string |
Incoming message text if present |
fallbackUrl |
string |
Url to open if mobile phone does not support this action (unlikely) |
viewLocationAction |
json object |
Wraps a viewLocationAction suggestion |
latLong |
json object |
Wraps a latitude+longitude object |
latitude |
float |
Latitude -90.0000 to +90.0000 Southern hemisphere is negative |
longitude |
float |
Longitude -180.0000 to +180.0000 West of Greenwich is negative |
label |
string |
Map marker label |
Suggestion createCalendarEventAction
Opens user’s default calendar app and starts the new calendar event flow with the event data pre-filled. Also informs the sending agent with an incoming action message.
All actions have the button text
field, the postbackData
field and the fallbackUrl
field
where the action is replaced with opening the mobile handset browser at the given url if the
mobile handset does not support the originally intended action. Probably the fallbackUrl will not
be of much use, since most actions are things mobile handsets usually do. In addition each action
has its own xxxAction
field defining what action it is and containing the parameters for the
specific action.
{
"action" : {
"text" : "Save the date",
"createCalendarEventAction" : {
"startTime": "2026-04-30T17:00:00Z",
"endTime": "2026-04-30T21:00:00Z",
"title": "Valpurgis night bonfire",
"description": "A good time will be had by all"
}
}
}
Fields and subfields:
json key | json value | |
---|---|---|
action |
json object |
Wraps an action suggestion |
text |
string |
Button text of the action suggestion. Also incoming message text if postbackData is empty |
postbackData |
string |
Incoming message text if present |
fallbackUrl |
string |
Url to open if mobile phone does not support this action (unlikely) |
createCalendarEventAction |
json object |
Wraps a calendarEventAction suggestion |
startTime |
string |
Timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z" |
endTime |
string |
Timestamp in RFC3339 UTC "Zulu" format, as startTime |
title |
string |
Event title |
description |
string |
Event description |
Suggestion openUrlAction
Opens the user’s default web browser app to the given URL. Also informs the sending agent with an incoming action message.
All actions have the button text
field, the postbackData
field and the fallbackUrl
field
where the action is replaced with opening the mobile handset browser at the given url if the
mobile handset does not support the originally intended action. Probably the fallbackUrl will not
be of much use, since most actions are things mobile handsets usually do. In addition each action
has its own xxxAction
field defining what action it is and containing the parameters for the
specific action.
{
"action" : {
"text" : "Book the hotel",
"postbackData" : "BOOK CTHULHU",
"openUrlAction" : {
"url" : "https://creepyhotels.com/cthulhu"
}
}
}
Fields and subfields:
json key | json value | |
---|---|---|
action |
json object |
Wraps an action suggestion |
text |
string |
Button text of the action suggestion. Also incoming message text if postbackData is empty |
postbackData |
string |
Incoming message text if present |
fallbackUrl |
string |
Url to open if mobile phone does not support this action (absurdly unlikely) |
openUrlAction |
json object |
Wraps an openUrlAction suggestion |
url |
string |
Url to open in browser |
Suggestion shareLocationAction
Opens the RCS app’s location chooser so the user can pick a location to send to the agent. Also informs the sending agent with an incoming action message, when the suggestion button is pressed. The location data arrives as a second incoming message of type LOCATION, when the user shares it.
All actions have the button text
field, the postbackData
field and the fallbackUrl
field
where the action is replaced with opening the mobile handset browser at the given url if the
mobile handset does not support the originally intended action. Probably the fallbackUrl will not
be of much use, since most actions are things mobile handsets usually do. In addition each action
has its own xxxAction
field defining what action it is and containing the parameters for the
specific action.
{
"action" : {
"text" : "Where are you?",
"shareLocationAction" : { }
}
}
Fields and subfields:
json key | json value | |
---|---|---|
action |
json object |
Wraps an action suggestion |
text |
string |
Button text of the action suggestion. Also incoming message text if postbackData is empty |
postbackData |
string |
Incoming message text if present |
fallbackUrl |
string |
Url to open if mobile phone does not support this action (absurdly unlikely) |
shareLocationAction |
json object |
Wraps an openUrlAction suggestion, has no parameters |
3. Opting in and out and blocking of numbers
The phone user can always send in a text message to the service with the text "STOP" to get added to the blocklist of that specific sender account. If the user later sends in a text message with the text "START", the blocking is removed.
Google RCS for Business has announced that there will be unsubscribe and resubscribe buttons in the Messages app to achieve the same purpose, but currently any use of such buttons is not forwarded to our system in the Google RCS for Business announced format (or any other format), so we expect that in later versions. The unsubscribe and resubscribe buttons in the current version seem to affect settings inside Google RCS for Business which we currently cannot control or access. So a customer that unsubscribes with a Google button cannot yet resubscribe by sending in "START", but will need to resubscribe with the corresponding Google button.
Opting in and out does not affect our security-related blocking of country codes to which an account cannot send. These blocking rules will be set up so that sending is only attempted to countries where the sending RCS agent is registered, verified and launched. This by default includes the SMS channel, but user accounts could possibly relax the country code blocking rules, to achieve fallback to SMS when RCS sending fails to a foreign country.
4. Callback of receipts and incoming
In order to track status of sent messages and receive incoming responses, the API user can
set up notification of these events through callback (pushing, event-driven reporting) to
a user-specified callback URL (webhook) where the user’s system is listening for HTTP
requests. The user’s system must respond with a 200 OK
(or similar) HTTP
response, or
we will try to resend the callback.
When our system receives a status receipt from the sending channel, and that receipt does not imply trying another channel for sending, we forward the status receipt to the webhook URL set by the API user. Also, if an incoming message arrives to the RCS agent or to an SMS two-way number, we forward the incoming message to another webhook URL (which may, of course be the same URL as for receipts).
There is currently only one callback notification format, called "JSON". The user can set
callback type "JSON" for receipts and "NONE" for incoming, or vice versa, with the expected
result. There is an HTTP POST
API endpoint for setting the webhook urls.
4.1. The /seturl
endpoint for setting callback webhooks
The endpoint must be called with authentication for the user account that owns the agent. This will be the same authentication as for sending from the agent with the present API. One account is not allowed to set webhooks for another account.
Currently, the endpoint writes the new URL directly in the database, and for every
callback event, the database is read at that time. This is subject to change when API traffic
increases, when some kind of caching of callback URLs will probably be implemented. We discourage
depending on instant URL changes. To distinguish between different message batches, we offer the
metadata
and conversationid
fields in the sent message, which will be returned with the receipt,
and with incoming responses which are estimated to be answers to a given message.
4.1.1. POST request example: setting both endpoints
https://secure.lekab.com/rime/seturl
The example call sets two urls, one for receiving receipts and another for receiving incoming responses.
{
"receipttype" : "JSON",
"receipturl" : "https://mycompany.com/mess/receipts?receiptkey=klm765",
"receiptheaders": "{\"Importantheader\":\"Hello\"}",
"incomingtype" : "JSON",
"incomingurl" : "https://mycompany.com/mess/incoming?incomingkey=abc123"
}
4.1.2. Explanation of parameters for /seturl
POST json key | json value | |
---|---|---|
receipttype |
string |
The output format for receipt callback. "JSON" or "NONE" or null/not set for no change |
receipturl |
string |
The webhook url for receipt callback. Can have url query parameters e.g. for authentication |
receiptheaders |
string of JSON |
JSON string containing Map of String to String, sent as headers e.g. for authentication, only set when url is set |
incomingtype |
string |
The output format for incoming callback. "JSON" or "NONE" or null/not set for no change |
incomingurl |
string |
The webhook url for receipt callback. Can have url query parameters e.g. for authentication |
incomingheaders |
string of JSON |
JSON string containing Map of String to String, sent as headers e.g. for authentication, only set when url is set |
4.1.3. HTTP response to /seturl
A successful request will return 200 OK
and a json document of the following format,
containing the current status of the callback webhook URLs for the user.
The Content-Type
header of the response is application/json
for all responses.
{
"userid" : "12345",
"appname" : "",
"receipttype" : "JSON",
"receipturl" : "https://mycompany.com/mess/receipts?receiptkey=klm765",
"receiptheaders" : "{\"Importantheader\":\"Hello\"}",
"incomingtype" : "JSON",
"incomingurl" : "https://mycompany.com/mess/incoming?incomingkey=abc123"
}
4.2. Format of callback to user webhook of status and incoming
The only callback type choice that results in any callback is "JSON".
4.2.1. Callback of status
For RCS messages, only three statuses can happen: SENDFAIL
, DELIVERED
and READ
.
The SENDFAIL
status is only called back for the last channel in the list of channels to try.
SMS messages can receive other failing statuses from the operators, summed into the
status UNDELIVERABLE
that likewise is only called back if SMS is the last channel to try.
Example callback of status DELIVERED
{
"id": "1249385535177367552",
"userid": "12345",
"appname": "",
"time": "2025-09-08T13:32:24Z",
"channel": "RCS",
"from": "mycompany_abcdabcd_agent",
"to": "46701234567",
"status": "DELIVERED",
"metadata": "This is the metadata",
"conversationid": "ABC123"
}
Example callback of status READ
{
"id": "1249385535177367552",
"userid": "12345",
"appname": "",
"time": "2025-09-08T13:32:30Z",
"channel": "RCS",
"from": "mycompany_abcdabcd_agent",
"to": "46701234567",
"status": "READ",
"metadata": "This is the metadata",
"conversationid": "ABC123"
}
Explanation of fields in status receipt callback
json key | json value | |
---|---|---|
id |
string of long integer |
The id of the message, same as was returned when sending |
userid |
string of long integer |
The id of the user account owning the message |
appname |
string |
Selects one of several sender agents for a user account. Will be the default appname "" if we have not supplied an appname to use. |
time |
string |
Date and time in UTC/Greenwich/Zulu zone, ISO 8601 format with second precision, always ends in Z |
channel |
string |
Channel which the status receipt refers to: RCS or SMS |
from |
string |
Sender agent for RCS, sender id for SMS |
to |
string |
Address (phone number) to which the message was sent in the channel |
status |
string |
One of DELIVERED, READ, SENDFAIL, UNDELIVERABLE |
statusText |
string |
Sometimes explanation of failed status (not always present, depends on what we know) |
metadata |
string |
User-defined data associated with the sent message, not sent to/from Google RCS for Business or SMS operator |
conversationid |
string |
(Legacy) Short user-defined data (up to 255 chars) associated with the sent message, not sent to/from Google RCS for Business or SMS operator |
4.2.2. Callback of incoming
Incoming messages are generated when the recipient of a message uses one of the suggested reply- or action-buttons in the message, or when the user responds with a text, media item or location in the conversation with the agent in the Messages app on the mobile device. Google RCS for Business services are expected to add buttons that generate opt-out (STOP) and opt-in (START) messages, but we have not yet seen such buttons or messages, so users for now have to generate them themselves by sending text messages containing the word "STOP" or "START".
There are currently 8 different types of incoming messages. The incoming message type is found in the field "type"
,
and will be one of REPLY
, ACTION
, STOP
, START
, TEXT
, USERFILE
, LOCATION
, SMS
. Each of the types will
have all the general fields id
, userid
, appname
, time
, channel
, from
, to
, and type
.
If we can make a reasonable guess at which message this message is in response to (either from pressing a
suggestion button which transmits that info in the background, or from guessing the last message of the
conversation), we will also give that message’s id (responseto
), metadata (resptometadata
) and
conversationid (resptoconvid
).
Example callback of incoming of type REPLY
{
"id": "1249385655331594240",
"userid": "12345",
"appname": "",
"time": "2025-09-08T13:32:32Z",
"channel": "RCS",
"from": "46701234567",
"to": "mycompany_abcdabcd_agent",
"responseto": "1249385535177367552",
"type": "REPLY",
"text": "ABCD",
"buttontext": "YES",
"resptometadata": "This is the metadata",
"resptoconvid": "ABC123"
}
Explanation of fields in incoming message callback
json key | json value | |
---|---|---|
id |
string of long integer |
The id of the message in our incoming message table |
userid |
string of long integer |
The id of the user account owning the incoming message |
appname |
string |
Selects one of several agents for a user account. Will be the default appname "" if we have not supplied an appname to use. |
time |
string |
Date and time in UTC/Greenwich/Zulu zone, ISO 8601 format with second precision, always ends in Z |
channel |
string |
Channel which the status receipt refers to: RCS or SMS |
from |
string |
Address (phone number) from which the message was received in the channel |
to |
string |
Receiving agent for RCS, incoming number for SMS |
responseto |
string |
Best guess of which sent message id this is a response to, always correct for buttons |
type |
string |
One of |
text |
string |
Incoming message for most message types, for buttons the postbackdata specified when sending (see below) |
buttontext |
string |
Text on reply or action button that was pressed to generate the message (see below) |
filepayload |
string |
Json object with data (url, size etc) of media file sent in by the user (see below) |
filethumb |
string |
Json object with data (url, size etc) of media file thumbnail usually generated by Google RCS for Business (see below) |
locationlatlong |
string |
e.g. 59.606927;16.567048 latitude and longitude semicolon separated in degrees with decimals (see below) |
postbacksuffix |
string |
Additional text that is sent back from every button in message, defined when sending |
resptometadata |
string |
User-defined data associated with the sent message to which this is probably a response, not sent to/from Google RCS for Business or SMS operator |
resptoconvid |
string |
(Legacy) Short user-defined data (up to 255 chars) associated with the sent message to which this is probably a response, not sent to/from Google RCS for Business or SMS operator |
4.2.3. Explanation of the different incoming message types
-
The
REPLY
message comes from the phone user pressing a reply suggestion button. Thetext
field value will be the postbackdata specified when sending. The button text is also given. -
The
ACTION
message comes from the phone user pressing an action suggestion button, for displaying a url in the browser, setting up a call to a phone number, displaying coordinates on a map, etc. Thetext
field value will be the postbackdata specified when sending. The button text is also given. Note that Google RCS for Business does not supply the exact variety of action in the incoming message, so if that is specifically needed, it should be included in the postback message when sending. -
The
STOP
message arises when the phone user sends a text message with the text "STOP" (any case of letters). It should also come when the phone user presses anUnsubscribe
button, but that is, apparently, not yet implemented by Google RCS for Business. -
The
START
message arises when the phone user sends a text message with the text "START" (any case of letters). It should also come when the phone user presses aSubscribe
button, but that is, apparently, not yet implemented by Google RCS for Business. -
When the RCS user writes a text in the chat, a
TEXT
incoming message is generated. Thetext
field contains the text. -
When the RCS user sends in a media file (picture, video, sound), this gives a
USERFILE
incoming message. The incoming file will be stored in a Google RCS for Business public server, and a url pointing to the file will be found inside the json object of thefilepayload
field while the url to the Google RCS for Business generated thumbnail of the incoming file will be found in thefilethumb
field. We are planning to file away the incoming media from Google to keep it safe and easy to refer to in our own media server, when that is implemented. If the phone user sends both a text and a media file, two messages will be received by the agent. Typical content of theUSERFILE
filepayload
, where thefileUri
can be called to retrieve the media:
{"mimeType":"image/jpeg","fileSizeBytes":422754,"fileUri":"https://rcs-copper-eu.googleapis.com/blob/75b23925-54d6-7654-9515-e5127833eeb1/J7IO2A14B9PMG_0U7WegoGUqOmtX4f8UWEljx3Lo?ct=aW1hZ2bulnBlZw","fileName":"720332000011178328.jpg"}
-
The
shareLocation
action will generate anACTION
incoming message when pressed, and when the user subsequently submits a position (probably from the phone’s GPS), aLOCATION
incoming message with the given location will be generated. TheLOCATION
messagelocationlatlong
field will be a semicolon separated string with the latitude in degrees (with decimals) in the interval -90 to 90 degrees (south is negative), and the longitude in degrees (with decimals) in the interval -180 to 180 degrees (west is negative). Example : "59.327402;18.055316". -
If the messages was transmitted through the SMS channel and the phone user responds to the sender number (or uses a reserved incoming SMS number), an
SMS
incoming message will be generated. The SMS message text will be found in thetext
field.