Build RCS rich cards and carousels
Use one card for an appointment or order and a carousel for a small set of choices. Start with the first-message walkthrough, which supplies the channel, recipient and runnable HTTP clients used here.
Build a card
Bird's RCS sending reference maps both a single rich card and a carousel to body.type: "carousel". One item produces a single card. The title and description explain the task; actions provide its next steps.
| Element | Appointment example | Customer check |
|---|---|---|
| Title | Your studio visit | Understandable without an image. |
| Description | Tuesday, 10:00. Riverside Studio. | Actual date, time zone and location are clear. |
| URL action | View appointment | Opens this appointment after sign-in. |
| Postback | Change my time | Returns a stable value to your rescheduling handler. |
| Optional media | Studio entrance | Helps recognize the location; essential details remain in text. |
Add an accessible HTTPS image through the item's mediaUrl and describe it with altText. Keep its URL reachable when the platform fetches it. This is a body example to place inside the request's existing receiver/reference envelope:
Exemple de code
{
"type": "carousel",
"carousel": {
"items": [
{
"title": "Your studio visit",
"description": "Tuesday, 10:00. Riverside Studio.",
"mediaUrl": "https://your-app.example/images/studio-entrance.jpg",
"altText": "Riverside Studio entrance",
"actions": [
{
"type": "link",
"link": {
"text": "View appointment",
"url": "https://your-app.example/appointments/demo"
}
}
]
}
]
}
}Replace both example URLs with your own resources. See the illustrative card experience for the visual sequence; verify the actual rendering on your test phone.
Choose an action or a reply
A link opens an application destination. A reply displays suggested text. A postback includes both a visible text and a stable payload you choose. Use an opaque action token in a real booking workflow; keep the authorized customer and current appointment in your application's records.
For the quickstart's Change my time button, the interaction API returns type: "clicked" and metadata.button.payload: "appointment-demo/change". The inbound message represents the selection in body.text.actions[].postback.payload. Button position in details helps debug rendering; the payload identifies the application intent.
This application-owned example consumes rcs-interactions.json from the quickstart and records one rescheduling request in a local SQLite database. Save it as record_rcs_choice.py and run python3 record_rcs_choice.py. It does not change a booking or send another message.
Exemple de code
import json
import os
import sqlite3
from pathlib import Path
sent = json.loads(Path("rcs-result.json").read_text())
interactions = json.loads(Path("rcs-interactions.json").read_text())["results"]
with sqlite3.connect("rcs-choices.sqlite") as db:
db.execute("CREATE TABLE IF NOT EXISTS seen (interaction_id TEXT PRIMARY KEY)")
db.execute("""CREATE TABLE IF NOT EXISTS tasks (
task_key TEXT PRIMARY KEY, message_id TEXT NOT NULL, status TEXT NOT NULL
)""")
for event in interactions:
if event.get("channelId") != os.environ["RCS_CHANNEL_ID"]:
continue
if event.get("messageId") != sent["id"] or event.get("type") != "clicked":
continue
payload = (event.get("metadata") or {}).get("button", {}).get("payload")
if payload != "appointment-demo/change" or not event.get("id"):
continue
with db:
inserted = db.execute(
"INSERT OR IGNORE INTO seen VALUES (?)", (event["id"],)
).rowcount
if inserted:
db.execute(
"INSERT OR IGNORE INTO tasks VALUES (?, ?, ?)",
("appointment-demo/reschedule", sent["id"], "requested"),
)
print(db.execute("SELECT task_key, status FROM tasks").fetchall())Repeated reads of the same interactions leave one task. Two distinct clicks for this same appointment also leave one task. In your application, derive that task key from the saved appointment and action, check the customer's authority and current booking state, then let the booking service offer available times. A reply requests a change; it does not prove a new time was reserved.
For live replies, create a webhook subscription for service: "channels", event: "rcs-google.inbound", filtered by channelId. Verify the webhook signature before consuming its message. Deduplicate inbound message IDs in your receiving handler. Choose either the inbound selection or the corresponding click as the source of the business task so both do not trigger it. Store ordinary free-text replies as conversation input; route unknown payloads for review instead of treating them as a booking instruction.
Add a carousel
Replace the quickstart's body with two comparable appointment options. Each choice has its own payload; both belong to the same appointment workflow.
Exemple de code
{
"type": "carousel",
"carousel": {
"items": [
{
"title": "Morning visit",
"description": "Tuesday, 10:00. Riverside Studio.",
"actions": [
{
"type": "postback",
"postback": { "text": "Choose morning", "payload": "appointment-demo/morning" }
}
]
},
{
"title": "Afternoon visit",
"description": "Tuesday, 15:00. Riverside Studio.",
"actions": [
{
"type": "postback",
"postback": { "text": "Choose afternoon", "payload": "appointment-demo/afternoon" }
}
]
}
]
}
}Extend the application's allowlist to these two choices and check availability when processing the selection. If the slot has gone, offer fresh options. The quickstart's single-choice SQLite example deliberately ignores these different payloads until you implement that behavior.
Preview and publish
For reusable content, follow Create an RCS message template. Add variables and language versions, preview resolved content, and publish. Use cross-platform optimization and its Android/iOS previews to check layout and button compatibility. Recheck image crop, long values and action destinations on representative phones.
A template send replaces body with template in the direct-channel request. Use the published project's ID, version and locale. This example assumes your template declares a string parameter called customer_name:
Exemple de code
{
"projectId": "replace-with-template-project-id",
"version": "replace-with-published-version",
"locale": "en",
"parameters": [{ "type": "string", "key": "customer_name", "value": "Alex" }]
}Keep the template's content and variables aligned with the action handler. Updating display text should not accidentally change what an existing action token means.
Continue building
- Send your first RCS message
- Configure SMS fallback
- Trace a delivery or reply problem
- RCS resource library
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet. Les ressources sont en anglais.