RCS Templates
Learn how to create and structure RCS templates (TEXT, CARD, CAROUSEL) using the POST /api/message-server/rcs/templates endpoint. This guide covers JSON structure, field requirements, and real-world examples in multiple programming languages.
Base payload structure
Every RCS template includes a name, description, type, and body. The body structure varies by template type.
{
"name": "Human-readable name",
"description": "Brief template description",
"type": "TEXT | CARD | CAROUSEL",
"body": { /* template content */ }
}
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | Display name for your template |
description | string | Yes | Clear description of the template's purpose |
type | string | Yes | One of: TEXT, CARD, CAROUSEL |
body | object | Yes | Template structure (varies by type) |
Common fields (in body)
SMS Fallback (optional)
Provide an SMS alternative that sends automatically if RCS isn't available on the recipient's device. Both sender and text are required if you define fallbackSms.
"fallbackSms": {
"sender": "SMS-SENDER",
"text": "SMS message text"
}
sender: Your authorized SMS sender ID (typically 4-11 alphanumeric characters)text: Fallback message content (must match template language and tone)
Suggestions (optional)
Add interactive buttons and actions that recipients can tap. Each suggestion defines the action type and required fields:
- type: action type (
url,dial,reply,coordinates,location,calendar) - text: button label (displays to the user)
"suggestions": [
{
"type": "url",
"text": "Visit website",
"url": { "url": "https://example.com" }
},
{
"type": "dial",
"text": "Call us",
"dial": { "phoneNumber": "+39037224525" }
}
]
Best practices for suggestions:
- Keep button text concise (under 25 characters)
- Use action-oriented labels ("Call us", "Learn more", "Shop now")
- Include 1-4 suggestions per template for optimal UX
- Ensure phone numbers include country code
TEXT Template
Send rich text messages with optional interactive suggestions and SMS fallback. Perfect for confirmations, alerts, and personalized messages.
Structure
{
"type": "TEXT",
"body": {
"text": "Message text",
"suggestions": [ ... ],
"fallbackSms": { ... }
}
}
| Field | Required | Notes |
|---|---|---|
text | Yes | Non-empty message content (supports placeholders like \{name\}) |
suggestions | No | Array of interactive buttons and actions |
fallbackSms | No | SMS alternative if RCS unavailable |
Create a TEXT template
This example creates a welcome message with a website link, call button, and SMS fallback.
- cURL
- Python
- Node.js
- PHP
- Java
- C#
- Go
- Ruby
curl -X POST https://lora-api.agiletelecom.com/api/message-server/rcs/templates \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome Message",
"description": "Personalized welcome with SMS fallback",
"type": "TEXT",
"body": {
"text": "Hi {name}, welcome to our service!",
"suggestions": [
{
"type": "url",
"text": "Visit our site",
"url": { "url": "https://agiletelecom.com" }
},
{
"type": "dial",
"text": "Call us",
"dial": { "phoneNumber": "+39037224525" }
}
],
"fallbackSms": {
"sender": "AGILE",
"text": "Hi {name}, welcome to our service!"
}
}
}'
import requests
import json
api_key = "YOUR_API_KEY"
base_url = "https://lora-api.agiletelecom.com"
payload = {
"name": "Welcome Message",
"description": "Personalized welcome with SMS fallback",
"type": "TEXT",
"body": {
"text": "Hi {name}, welcome to our service!",
"suggestions": [
{
"type": "url",
"text": "Visit our site",
"url": {"url": "https://agiletelecom.com"}
},
{
"type": "dial",
"text": "Call us",
"dial": {"phoneNumber": "+39037224525"}
}
],
"fallbackSms": {
"sender": "AGILE",
"text": "Hi {name}, welcome to our service!"
}
}
}
response = requests.post(
f"{base_url}/api/message-server/rcs/templates",
headers={"X-Api-Key": api_key},
json=payload
)
template = response.json()
print(f"Template created: {template['id']}")
const fetch = require('node-fetch');
const apiKey = 'YOUR_API_KEY';
const baseUrl = 'https://lora-api.agiletelecom.com';
const payload = {
name: 'Welcome Message',
description: 'Personalized welcome with SMS fallback',
type: 'TEXT',
body: {
text: 'Hi {name}, welcome to our service!',
suggestions: [
{
type: 'url',
text: 'Visit our site',
url: { url: 'https://agiletelecom.com' }
},
{
type: 'dial',
text: 'Call us',
dial: { phoneNumber: '+39037224525' }
}
],
fallbackSms: {
sender: 'AGILE',
text: 'Hi {name}, welcome to our service!'
}
}
};
fetch(`${baseUrl}/api/message-server/rcs/templates`, {
method: 'POST',
headers: {
'X-Api-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => console.log(`Template created: ${data.id}`));
<?php
$apiKey = 'YOUR_API_KEY';
$baseUrl = 'https://lora-api.agiletelecom.com';
$payload = [
'name' => 'Welcome Message',
'description' => 'Personalized welcome with SMS fallback',
'type' => 'TEXT',
'body' => [
'text' => 'Hi {name}, welcome to our service!',
'suggestions' => [
[
'type' => 'url',
'text' => 'Visit our site',
'url' => ['url' => 'https://agiletelecom.com']
],
[
'type' => 'dial',
'text' => 'Call us',
'dial' => ['phoneNumber' => '+39037224525']
]
],
'fallbackSms' => [
'sender' => 'AGILE',
'text' => 'Hi {name}, welcome to our service!'
]
]
];
$ch = curl_init("$baseUrl/api/message-server/rcs/templates");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $apiKey,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
$response = curl_exec($ch);
$template = json_decode($response, true);
echo "Template created: " . $template['id'];
?>
import java.net.http.*;
import com.google.gson.*;
public class CreateTextTemplate {
public static void main(String[] args) throws Exception {
String apiKey = "YOUR_API_KEY";
String baseUrl = "https://lora-api.agiletelecom.com";
JsonObject payload = new JsonObject();
payload.addProperty("name", "Welcome Message");
payload.addProperty("description", "Personalized welcome with SMS fallback");
payload.addProperty("type", "TEXT");
JsonObject body = new JsonObject();
body.addProperty("text", "Hi {name}, welcome to our service!");
JsonArray suggestions = new JsonArray();
JsonObject urlSuggestion = new JsonObject();
urlSuggestion.addProperty("type", "url");
urlSuggestion.addProperty("text", "Visit our site");
JsonObject urlObj = new JsonObject();
urlObj.addProperty("url", "https://agiletelecom.com");
urlSuggestion.add("url", urlObj);
suggestions.add(urlSuggestion);
JsonObject dialSuggestion = new JsonObject();
dialSuggestion.addProperty("type", "dial");
dialSuggestion.addProperty("text", "Call us");
JsonObject dialObj = new JsonObject();
dialObj.addProperty("phoneNumber", "+39037224525");
dialSuggestion.add("dial", dialObj);
suggestions.add(dialSuggestion);
body.add("suggestions", suggestions);
JsonObject fallbackSms = new JsonObject();
fallbackSms.addProperty("sender", "AGILE");
fallbackSms.addProperty("text", "Hi {name}, welcome to our service!");
body.add("fallbackSms", fallbackSms);
payload.add("body", body);
HttpRequest request = HttpRequest.newBuilder()
.uri(new java.net.URI(baseUrl + "/api/message-server/rcs/templates"))
.header("X-Api-Key", apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload.toString()))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject();
System.out.println("Template created: " + result.get("id"));
}
}
using System;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
public class CreateTextTemplate
{
public static async Task Main()
{
string apiKey = "YOUR_API_KEY";
string baseUrl = "https://lora-api.agiletelecom.com";
var payload = new
{
name = "Welcome Message",
description = "Personalized welcome with SMS fallback",
type = "TEXT",
body = new
{
text = "Hi {name}, welcome to our service!",
suggestions = new object[]
{
new
{
type = "url",
text = "Visit our site",
url = new { url = "https://agiletelecom.com" }
},
new
{
type = "dial",
text = "Call us",
dial = new { phoneNumber = "+39037224525" }
}
},
fallbackSms = new
{
sender = "AGILE",
text = "Hi {name}, welcome to our service!"
}
}
};
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
var json = JsonConvert.SerializeObject(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
$"{baseUrl}/api/message-server/rcs/templates",
content
);
var responseBody = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<dynamic>(responseBody);
Console.WriteLine($"Template created: {result.id}");
}
}
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
apiKey := "YOUR_API_KEY"
baseUrl := "https://lora-api.agiletelecom.com"
payload := map[string]interface{}{
"name": "Welcome Message",
"description": "Personalized welcome with SMS fallback",
"type": "TEXT",
"body": map[string]interface{}{
"text": "Hi {name}, welcome to our service!",
"suggestions": []map[string]interface{}{
{
"type": "url",
"text": "Visit our site",
"url": map[string]string{"url": "https://agiletelecom.com"},
},
{
"type": "dial",
"text": "Call us",
"dial": map[string]string{"phoneNumber": "+39037224525"},
},
},
"fallbackSms": map[string]string{
"sender": "AGILE",
"text": "Hi {name}, welcome to our service!",
},
},
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest(
"POST",
fmt.Sprintf("%s/api/message-server/rcs/templates", baseUrl),
bytes.NewBuffer(jsonData),
)
req.Header.Set("X-Api-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
fmt.Printf("Template created: %v\n", result["id"])
}
require 'net/http'
require 'json'
api_key = 'YOUR_API_KEY'
base_url = 'https://lora-api.agiletelecom.com'
payload = {
name: 'Welcome Message',
description: 'Personalized welcome with SMS fallback',
type: 'TEXT',
body: {
text: 'Hi {name}, welcome to our service!',
suggestions: [
{
type: 'url',
text: 'Visit our site',
url: { url: 'https://agiletelecom.com' }
},
{
type: 'dial',
text: 'Call us',
dial: { phoneNumber: '+39037224525' }
}
],
fallbackSms: {
sender: 'AGILE',
text: 'Hi {name}, welcome to our service!'
}
}
}
uri = URI("#{base_url}/api/message-server/rcs/templates")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request['X-Api-Key'] = api_key
request['Content-Type'] = 'application/json'
request.body = payload.to_json
response = http.request(request)
result = JSON.parse(response.body)
puts "Template created: #{result['id']}"
CARD Template
Display a single rich card with media, title, description, and interactive actions. Perfect for promotional offers, product showcases, and event announcements.
Structure
{
"type": "CARD",
"body": {
"cardOrientation": "VERTICAL | HORIZONTAL | UNSPECIFIED",
"thumbnailAlignment": "LEFT | RIGHT | UNSPECIFIED",
"card": {
"title": "Title",
"description": "Description",
"media": {
"height": "SHORT | MEDIUM | TALL | UNSPECIFIED",
"fileUrl": "https://..."
},
"suggestions": [ ... ]
},
"suggestions": [ ... ],
"fallbackSms": { ... }
}
}
| Field | Required | Notes |
|---|---|---|
cardOrientation | Yes | Layout: VERTICAL (recommended) or HORIZONTAL |
card | Yes | Card container with title, description, media |
card.media.height | If media present | Height: SHORT, MEDIUM, TALL |
card.media.fileUrl | If media present | Public URL to image or video (kept online during send) |
suggestions | No | Card-level and global-level action buttons |
Create a CARD template
This example creates a promotional card with an image, offer details, and action buttons.
- cURL
- Python
- Node.js
- PHP
- Java
- C#
- Go
- Ruby
curl -X POST https://lora-api.agiletelecom.com/api/message-server/rcs/templates \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Fall Promotion Card",
"description": "Vertical card with seasonal offer",
"type": "CARD",
"body": {
"cardOrientation": "VERTICAL",
"card": {
"title": "Fall promotion",
"description": "20% off selected products",
"media": {
"height": "TALL",
"fileUrl": "https://cdn.example.com/img/offer.jpg"
},
"suggestions": [
{
"type": "url",
"text": "Discover offer",
"url": { "url": "https://shop.example.com/offer" }
}
]
},
"suggestions": [
{
"type": "dial",
"text": "Call us",
"dial": { "phoneNumber": "+39037224525" }
}
],
"fallbackSms": {
"sender": "AGILE",
"text": "Promo -20% on shop.example.com"
}
}
}'
import requests
api_key = "YOUR_API_KEY"
base_url = "https://lora-api.agiletelecom.com"
payload = {
"name": "Fall Promotion Card",
"description": "Vertical card with seasonal offer",
"type": "CARD",
"body": {
"cardOrientation": "VERTICAL",
"card": {
"title": "Fall promotion",
"description": "20% off selected products",
"media": {
"height": "TALL",
"fileUrl": "https://cdn.example.com/img/offer.jpg"
},
"suggestions": [
{
"type": "url",
"text": "Discover offer",
"url": {"url": "https://shop.example.com/offer"}
}
]
},
"suggestions": [
{
"type": "dial",
"text": "Call us",
"dial": {"phoneNumber": "+39037224525"}
}
],
"fallbackSms": {
"sender": "AGILE",
"text": "Promo -20% on shop.example.com"
}
}
}
response = requests.post(
f"{base_url}/api/message-server/rcs/templates",
headers={"X-Api-Key": api_key},
json=payload
)
template = response.json()
print(f"Template created: {template['id']}")
const fetch = require('node-fetch');
const apiKey = 'YOUR_API_KEY';
const baseUrl = 'https://lora-api.agiletelecom.com';
const payload = {
name: 'Fall Promotion Card',
description: 'Vertical card with seasonal offer',
type: 'CARD',
body: {
cardOrientation: 'VERTICAL',
card: {
title: 'Fall promotion',
description: '20% off selected products',
media: {
height: 'TALL',
fileUrl: 'https://cdn.example.com/img/offer.jpg'
},
suggestions: [
{
type: 'url',
text: 'Discover offer',
url: { url: 'https://shop.example.com/offer' }
}
]
},
suggestions: [
{
type: 'dial',
text: 'Call us',
dial: { phoneNumber: '+39037224525' }
}
],
fallbackSms: {
sender: 'AGILE',
text: 'Promo -20% on shop.example.com'
}
}
};
fetch(`${baseUrl}/api/message-server/rcs/templates`, {
method: 'POST',
headers: {
'X-Api-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => console.log(`Template created: ${data.id}`));
<?php
$apiKey = 'YOUR_API_KEY';
$baseUrl = 'https://lora-api.agiletelecom.com';
$payload = [
'name' => 'Fall Promotion Card',
'description' => 'Vertical card with seasonal offer',
'type' => 'CARD',
'body' => [
'cardOrientation' => 'VERTICAL',
'card' => [
'title' => 'Fall promotion',
'description' => '20% off selected products',
'media' => [
'height' => 'TALL',
'fileUrl' => 'https://cdn.example.com/img/offer.jpg'
],
'suggestions' => [
[
'type' => 'url',
'text' => 'Discover offer',
'url' => ['url' => 'https://shop.example.com/offer']
]
]
],
'suggestions' => [
[
'type' => 'dial',
'text' => 'Call us',
'dial' => ['phoneNumber' => '+39037224525']
]
],
'fallbackSms' => [
'sender' => 'AGILE',
'text' => 'Promo -20% on shop.example.com'
]
]
];
$ch = curl_init("$baseUrl/api/message-server/rcs/templates");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $apiKey,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
$response = curl_exec($ch);
$template = json_decode($response, true);
echo "Template created: " . $template['id'];
?>
import java.net.http.*;
import com.google.gson.*;
public class CreateCardTemplate {
public static void main(String[] args) throws Exception {
String apiKey = "YOUR_API_KEY";
String baseUrl = "https://lora-api.agiletelecom.com";
JsonObject payload = new JsonObject();
payload.addProperty("name", "Fall Promotion Card");
payload.addProperty("description", "Vertical card with seasonal offer");
payload.addProperty("type", "CARD");
JsonObject body = new JsonObject();
body.addProperty("cardOrientation", "VERTICAL");
JsonObject card = new JsonObject();
card.addProperty("title", "Fall promotion");
card.addProperty("description", "20% off selected products");
JsonObject media = new JsonObject();
media.addProperty("height", "TALL");
media.addProperty("fileUrl", "https://cdn.example.com/img/offer.jpg");
card.add("media", media);
JsonArray cardSuggestions = new JsonArray();
JsonObject urlSuggestion = new JsonObject();
urlSuggestion.addProperty("type", "url");
urlSuggestion.addProperty("text", "Discover offer");
JsonObject urlObj = new JsonObject();
urlObj.addProperty("url", "https://shop.example.com/offer");
urlSuggestion.add("url", urlObj);
cardSuggestions.add(urlSuggestion);
card.add("suggestions", cardSuggestions);
body.add("card", card);
JsonArray globalSuggestions = new JsonArray();
JsonObject dialSuggestion = new JsonObject();
dialSuggestion.addProperty("type", "dial");
dialSuggestion.addProperty("text", "Call us");
JsonObject dialObj = new JsonObject();
dialObj.addProperty("phoneNumber", "+39037224525");
dialSuggestion.add("dial", dialObj);
globalSuggestions.add(dialSuggestion);
body.add("suggestions", globalSuggestions);
JsonObject fallbackSms = new JsonObject();
fallbackSms.addProperty("sender", "AGILE");
fallbackSms.addProperty("text", "Promo -20% on shop.example.com");
body.add("fallbackSms", fallbackSms);
payload.add("body", body);
HttpRequest request = HttpRequest.newBuilder()
.uri(new java.net.URI(baseUrl + "/api/message-server/rcs/templates"))
.header("X-Api-Key", apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload.toString()))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject();
System.out.println("Template created: " + result.get("id"));
}
}
using System;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
public class CreateCardTemplate
{
public static async Task Main()
{
string apiKey = "YOUR_API_KEY";
string baseUrl = "https://lora-api.agiletelecom.com";
var payload = new
{
name = "Fall Promotion Card",
description = "Vertical card with seasonal offer",
type = "CARD",
body = new
{
cardOrientation = "VERTICAL",
card = new
{
title = "Fall promotion",
description = "20% off selected products",
media = new
{
height = "TALL",
fileUrl = "https://cdn.example.com/img/offer.jpg"
},
suggestions = new object[]
{
new
{
type = "url",
text = "Discover offer",
url = new { url = "https://shop.example.com/offer" }
}
}
},
suggestions = new object[]
{
new
{
type = "dial",
text = "Call us",
dial = new { phoneNumber = "+39037224525" }
}
},
fallbackSms = new
{
sender = "AGILE",
text = "Promo -20% on shop.example.com"
}
}
};
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
var json = JsonConvert.SerializeObject(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
$"{baseUrl}/api/message-server/rcs/templates",
content
);
var responseBody = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<dynamic>(responseBody);
Console.WriteLine($"Template created: {result.id}");
}
}
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
apiKey := "YOUR_API_KEY"
baseUrl := "https://lora-api.agiletelecom.com"
payload := map[string]interface{}{
"name": "Fall Promotion Card",
"description": "Vertical card with seasonal offer",
"type": "CARD",
"body": map[string]interface{}{
"cardOrientation": "VERTICAL",
"card": map[string]interface{}{
"title": "Fall promotion",
"description": "20% off selected products",
"media": map[string]string{
"height": "TALL",
"fileUrl": "https://cdn.example.com/img/offer.jpg",
},
"suggestions": []map[string]interface{}{
{
"type": "url",
"text": "Discover offer",
"url": map[string]string{"url": "https://shop.example.com/offer"},
},
},
},
"suggestions": []map[string]interface{}{
{
"type": "dial",
"text": "Call us",
"dial": map[string]string{"phoneNumber": "+39037224525"},
},
},
"fallbackSms": map[string]string{
"sender": "AGILE",
"text": "Promo -20% on shop.example.com",
},
},
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest(
"POST",
fmt.Sprintf("%s/api/message-server/rcs/templates", baseUrl),
bytes.NewBuffer(jsonData),
)
req.Header.Set("X-Api-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
fmt.Printf("Template created: %v\n", result["id"])
}
require 'net/http'
require 'json'
api_key = 'YOUR_API_KEY'
base_url = 'https://lora-api.agiletelecom.com'
payload = {
name: 'Fall Promotion Card',
description: 'Vertical card with seasonal offer',
type: 'CARD',
body: {
cardOrientation: 'VERTICAL',
card: {
title: 'Fall promotion',
description: '20% off selected products',
media: {
height: 'TALL',
fileUrl: 'https://cdn.example.com/img/offer.jpg'
},
suggestions: [
{
type: 'url',
text: 'Discover offer',
url: { url: 'https://shop.example.com/offer' }
}
]
},
suggestions: [
{
type: 'dial',
text: 'Call us',
dial: { phoneNumber: '+39037224525' }
}
],
fallbackSms: {
sender: 'AGILE',
text: 'Promo -20% on shop.example.com'
}
}
}
uri = URI("#{base_url}/api/message-server/rcs/templates")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request['X-Api-Key'] = api_key
request['Content-Type'] = 'application/json'
request.body = payload.to_json
response = http.request(request)
result = JSON.parse(response.body)
puts "Template created: #{result['id']}"
CAROUSEL Template
Display a scrollable horizontal list of 2-10 cards. Each card has its own media, content, and actions. Ideal for product catalogs, portfolios, and menus.
Structure
{
"type": "CAROUSEL",
"body": {
"cardWidth": "SMALL | MEDIUM | UNSPECIFIED",
"cards": [
{
"title": "Title",
"description": "Description",
"media": {
"height": "SHORT | MEDIUM | TALL | UNSPECIFIED",
"fileUrl": "https://..."
},
"suggestions": [ ... ]
}
],
"suggestions": [ ... ],
"fallbackSms": { ... }
}
}
| Field | Required | Notes |
|---|---|---|
cardWidth | Yes | Card size: SMALL or MEDIUM |
cards | Yes | Array of 2-10 card objects |
cards[].media.height | If media present | Height: SHORT, MEDIUM, TALL |
cards[].suggestions | No | Card-specific action buttons |
suggestions | No | Global actions shown below carousel |
Create a CAROUSEL template
This example creates a restaurant menu carousel with multiple dishes, photos, and booking actions.
- cURL
- Python
- Node.js
- PHP
- Java
- C#
- Go
- Ruby
curl -X POST https://lora-api.agiletelecom.com/api/message-server/rcs/templates \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Restaurant Menu Carousel",
"description": "Scrollable menu with dishes and booking",
"type": "CAROUSEL",
"body": {
"cardWidth": "MEDIUM",
"cards": [
{
"title": "Appetizers",
"description": "A burst of flavors.",
"media": {
"height": "SHORT",
"fileUrl": "https://cdn.example.com/appetizers.jpg"
},
"suggestions": [
{
"type": "dial",
"text": "Call now",
"dial": { "phoneNumber": "+39037224525" }
}
]
},
{
"title": "Mains",
"description": "Fresh pasta made locally.",
"media": {
"height": "SHORT",
"fileUrl": "https://cdn.example.com/mains.jpg"
},
"suggestions": [
{
"type": "url",
"text": "View online",
"url": { "url": "https://restaurant.example.com/menu" }
}
]
}
],
"suggestions": [
{
"type": "calendar",
"text": "Book a table",
"calendar": {
"title": "Dinner reservation",
"description": "Reserve your table",
"startTime": "2026-06-15T19:00:00Z",
"endTime": "2026-06-15T21:00:00Z"
}
}
],
"fallbackSms": {
"sender": "AGILE",
"text": "Discover our menu at restaurant.example.com"
}
}
}'
import requests
api_key = "YOUR_API_KEY"
base_url = "https://lora-api.agiletelecom.com"
payload = {
"name": "Restaurant Menu Carousel",
"description": "Scrollable menu with dishes and booking",
"type": "CAROUSEL",
"body": {
"cardWidth": "MEDIUM",
"cards": [
{
"title": "Appetizers",
"description": "A burst of flavors.",
"media": {
"height": "SHORT",
"fileUrl": "https://cdn.example.com/appetizers.jpg"
},
"suggestions": [
{
"type": "dial",
"text": "Call now",
"dial": {"phoneNumber": "+39037224525"}
}
]
},
{
"title": "Mains",
"description": "Fresh pasta made locally.",
"media": {
"height": "SHORT",
"fileUrl": "https://cdn.example.com/mains.jpg"
},
"suggestions": [
{
"type": "url",
"text": "View online",
"url": {"url": "https://restaurant.example.com/menu"}
}
]
}
],
"suggestions": [
{
"type": "calendar",
"text": "Book a table",
"calendar": {
"title": "Dinner reservation",
"description": "Reserve your table",
"startTime": "2026-06-15T19:00:00Z",
"endTime": "2026-06-15T21:00:00Z"
}
}
],
"fallbackSms": {
"sender": "AGILE",
"text": "Discover our menu at restaurant.example.com"
}
}
}
response = requests.post(
f"{base_url}/api/message-server/rcs/templates",
headers={"X-Api-Key": api_key},
json=payload
)
template = response.json()
print(f"Template created: {template['id']}")
const fetch = require('node-fetch');
const apiKey = 'YOUR_API_KEY';
const baseUrl = 'https://lora-api.agiletelecom.com';
const payload = {
name: 'Restaurant Menu Carousel',
description: 'Scrollable menu with dishes and booking',
type: 'CAROUSEL',
body: {
cardWidth: 'MEDIUM',
cards: [
{
title: 'Appetizers',
description: 'A burst of flavors.',
media: {
height: 'SHORT',
fileUrl: 'https://cdn.example.com/appetizers.jpg'
},
suggestions: [
{
type: 'dial',
text: 'Call now',
dial: { phoneNumber: '+39037224525' }
}
]
},
{
title: 'Mains',
description: 'Fresh pasta made locally.',
media: {
height: 'SHORT',
fileUrl: 'https://cdn.example.com/mains.jpg'
},
suggestions: [
{
type: 'url',
text: 'View online',
url: { url: 'https://restaurant.example.com/menu' }
}
]
}
],
suggestions: [
{
type: 'calendar',
text: 'Book a table',
calendar: {
title: 'Dinner reservation',
description: 'Reserve your table',
startTime: '2026-06-15T19:00:00Z',
endTime: '2026-06-15T21:00:00Z'
}
}
],
fallbackSms: {
sender: 'AGILE',
text: 'Discover our menu at restaurant.example.com'
}
}
};
fetch(`${baseUrl}/api/message-server/rcs/templates`, {
method: 'POST',
headers: {
'X-Api-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(res => res.json())
.then(data => console.log(`Template created: ${data.id}`));
<?php
$apiKey = 'YOUR_API_KEY';
$baseUrl = 'https://lora-api.agiletelecom.com';
$payload = [
'name' => 'Restaurant Menu Carousel',
'description' => 'Scrollable menu with dishes and booking',
'type' => 'CAROUSEL',
'body' => [
'cardWidth' => 'MEDIUM',
'cards' => [
[
'title' => 'Appetizers',
'description' => 'A burst of flavors.',
'media' => [
'height' => 'SHORT',
'fileUrl' => 'https://cdn.example.com/appetizers.jpg'
],
'suggestions' => [
[
'type' => 'dial',
'text' => 'Call now',
'dial' => ['phoneNumber' => '+39037224525']
]
]
],
[
'title' => 'Mains',
'description' => 'Fresh pasta made locally.',
'media' => [
'height' => 'SHORT',
'fileUrl' => 'https://cdn.example.com/mains.jpg'
],
'suggestions' => [
[
'type' => 'url',
'text' => 'View online',
'url' => ['url' => 'https://restaurant.example.com/menu']
]
]
]
],
'suggestions' => [
[
'type' => 'calendar',
'text' => 'Book a table',
'calendar' => [
'title' => 'Dinner reservation',
'description' => 'Reserve your table',
'startTime' => '2026-06-15T19:00:00Z',
'endTime' => '2026-06-15T21:00:00Z'
]
]
],
'fallbackSms' => [
'sender' => 'AGILE',
'text' => 'Discover our menu at restaurant.example.com'
]
]
];
$ch = curl_init("$baseUrl/api/message-server/rcs/templates");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $apiKey,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
$response = curl_exec($ch);
$template = json_decode($response, true);
echo "Template created: " . $template['id'];
?>
import java.net.http.*;
import com.google.gson.*;
public class CreateCarouselTemplate {
public static void main(String[] args) throws Exception {
String apiKey = "YOUR_API_KEY";
String baseUrl = "https://lora-api.agiletelecom.com";
JsonObject payload = new JsonObject();
payload.addProperty("name", "Restaurant Menu Carousel");
payload.addProperty("description", "Scrollable menu with dishes and booking");
payload.addProperty("type", "CAROUSEL");
JsonObject body = new JsonObject();
body.addProperty("cardWidth", "MEDIUM");
JsonArray cards = new JsonArray();
// First card
JsonObject card1 = new JsonObject();
card1.addProperty("title", "Appetizers");
card1.addProperty("description", "A burst of flavors.");
JsonObject media1 = new JsonObject();
media1.addProperty("height", "SHORT");
media1.addProperty("fileUrl", "https://cdn.example.com/appetizers.jpg");
card1.add("media", media1);
JsonArray card1Suggestions = new JsonArray();
JsonObject dialSugg = new JsonObject();
dialSugg.addProperty("type", "dial");
dialSugg.addProperty("text", "Call now");
JsonObject dialObj = new JsonObject();
dialObj.addProperty("phoneNumber", "+39037224525");
dialSugg.add("dial", dialObj);
card1Suggestions.add(dialSugg);
card1.add("suggestions", card1Suggestions);
cards.add(card1);
// Second card
JsonObject card2 = new JsonObject();
card2.addProperty("title", "Mains");
card2.addProperty("description", "Fresh pasta made locally.");
JsonObject media2 = new JsonObject();
media2.addProperty("height", "SHORT");
media2.addProperty("fileUrl", "https://cdn.example.com/mains.jpg");
card2.add("media", media2);
JsonArray card2Suggestions = new JsonArray();
JsonObject urlSugg = new JsonObject();
urlSugg.addProperty("type", "url");
urlSugg.addProperty("text", "View online");
JsonObject urlObj = new JsonObject();
urlObj.addProperty("url", "https://restaurant.example.com/menu");
urlSugg.add("url", urlObj);
card2Suggestions.add(urlSugg);
card2.add("suggestions", card2Suggestions);
cards.add(card2);
body.add("cards", cards);
JsonArray globalSuggestions = new JsonArray();
JsonObject calendarSugg = new JsonObject();
calendarSugg.addProperty("type", "calendar");
calendarSugg.addProperty("text", "Book a table");
JsonObject calendar = new JsonObject();
calendar.addProperty("title", "Dinner reservation");
calendar.addProperty("description", "Reserve your table");
calendar.addProperty("startTime", "2026-06-15T19:00:00Z");
calendar.addProperty("endTime", "2026-06-15T21:00:00Z");
calendarSugg.add("calendar", calendar);
globalSuggestions.add(calendarSugg);
body.add("suggestions", globalSuggestions);
JsonObject fallbackSms = new JsonObject();
fallbackSms.addProperty("sender", "AGILE");
fallbackSms.addProperty("text", "Discover our menu at restaurant.example.com");
body.add("fallbackSms", fallbackSms);
payload.add("body", body);
HttpRequest request = HttpRequest.newBuilder()
.uri(new java.net.URI(baseUrl + "/api/message-server/rcs/templates"))
.header("X-Api-Key", apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload.toString()))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject();
System.out.println("Template created: " + result.get("id"));
}
}
using System;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
public class CreateCarouselTemplate
{
public static async Task Main()
{
string apiKey = "YOUR_API_KEY";
string baseUrl = "https://lora-api.agiletelecom.com";
var payload = new
{
name = "Restaurant Menu Carousel",
description = "Scrollable menu with dishes and booking",
type = "CAROUSEL",
body = new
{
cardWidth = "MEDIUM",
cards = new object[]
{
new
{
title = "Appetizers",
description = "A burst of flavors.",
media = new
{
height = "SHORT",
fileUrl = "https://cdn.example.com/appetizers.jpg"
},
suggestions = new object[]
{
new
{
type = "dial",
text = "Call now",
dial = new { phoneNumber = "+39037224525" }
}
}
},
new
{
title = "Mains",
description = "Fresh pasta made locally.",
media = new
{
height = "SHORT",
fileUrl = "https://cdn.example.com/mains.jpg"
},
suggestions = new object[]
{
new
{
type = "url",
text = "View online",
url = new { url = "https://restaurant.example.com/menu" }
}
}
}
},
suggestions = new object[]
{
new
{
type = "calendar",
text = "Book a table",
calendar = new
{
title = "Dinner reservation",
description = "Reserve your table",
startTime = "2026-06-15T19:00:00Z",
endTime = "2026-06-15T21:00:00Z"
}
}
},
fallbackSms = new
{
sender = "AGILE",
text = "Discover our menu at restaurant.example.com"
}
}
};
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
var json = JsonConvert.SerializeObject(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
$"{baseUrl}/api/message-server/rcs/templates",
content
);
var responseBody = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<dynamic>(responseBody);
Console.WriteLine($"Template created: {result.id}");
}
}
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
apiKey := "YOUR_API_KEY"
baseUrl := "https://lora-api.agiletelecom.com"
payload := map[string]interface{}{
"name": "Restaurant Menu Carousel",
"description": "Scrollable menu with dishes and booking",
"type": "CAROUSEL",
"body": map[string]interface{}{
"cardWidth": "MEDIUM",
"cards": []map[string]interface{}{
{
"title": "Appetizers",
"description": "A burst of flavors.",
"media": map[string]string{
"height": "SHORT",
"fileUrl": "https://cdn.example.com/appetizers.jpg",
},
"suggestions": []map[string]interface{}{
{
"type": "dial",
"text": "Call now",
"dial": map[string]string{"phoneNumber": "+39037224525"},
},
},
},
{
"title": "Mains",
"description": "Fresh pasta made locally.",
"media": map[string]string{
"height": "SHORT",
"fileUrl": "https://cdn.example.com/mains.jpg",
},
"suggestions": []map[string]interface{}{
{
"type": "url",
"text": "View online",
"url": map[string]string{"url": "https://restaurant.example.com/menu"},
},
},
},
},
"suggestions": []map[string]interface{}{
{
"type": "calendar",
"text": "Book a table",
"calendar": map[string]string{
"title": "Dinner reservation",
"description": "Reserve your table",
"startTime": "2026-06-15T19:00:00Z",
"endTime": "2026-06-15T21:00:00Z",
},
},
},
"fallbackSms": map[string]string{
"sender": "AGILE",
"text": "Discover our menu at restaurant.example.com",
},
},
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest(
"POST",
fmt.Sprintf("%s/api/message-server/rcs/templates", baseUrl),
bytes.NewBuffer(jsonData),
)
req.Header.Set("X-Api-Key", apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
fmt.Printf("Template created: %v\n", result["id"])
}
require 'net/http'
require 'json'
api_key = 'YOUR_API_KEY'
base_url = 'https://lora-api.agiletelecom.com'
payload = {
name: 'Restaurant Menu Carousel',
description: 'Scrollable menu with dishes and booking',
type: 'CAROUSEL',
body: {
cardWidth: 'MEDIUM',
cards: [
{
title: 'Appetizers',
description: 'A burst of flavors.',
media: {
height: 'SHORT',
fileUrl: 'https://cdn.example.com/appetizers.jpg'
},
suggestions: [
{
type: 'dial',
text: 'Call now',
dial: { phoneNumber: '+39037224525' }
}
]
},
{
title: 'Mains',
description: 'Fresh pasta made locally.',
media: {
height: 'SHORT',
fileUrl: 'https://cdn.example.com/mains.jpg'
},
suggestions: [
{
type: 'url',
text: 'View online',
url: { url: 'https://restaurant.example.com/menu' }
}
]
}
],
suggestions: [
{
type: 'calendar',
text: 'Book a table',
calendar: {
title: 'Dinner reservation',
description: 'Reserve your table',
startTime: '2026-06-15T19:00:00Z',
endTime: '2026-06-15T21:00:00Z'
}
}
],
fallbackSms: {
sender: 'AGILE',
text: 'Discover our menu at restaurant.example.com'
}
}
}
uri = URI("#{base_url}/api/message-server/rcs/templates")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request['X-Api-Key'] = api_key
request['Content-Type'] = 'application/json'
request.body = payload.to_json
response = http.request(request)
result = JSON.parse(response.body)
puts "Template created: #{result['id']}"
Suggestion types and required fields
| Type | Description | Required Fields | Example |
|---|---|---|---|
reply | Text reply button | - | \{ "type": "reply", "text": "Yes", "reply": \{\} \} |
url | Open a web link | url.url | \{ "type": "url", "text": "Learn more", "url": \{"url": "https://example.com"\} \} |
dial | Initiate a phone call | dial.phoneNumber | \{ "type": "dial", "text": "Call us", "dial": \{"phoneNumber": "+39037224525"\} \} |
coordinates | Navigate to a location | latitude, longitude | \{ "type": "coordinates", "text": "Find us", "locationCoordinates": \{"label": "Store", "latitude": 45.464, "longitude": 9.188\} \} |
location | Search for nearby places | locationQuery.query | \{ "type": "location", "text": "Nearby", "locationQuery": \{"query": "restaurants"\} \} |
calendar | Add event to calendar | startTime, endTime | \{ "type": "calendar", "text": "Save date", "calendar": \{"title": "Webinar", "startTime": "2026-06-15T14:00:00Z", "endTime": "2026-06-15T15:00:00Z"\} \} |
Always use ISO-8601 format: 2026-06-15T14:00:00Z (UTC) or 2026-06-15T14:00:00+02:00 (with timezone offset).
Media in cards
Add images or videos to cards with the media object:
| Field | Type | Notes |
|---|---|---|
height | enum | Required if media is present. Options: SHORT (50px), MEDIUM (100px), TALL (200px), UNSPECIFIED |
fileUrl | string | Public HTTPS URL to image or video file |
fileUrlmust be publicly accessible (no authentication)- Keep files online during the entire campaign send
- Missing or unreachable media causes send errors
- Use CDN or stable hosting for reliability
Enumerations reference
Template and card types:
- type:
TEXT|CARD|CAROUSEL - cardOrientation:
VERTICAL(recommended) |HORIZONTAL|UNSPECIFIED - thumbnailAlignment:
LEFT|RIGHT|UNSPECIFIED
Media dimensions:
- height:
SHORT|MEDIUM|TALL|UNSPECIFIED - cardWidth:
SMALL|MEDIUM|UNSPECIFIED
Placeholders for personalization
Personalize templates with dynamic values using placeholders. Wrap variable names in curly braces (e.g., \{firstName\}, \{orderId\}) and provide values during send.
How placeholders work
- Define placeholders in your template text
- Send a message referencing the template
- Provide placeholder values in the
placeholdersobject - Substitution happens automatically
Placeholder example
Template definition:
{
"name": "Order Status",
"type": "TEXT",
"body": {
"text": "Hi {firstName}, your order #{orderId} is being prepared. Estimated delivery: {deliveryDate}."
}
}
Send with values:
{
"templateId": "tpl_123",
"destination": "+391234567890",
"agentId": "agent_abc",
"messageId": "msg-001",
"placeholders": {
"firstName": "Marco",
"orderId": "12345",
"deliveryDate": "June 15"
}
}
Recipient sees:
Hi Marco, your order #12345 is being prepared. Estimated delivery: June 15.
If a placeholder is defined in the template but not provided during send, the unreplaced placeholder (e.g., \{firstName\}) will appear in the message. Always provide all placeholder values.
Common template errors and how to fix them
| Template Type | Error | Solution |
|---|---|---|
| TEXT | Missing body.text | Ensure text field is populated and non-empty |
| CARD | Missing body.card or media height | Add card object and set media.height if including media |
| CAROUSEL | Missing cardWidth or empty cards | Set cardWidth and include 2-10 cards in array |
| Media | fileUrl not publicly accessible | Use HTTPS URLs; verify no authentication required |
| Suggestions | Type-field mismatch | Match suggestion type with required fields (e.g., dial needs phoneNumber) |
| Placeholders | Values not provided at send time | Always supply placeholder values to avoid \{placeholder\} appearing in message |
Best practices checklist
- Placeholders: Provide all placeholder values during send to avoid unreplaced text
- Media URLs: Use stable, public HTTPS URLs; keep them online throughout the campaign
- Media height: Always specify
SHORT,MEDIUM, orTALLfor consistent rendering - Suggestions: Use consistent, concise button labels (under 25 characters)
- SMS fallback: If enabled, ensure sender ID is authorized and matches template language
- Testing: Test with real placeholder values before sending campaigns
- Card orientation: Default to
VERTICALfor optimal mobile display
Next steps
Once you create templates, use the RCS Send API to deliver messages:
- Send from template: Reference template ID with placeholder values
- Send inline: Create ad-hoc messages without storing templates
- Track webhooks: Monitor delivery status and user interactions
See RCS API documentation for send endpoints, authentication, and webhook setup.
To test templates interactively, download the Postman Collection from your dashboard.