RCS Message Guide

Build RCS messages with text, images, video, rich cards and buttons

This guide shows you how to build each RCS message format with the Send RCS message endpoint. Each example shows a preview of the message, the JSON field behind each numbered part, and the full request body.

The examples use placeholder values. Replace DemoSender with your RCS agent ID, and replace the recipient, links and media URLs with your own. The cdn.example.com media URLs don't exist, so a request that uses them fails with a 400 error.

Choose a message format

Formatcontent_typeObject inside contentUse it for
TexttexttextReminders, alerts and conversations
Image or videomediamediaA single media file, without a caption
Standalone rich cardrich_cardrich_card.standaloneOne item with text, optional media and buttons
Carouselrich_cardrich_card.carousel2 to 10 cards that the recipient swipes through

The content_type value must match the object you send inside content. Standalone cards and carousels both use rich_card.

Every format can also carry suggestions: reply chips, links and dial buttons shown under the message. See Suggested replies and actions.

Text with suggested replies

Use text for a simple message of up to 3,072 characters. Add suggested replies so the recipient can respond with a tap.

#What the recipient seesJSON field
1Message textcontent.text.message
2Yes reply chipcontent.suggestions[0].reply.text
3No reply chipcontent.suggestions[1].reply.text

The agent name and logo above the conversation come from your RCS agent, not from the request.

Send the request with your API key in the x-api-key header:

9:30
KKudosity
1Can you confirm your appointment on Thursday at 10am?
9:41 AM
2Yes3No
curl --request POST \
  --url https://api.transmitmessage.com/v2/rcs/messages \
  --header "x-api-key: $KUDOSITY_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "sender": "DemoSender",
    "recipient": "61438333061",
    "content_type": "text",
    "content": {
      "text": {
        "message": "Can you confirm your appointment on Thursday at 10am?"
      },
      "suggestions": [
        {
          "reply": {
            "text": "Yes",
            "postback_data": "appt-789-yes"
          }
        },
        {
          "reply": {
            "text": "No",
            "postback_data": "appt-789-no"
          }
        }
      ]
    },
    "sms_fallback": {
      "message": "Can you confirm your appointment on Thursday at 10am? Reply YES or NO."
    },
    "message_ref": "appt-789"
  }'

A successful request returns 200 with the message as it was accepted. Keep the id to look up the message later with Get RCS message.

{
  "data": {
    "id": "0b8f3c2e-6d4a-4f1e-9a7b-2c5d8e1f4a63",
    "message_ref": "appt-789",
    "sender": "DemoSender",
    "recipient": "61438333061",
    "recipient_country": "AU",
    "content_type": "text",
    "content": {
      "text": {
        "message": "Can you confirm your appointment on Thursday at 10am?"
      },
      "suggestions": [
        {
          "reply": {
            "text": "Yes",
            "postback_data": "appt-789-yes"
          }
        },
        {
          "reply": {
            "text": "No",
            "postback_data": "appt-789-no"
          }
        }
      ]
    },
    "sms_fallback": {
      "message": "Can you confirm your appointment on Thursday at 10am? Reply YES or NO."
    },
    "created_at": "2026-11-12T01:30:00Z"
  }
}

A 200 response confirms that the message was accepted, not that it was delivered. To track delivery, subscribe to the RCS_STATUS webhook event. See RCS Status Events.

When the recipient taps a reply chip, the chip's label is sent back to your agent as their reply. The postback_data value isn't shown to the recipient. Your application uses it to tell which chip was tapped. See Handle a tap in your application.

To send plain text, leave out content.suggestions.

The remaining examples show the request body only. Send each one to the same endpoint with the same headers.

Image or video with a link

Use media to send one image or video. This example adds a Shop now link under the image.

Media messages don't support a caption. When the image needs accompanying text, use a standalone rich card.

#What the recipient seesJSON field
1Imagecontent.media.file_url
2Shop now labelcontent.suggestions[0].action.text
Page the link openscontent.suggestions[0].action.open_url_action.url
9:30
KKudosity
1SUMMER SALE20% OFF
9:41 AM
2Shop now
{
  "sender": "DemoSender",
  "recipient": "61438333061",
  "content_type": "media",
  "content": {
    "media": {
      "file_url": "https://cdn.example.com/promo/summer-sale.jpg"
    },
    "suggestions": [
      {
        "action": {
          "text": "Shop now",
          "open_url_action": {
            "url": "https://example.com/sale"
          }
        }
      }
    ]
  },
  "message_ref": "sale-2027"
}

To send a video, use the same request and point file_url at a video file. There's no separate content type for video. See Media file requirements for the supported file types.

Standalone rich card

A standalone card combines an image or video, a title, a description and up to 4 buttons in one message. This order update puts a Track parcel button on the card.

The fields in this table sit inside content.rich_card.standalone.card_content.

#What the recipient seesJSON field
1Imagemedia.file_url
2Titletitle
3Descriptiondescription
4Track parcel labelsuggestions[0].action.text
Page the button openssuggestions[0].action.open_url_action.url
9:30
KKudosity
1
2Your order has shipped
3Order #12345 is on its way and should arrive Thursday.
4Track parcel
9:41 AM
{
  "sender": "DemoSender",
  "recipient": "61438333061",
  "content_type": "rich_card",
  "content": {
    "rich_card": {
      "standalone": {
        "card_content": {
          "title": "Your order has shipped",
          "description": "Order #12345 is on its way and should arrive Thursday.",
          "media": {
            "file_url": "https://cdn.example.com/orders/parcel.jpg"
          },
          "suggestions": [
            {
              "action": {
                "text": "Track parcel",
                "open_url_action": {
                  "url": "https://example.com/track/12345"
                }
              }
            }
          ]
        }
      }
    }
  },
  "message_ref": "order-12345"
}

A card needs at least one of title, description or media. It doesn't have to contain all three. A standalone card is shown vertically, with the media above the text at medium height.

Carousel of rich cards

Use a carousel to present several products or options in one message. Each entry in card_contents creates one card, and the recipient swipes sideways to see the rest.

#What the recipient seesJSON field
1First cardcontent.rich_card.carousel.card_contents[0]
2Second cardcontent.rich_card.carousel.card_contents[1]
3Buttons on a cardcontent.rich_card.carousel.card_contents[n].suggestions
4Call us chip under the carouselcontent.suggestions[0]
9:30
KKudosity
9:41 AM
4Call us
{
  "sender": "DemoSender",
  "recipient": "61438333061",
  "content_type": "rich_card",
  "content": {
    "rich_card": {
      "carousel": {
        "card_contents": [
          {
            "title": "Classic tee",
            "description": "$29",
            "media": {
              "file_url": "https://cdn.example.com/shop/tee.jpg"
            },
            "suggestions": [
              {
                "action": {
                  "text": "Buy tee",
                  "open_url_action": {
                    "url": "https://example.com/shop/tee"
                  }
                }
              },
              {
                "reply": {
                  "text": "More colours"
                }
              }
            ]
          },
          {
            "title": "Hoodie",
            "description": "$59",
            "media": {
              "file_url": "https://cdn.example.com/shop/hoodie.jpg"
            },
            "suggestions": [
              {
                "action": {
                  "text": "Buy hoodie",
                  "open_url_action": {
                    "url": "https://example.com/shop/hoodie"
                  }
                }
              },
              {
                "reply": {
                  "text": "Size guide"
                }
              }
            ]
          }
        ]
      }
    },
    "suggestions": [
      {
        "action": {
          "text": "Call us",
          "dial_action": {
            "phone_number": "+61290000000"
          }
        }
      }
    ]
  },
  "sms_fallback": {
    "message": "New arrivals: the classic tee ($29) and hoodie ($59). Shop at example.com"
  }
}

A carousel needs 2 to 10 cards. Each card follows the same rules as a standalone card. Carousel cards are medium width, with the media at medium height.

Set either standalone or carousel inside rich_card, never both.

Suggested replies and actions

A suggestion is either a reply or an action. An action is either an open_url_action or a dial_action. Set exactly one at each level.

TypeWhat a tap doesExample
ReplySends the label back to your agent as a reply{"reply": {"text": "Yes", "postback_data": "appt-789-yes"}}
Open URLOpens a web page{"action": {"text": "Shop now", "open_url_action": {"url": "https://example.com/sale"}}}
DialOpens the phone dialler with the number filled in{"action": {"text": "Call us", "dial_action": {"phone_number": "+61290000000"}}}

Rules for every suggestion:

  • text is the label the recipient sees. It must be 1 to 25 characters.
  • open_url_action.url must be an absolute https URL of up to 2,048 characters.
  • dial_action.phone_number must be in E.164 international format. The leading + is optional. Local formats, such as 02 9000 0000, are rejected.
  • postback_data is optional on replies and actions, and can be up to 2,048 characters.

Where to put suggestions

PlacementJSON fieldLimitBehaviour
Under the messagecontent.suggestions11 chipsThe chips disappear once the recipient replies or another message arrives.
On a cardcard_content.suggestions for a standalone card, or card_contents[n].suggestions for a carousel4 per cardThe buttons stay on the card after the recipient replies.

Use card buttons for actions that should stay available, such as tracking an order. Use chips under the message for a quick response to the latest message.

Handle a tap in your application

Set postback_data to a value your application understands, such as appt-789-yes. If you leave it out, Kudosity sets a value for you. Set your own so that the value maps to something in your application, such as an order, a booking or a step in a conversation.

When the recipient taps a suggestion, your RCS_INBOUND webhook receives an event with mo.content_type set to SUGGESTION. The label is in mo.content.suggestion.text, and your value is in mo.content.suggestion.postback_data.

{
  "event_type": "RCS_INBOUND",
  "timestamp": "2026-11-12T01:31:20.246802Z",
  "webhook_id": "fd0e6485-b905-44c1-bd55-fee1d0d6d864",
  "webhook_name": "RCS Inbound Webhook",
  "mo": {
    "type": "RCS",
    "id": "bc2e0a4a-7e21-49de-9912-20cb4148c21f",
    "sender": "61438333061",
    "recipient": "DemoSender",
    "content_type": "SUGGESTION",
    "content": {
      "suggestion": {
        "text": "Yes",
        "postback_data": "appt-789-yes"
      }
    }
  }
}

For the appointment example, route appt-789-yes to your confirmation workflow and appt-789-no to your rescheduling workflow. To receive these events, create a webhook that subscribes to RCS_INBOUND. See About Webhooks.

Add an SMS fallback

An RCS message can fail, for example when the recipient's device doesn't support RCS. To reach the recipient anyway, add a top-level sms_fallback object with the text to send by SMS.

{
  "sender": "DemoSender",
  "recipient": "61438333061",
  "content_type": "text",
  "content": {
    "text": {
      "message": "Can you confirm your appointment on Thursday at 10am?"
    }
  },
  "sms_fallback": {
    "message": "Can you confirm your appointment on Thursday at 10am? Reply YES or NO."
  }
}
  • sms_fallback.message is required when you include the object. Write it as a standalone SMS. Images, cards and buttons aren't carried over, so include any link or reply instruction in the text.
  • sms_fallback.sender is optional. Use it to set the number or alphanumeric sender ID that the SMS comes from.
  • Without sms_fallback, a failed RCS message isn't sent by SMS.

Media file requirements

These rules apply to content.media.file_url and to the media.file_url of every card. Kudosity checks each file when you send the request, and the request fails with a 400 error if a file doesn't meet them.

  • Use an absolute https URL of up to 2,048 characters, on the default port, without credentials. Percent-encode spaces and non-ASCII characters.
  • The URL must be publicly reachable and return the file directly, without redirects. Your server must respond within 10 seconds.
  • Your server must return a Content-Type header for a supported type, and the file content must match it:
    • Images: image/jpeg, image/png, image/gif
    • Videos: video/mp4, video/mpeg, video/mpeg4, video/m4v, video/webm, video/h263
  • The maximum file size is 100 MB. Return a Content-Length or Content-Range header so that the size can be checked.
  • Files are cached by URL for up to 60 days. To change a file, host it at a new URL.

Limits at a glance

ItemLimit
Text message3,072 characters
Cards in a carousel2 to 10
Card title200 characters
Card description2,000 characters
Buttons on a card4
Suggestions under a message11
Suggestion label1 to 25 characters
postback_data2,048 characters
Link and media URLs2,048 characters
Media file size100 MB
message_ref500 characters

How a message looks varies between messaging apps and devices. Before you send to customers, test your button placement, media cropping and SMS fallback with your agent's test numbers.


Did this page help you?