Session Carousel API
1. Overview
Session Carousel lets a sender push a horizontally scrollable set of 2–10 cards (image/video header, body text, one CTA button per card) inside a single WhatsApp interactive message. RML accepts a simplified payload and converts it internally to the native WhatsApp Cloud API carousel interactive type before dispatch. This document covers the send-message payload, the internal conversion, the template-creation payload, and validation rules.
2. Send Message
Endpoint: POST https://apis.rmlconnect.net/wba-testing/v1/messages
2.1 RML Payload (input)
{
"phone": "+9922892143",
"media": {
"type": "interactive_carousel",
"body": "Check out our featured dishes for today",
"cards": [
{
"header": {
"type": "image",
"url": "https://picsum.photos/200/300?grayscale"
},
"body": "Chicken Kadai - rich, spicy gravy",
"buttons": [
{ "type": "quick_reply", "text": "Order Kadai" }
]
},
{
"header": {
"type": "image",
"url": "https://picsum.photos/200/300?grayscale"
},
"body": "Fish Masala - coastal flavour",
"buttons": [
{ "type": "quick_reply", "text": "Order Fish" }
]
}
]
}
}2.2 Field Reference
| Field | Type | Required | Constraints |
|---|---|---|---|
| phone | string | Yes | E.164-style; leading + optional (both formats seen across examples) |
| media.type | string | Yes | Must be interactive _carousel |
| media.body | string | Yes | Max 1024 chars (SC-N9) |
| media.cards | array | Yes | 2–10 card objects (SC-N1, SC-N2) |
| cards[].header | object | Yes | Required per card (SC-N11) |
| cards[].header.type | string | Yes | One of image, video only (SC-N6) |
| cards[].header.url | string | Yes | Media source URL |
| cards[].body | string | Yes | Card body text |
| cards[].buttons | array | Yes | Exactly 1 button per card (SC-N7, SC-N8) |
| buttons[].type | string | Yes | quick_reply or url; same type across every card (SC-N3) |
| buttons[].text | string | Yes | Max 25 chars (SC-N10) |
| buttons[].url | string | Conditional | Required if type=url (SC-N4); disallowed if type=quick_reply (SC-N5) |
2.3 Converted Payload (WhatsApp Cloud API format, internal)
{
"messaging_product": "whatsapp",
"type": "interactive",
"to": "+919922892143",
"recipient_type": "individual",
"interactive": {
"type": "carousel",
"body": { "text": "Check out our featured dishes for today" },
"action": {
"cards": [
{
"card_index": 0,
"type": "button",
"header": {
"type": "image",
"image": { "link": "https://.../sample01.jpg" }
},
"body": { "text": "Chicken Kadai - rich, spicy gravy" },
"action": {
"buttons": [
{
"type": "quick_reply",
"quick_reply": { "id": "btn_0_0", "title": "Order Kadai" }
}
]
}
},
{
"card_index": 1,
"type": "button",
"header": {
"type": "image",
"image": { "link": "https://.../sample02.jpg" }
},
"body": { "text": "Fish Masala - coastal flavour" },
"action": {
"buttons": [
{
"type": "quick_reply",
"quick_reply": { "id": "btn_1_0", "title": "Order Fish" }
}
]
}
}
]
}
}
}Mapping Notes
- phone → to, normalized to include country code (e.g. +9922892143 → +919922892143).
- media.cards[] → interactive.action.cards[], each assigned a zero-indexed card_index.
- header.url on input is not passed through verbatim to output in the reference sample; output uses image.link. Confirm whether this is a real re-hosting/CDN step or a documentation artifact (see Open Questions).
- buttons[].id is synthesized as btn_{card_index}_{button_index} since RML input has no button ID field.
3. Template Creation
Purpose: register a reusable carousel template (as opposed to a one-off session send).
3.1 RML Payload
{
"name": "session carousel",
"type": "carousel",
"templateDetails": {
"bot_name": [ { "routeotp": "JIO" } ],
"variables": ["test1", "test2", "rml_ctr_postback"],
"category": "carousel",
"payload": {}
},
"width": "SMALL_WIDTH",
"height": "SHORT_HEIGHT",
"carouselList": [
{
"suggestions": [
{
"displayText": "hello card1[test1]",
"suggestionType": "reply",
"postback": "[rml_ctr_postback]::text"
}
],
"cardTitle": "card1[test1]",
"cardDescription": "card1[test1]",
"mediaUrl": "https://fastly.picsum.photos/id/1071/200/200.jpg"
},
{
"suggestions": [
{
"displayText": "hello card2[test2]",
"suggestionType": "reply",
"postback": "[rml_ctr_postback]::text"
}
],
"cardTitle": "card2[test2]",
"cardDescription": "card2[test1]",
"mediaUrl": "https://fastly.picsum.photos/id/1071/200/200.jpg"
}
]
}3.2 Field Reference
| Field | Type | Notes |
|---|---|---|
| name | string | Template name |
| type | string | carousel |
| templateDetails.bot_name | array | Bot/channel binding, keyed by carrier (e.g. routeotp: JIO) |
| templateDetails.variables | array | Named placeholders referenced in card text via [variable] syntax; includes reserved rml_ctr_postback |
| width / height | string | Card sizing; observed values SMALL_WIDTH / SHORT_HEIGHT — full enum not confirmed |
| carouselList[].suggestions | array | Per-card CTA(s); suggestionType: reply maps to quick-reply behavior |
| carouselList[].postback | string | Format [variable]::text — payload contract not fully specified |
| carouselList[].cardTitle / cardDescription | string | Support inline variable substitution, e.g. card1[test1] |
| carouselList[].mediaUrl | string | Header media source |
4. Validation Rules (Send API)
All violations return HTTP 422.
| Rule | Trigger | Reference |
|---|---|---|
| Card count | Must be 2–10 cards | SC-N1, SC-N2 |
| Uniform button type | All cards must use the same button-type combination | SC-N3 |
| URL button completeness | url required when type = url | SC-N4 |
| Quick-reply button purity | url not allowed when type = quick_reply | SC-N5 |
| Header type | Must be image or video | SC-N6 |
| Buttons per card (min) | At least 1 button required | SC-N7 |
| Buttons per card (max) | Exactly 1 button allowed | SC-N8 |
| Carousel body length | Max 1024 chars | SC-N9 |
| Button text length | Max 25 chars | SC-N10 |
| Header presence | Required on every card | SC-N11 |
5. Test Cases
| ID | Scenario | Expected |
|---|---|---|
| SC-N1 | Single card (below minimum) | 422 — carousel must contain 2–10 cards |
| SC-N2 | 11 cards (above maximum) | 422 — carousel must contain 2–10 cards |
| SC-N3 | Card 1 quick_reply, card 2 url | 422 — all cards must use the same button-type combination |
| SC-N4 | url button missing url field | 422 — url is required when button type is url |
| SC-N5 | quick_reply button with url field set | 422 — url is not allowed when button type is quick_reply |
| SC-N6 | Header type document | 422 — header type must be one of [image, video] |
| SC-N7 | Card with 0 buttons | 422 — buttons list length must be between 1 and 1 |
| SC-N8 | Card with 2 buttons | 422 — buttons list length must be between 1 and 1 |
| SC-N9 | Carousel body > 1024 chars | 422 — longer than maximum length 1024 (body) |
| SC-N10 | Button text > 25 chars | 422 — longer than maximum length 25 (button text) |
| SC-N11 | Card missing header | 422 — header is required for each carousel card |
| SC-N12 | All cards use url buttons, otherwise valid (happy path) | 200 — message queued/sent successfully |
Updated 11 days ago
Did this page help you?