diff --git a/images/message-structure-hierarchy-card.svg b/images/message-structure-hierarchy-card.svg
new file mode 100644
index 000000000..49472b390
--- /dev/null
+++ b/images/message-structure-hierarchy-card.svg
@@ -0,0 +1,116 @@
+
diff --git a/sdk/react-native/ai-agents.mdx b/sdk/react-native/ai-agents.mdx
index 05f8e5bde..32452827c 100644
--- a/sdk/react-native/ai-agents.mdx
+++ b/sdk/react-native/ai-agents.mdx
@@ -57,13 +57,16 @@ Events arrive via `onAIAssistantEventReceived` in this order:
| 3 | Tool Call Arguments | Arguments being passed to the tool |
| 4 | Tool Call End | Tool execution completed |
| 5 | Tool Call Result | Tool's output is available |
-| 6 | Text Message Start | Agent started composing a reply |
-| 7 | Text Message Content | Streaming content chunks (multiple) |
-| 8 | Text Message End | Agent reply is complete |
-| 9 | Run Finished | Run finalized; persisted messages follow |
+| 6 | Card Start | Agent started generating a card |
+| 7 | Card | Full card payload is available |
+| 8 | Card End | Card generation is complete |
+| 9 | Text Message Start | Agent started composing a reply |
+| 10 | Text Message Content | Streaming content chunks (multiple) |
+| 11 | Text Message End | Agent reply is complete |
+| 12 | Run Finished | Run finalized; persisted messages follow |
-`Run Start` and `Run Finished` are always emitted. Tool Call events only appear when tools are invoked — there can be multiple tool call cycles in a single run. Text Message events are always emitted and carry the assistant's reply incrementally.
+`Run Start` and `Run Finished` are always emitted. Tool Call events only appear when tools are invoked — there can be multiple tool call cycles in a single run. Card events (`Card Start` → `Card` → `Card End`) only appear when the agent produces a card, and may repeat for each card. Text Message events are always emitted and carry the assistant's reply incrementally.
### Event Object Properties
@@ -88,6 +91,9 @@ Some events carry additional data:
| Text Message Content | `getDelta()` | The streaming text chunk for progressive rendering |
| Tool Call Arguments | `getToolCallId()`, `getDelta()` | Tool call ID and argument chunk |
| Tool Call Result | `getToolCallId()`, `getContent()`, `getRole()` | Tool call ID, result content, and role |
+| Card Start | `getCardId()`, `getExecutionText()`, `getStreamMessageId()` | Card ID, a human-readable status string, and the stream message ID |
+| Card | `getCardId()`, `getCard()`, `getStreamMessageId()` | Card ID, the raw card payload, and the stream message ID |
+| Card End | `getCardId()`, `getStreamMessageId()` | Card ID and the stream message ID |
@@ -137,11 +143,67 @@ CometChat.removeAIAssistantListener("unique_listener_id");
- Tool Call Arguments: Arguments being passed to the tool.
- Tool Call End: Tool execution completed.
- Tool Call Result: Tool's output is available.
+- Card Start: The agent started generating a card. Carries `cardId` and `executionText` (a human-readable status like "Building your product card...").
+- Card: The full card payload is available. Use `getCard()` to retrieve the raw card JSON and pass it to the renderer.
+- Card End: The card generation flow is finalized.
- Text Message Start: The agent started composing a reply.
- Text Message Content: Streaming content chunks for progressive rendering.
- Text Message End: The agent reply is complete.
- Run Finished: The run is finalized; persisted messages will follow.
+#### Card Streaming Events
+
+When the agent generates a card, the run emits a card cycle (`Card Start` → `Card` → `Card End`). All three are `AIAssistantBaseEvent` subclasses delivered through the same `onAIAssistantEventReceived` callback — no separate listener is required.
+
+| Class | Event type (`getType()`) | Getters |
+|-------|--------------------------|---------|
+| `AIAssistantCardStartedEvent` | `card_start` | `getCardId()`, `getExecutionText()`, `getStreamMessageId()` |
+| `AIAssistantCardReceivedEvent` | `card` | `getCardId()`, `getCard()`, `getStreamMessageId()` |
+| `AIAssistantCardEndedEvent` | `card_end` | `getCardId()`, `getStreamMessageId()` |
+
+
+
+```typescript
+CometChat.addAIAssistantListener("unique_listener_id", {
+ onAIAssistantEventReceived: (event: CometChat.AIAssistantBaseEvent) => {
+ if (event instanceof CometChat.AIAssistantCardStartedEvent) {
+ console.log("Card generation started:", event.getCardId());
+ console.log("Execution text:", event.getExecutionText());
+ } else if (event instanceof CometChat.AIAssistantCardReceivedEvent) {
+ console.log("Card received:", event.getCardId());
+ const cardPayload = event.getCard();
+ // Pass cardPayload to your card renderer
+ } else if (event instanceof CometChat.AIAssistantCardEndedEvent) {
+ console.log("Card generation ended:", event.getCardId());
+ }
+ },
+});
+```
+
+
+
+
+```javascript
+CometChat.addAIAssistantListener("unique_listener_id", {
+ onAIAssistantEventReceived: (event) => {
+ if (event instanceof CometChat.AIAssistantCardStartedEvent) {
+ console.log("Card generation started:", event.getCardId());
+ console.log("Execution text:", event.getExecutionText());
+ } else if (event instanceof CometChat.AIAssistantCardReceivedEvent) {
+ console.log("Card received:", event.getCardId());
+ const cardPayload = event.getCard();
+ // Pass cardPayload to your card renderer
+ } else if (event instanceof CometChat.AIAssistantCardEndedEvent) {
+ console.log("Card generation ended:", event.getCardId());
+ }
+ },
+});
+```
+
+
+
+
+
### Agentic Messages
After the run completes, these messages arrive via `MessageListener`:
@@ -220,6 +282,83 @@ The `getToolCalls()` method on `AIToolArgumentMessage` returns an array of `AITo
Always remove listeners when they're no longer needed (e.g., on component unmount or page navigation). Failing to remove listeners can cause memory leaks and duplicate event handling.
+### AIAssistantMessage Elements
+
+Starting from React Native SDK **4.0.26**, a persisted `AIAssistantMessage` carries its content as an ordered list of blocks via `getElements()`. This is the preferred render source — when present, walk the list in order and render each block. If the agent response contains only a card (no accompanying text), the text returned by `getAssistantMessageData().getText()` will be empty, so always prefer `getElements()`.
+
+`getElements()` returns an array of `AIAssistantElement` (or `null` for older messages without elements). Each element exposes:
+
+| Method | Return Type | Description |
+|--------|-------------|-------------|
+| `getType()` | `string` | The block type, e.g. `"text"` or `"card"`. Determines the shape of `getData()`. |
+| `getData()` | `string \| object` | The block's raw body. `"text"` → `string`; `"card"` → `{ card, cardId }`; other types → raw JSON value. The SDK never interprets this — it returns the value as-is. |
+
+
+
+```typescript
+function handleAIAssistantMessage(message: CometChat.AIAssistantMessage) {
+ const elements = message.getElements();
+
+ if (elements && elements.length > 0) {
+ // Preferred path: walk elements in order
+ elements.forEach((element: CometChat.AIAssistantElement) => {
+ switch (element.getType()) {
+ case "text":
+ console.log("Text block:", element.getData());
+ break;
+ case "card": {
+ const { card, cardId } = element.getData() as {
+ card: object;
+ cardId: string;
+ };
+ console.log("Card block:", cardId);
+ // Pass card to your card renderer
+ break;
+ }
+ default:
+ console.log("Unknown element type:", element.getType());
+ }
+ });
+ } else {
+ // Fallback for older messages without elements
+ console.log("Message text:", message.getAssistantMessageData().getText());
+ }
+}
+```
+
+
+
+
+```javascript
+function handleAIAssistantMessage(message) {
+ const elements = message.getElements();
+
+ if (elements && elements.length > 0) {
+ elements.forEach((element) => {
+ switch (element.getType()) {
+ case "text":
+ console.log("Text block:", element.getData());
+ break;
+ case "card": {
+ const { card, cardId } = element.getData();
+ console.log("Card block:", cardId);
+ // Pass card to your card renderer
+ break;
+ }
+ default:
+ console.log("Unknown element type:", element.getType());
+ }
+ });
+ } else {
+ console.log("Message text:", message.getAssistantMessageData().getText());
+ }
+}
+```
+
+
+
+
+
---
## Next Steps
diff --git a/sdk/react-native/campaigns.mdx b/sdk/react-native/campaigns.mdx
index 2c340bbdc..683654d63 100644
--- a/sdk/react-native/campaigns.mdx
+++ b/sdk/react-native/campaigns.mdx
@@ -518,3 +518,53 @@ When a user taps a button or link inside a card, the action callback receives on
| `initiateCall` | callType (audio/video), uid, guid | Start a call |
| `apiCall` | url, method, headers, body | Make an HTTP request |
| `customCallback` | callbackId, payload | App-specific logic |
+
+---
+
+## Card Messages
+
+Besides the notification feed, the **same Card Schema JSON** can arrive as a chat message. A `CardMessage` is a message with `category: "card"` — created and sent via the Platform (REST) API or the Dashboard Bubble Builder. The SDK only **receives** card messages; it does not send them.
+
+Card messages are delivered through the `onCardMessageReceived` callback of the [`MessageListener`](/sdk/react-native/real-time-listeners#message-listener), not through the notification feed listener. The payload from `getCard()` is the same Card Schema JSON described above, so you render it with the same [`CometChatCardView`](#rendering-cards) and handle the same [card actions](#supported-card-actions).
+
+### CardMessage Fields
+
+| Method | Return Type | Description |
+| --- | --- | --- |
+| `getCard()` | object | The raw Card Schema JSON payload. Pass directly to the Cards renderer. |
+| `getText()` | string | Preview text for push notifications and the conversation list. |
+| `getFallbackText()` | string | Fallback text from inside the card (`card.fallbackText`), used for accessibility or when the renderer fails. |
+| `getTags()` | `Array` | Tags associated with this message. |
+
+
+
+```typescript
+CometChat.addMessageListener("unique_listener_id", {
+ onCardMessageReceived: (cardMessage: CometChat.CardMessage) => {
+ console.log("Card message received:", cardMessage.getId());
+ const cardJson: string = JSON.stringify(cardMessage.getCard()); // same schema as feed cards
+ const fallback: string = cardMessage.getFallbackText();
+ const previewText: string = cardMessage.getText();
+ // Pass cardJson to CometChatCardView
+ },
+});
+```
+
+
+```javascript
+CometChat.addMessageListener("unique_listener_id", {
+ onCardMessageReceived: (cardMessage) => {
+ console.log("Card message received:", cardMessage.getId());
+ const cardJson = JSON.stringify(cardMessage.getCard()); // same schema as feed cards
+ const fallback = cardMessage.getFallbackText();
+ const previewText = cardMessage.getText();
+ // Pass cardJson to CometChatCardView
+ },
+});
+```
+
+
+
+
+Card messages are **receive-only**. They are created and sent exclusively via the Platform (REST) API and Dashboard Bubble Builder. The SDK exposes the raw payload via `getCard()` — the CometChat Cards library is responsible for drawing the card UI.
+
diff --git a/sdk/react-native/message-structure-and-hierarchy.mdx b/sdk/react-native/message-structure-and-hierarchy.mdx
index 7b41e3db4..ab688fbe9 100644
--- a/sdk/react-native/message-structure-and-hierarchy.mdx
+++ b/sdk/react-native/message-structure-and-hierarchy.mdx
@@ -1,7 +1,7 @@
---
title: "Message Structure and Hierarchy"
sidebarTitle: "Message Structure"
-description: "Understand the message categories, types, and hierarchy in the CometChat React Native SDK including text, media, custom, action, call, and interactive messages."
+description: "Understand the message categories, types, and hierarchy in the CometChat React Native SDK including text, media, custom, card, action, and call messages."
---
@@ -9,17 +9,17 @@ description: "Understand the message categories, types, and hierarchy in the Com
Message categories and types:
- **message** → `text`, `image`, `video`, `audio`, `file`
- **custom** → Developer-defined types (e.g., `location`, `poll`)
+- **card** → `CardMessage` (receive-only rich cards)
- **action** → `groupMember` (joined/left/kicked/banned), `message` (edited/deleted)
- **call** → `audio`, `video`
-- **interactive** → `form`, `card`, `customInteractive`
-Every message in CometChat belongs to a category (`message`, `custom`, `action`, `call`, `interactive`) and has a specific type within that category.
+Every message in CometChat belongs to a category (`message`, `custom`, `card`, `action`, `call`) and has a specific type within that category.
## Message Hierarchy
-
+
## Categories Overview
@@ -28,9 +28,9 @@ Every message in CometChat belongs to a category (`message`, `custom`, `action`,
| --- | --- | --- |
| `message` | `text`, `image`, `video`, `audio`, `file` | Standard user messages |
| `custom` | Developer-defined | Custom data (location, polls, etc.) |
+| `card` | `CardMessage` | Receive-only rich, structured cards |
| `action` | `groupMember`, `message` | System-generated events |
| `call` | `audio`, `video` | Call-related messages |
-| `interactive` | `form`, `card`, `customInteractive` | Interactive messages (forms, cards) |
## Checking Message Category and Type
@@ -122,17 +122,20 @@ const customMessage = new CometChat.CustomMessage(
See [Send Message → Custom Messages](/sdk/react-native/send-message#custom-message) for details.
-## Interactive Messages (`interactive` Category)
+## Card Messages (`card` Category)
-An InteractiveMessage encapsulates an interactive unit within a chat message, such as an embedded form that users can fill out directly within the chat interface.
+A message with category `card` is a [`CardMessage`](/sdk/react-native/campaigns#card-messages) — a rich, structured card rendered from Card Schema JSON.
-| Type | Description |
-| --- | --- |
-| `form` | Interactive form messages |
-| `card` | Interactive card messages |
-| `customInteractive` | Custom interaction messages |
+Card messages are **receive-only**: they cannot be sent from the SDK. They are created and sent exclusively via the **Platform (REST) API** or the **Dashboard Bubble Builder**, and delivered through the `onCardMessageReceived` callback of the `MessageListener`.
+
+| Method | Return Type | Description |
+| --- | --- | --- |
+| `getCard()` | object | The raw Card Schema JSON payload. Pass it to the [`CometChatCardView`](/sdk/react-native/campaigns#rendering-cards) renderer. |
+| `getText()` | string | Preview text for notifications and the conversation list. |
+| `getFallbackText()` | string | Fallback text used for accessibility or when the renderer fails. |
+| `getTags()` | `Array` | Tags associated with the message. |
-See [Interactive Messages](/sdk/react-native/interactive-messages) for details.
+See [Receive Messages → Card Messages](/sdk/react-native/receive-messages#card-messages) and [Campaigns → Card Messages](/sdk/react-native/campaigns#card-messages) for details.
## Action Messages (`action` Category)
@@ -178,8 +181,8 @@ See [Default Calling](/sdk/react-native/default-call) or [Direct Calling](/sdk/r
Listen for incoming messages in real time
-
- Build forms, cards, and custom interactive messages
+
+ Receive and render rich, structured card messages
Advanced message filtering with RequestBuilder
diff --git a/sdk/react-native/real-time-listeners.mdx b/sdk/react-native/real-time-listeners.mdx
index dc8980fed..1a8a385c1 100644
--- a/sdk/react-native/real-time-listeners.mdx
+++ b/sdk/react-native/real-time-listeners.mdx
@@ -351,6 +351,7 @@ Receive events for incoming messages, typing indicators, read/delivery receipts,
| **onTransientMessageReceived(receipt: [CometChat.TransientMessage](/sdk/reference/auxiliary#transientmessage))** | This event is triggered when a Transient Message is received. |
| **onMessageReactionAdded(receipt: [CometChat.ReactionEvent](/sdk/reference/auxiliary#reactionevent))** | This event is triggered when a reaction is added to a message in a user/group conversation. |
| **onMessageReactionRemoved(receipt: [CometChat.ReactionEvent](/sdk/reference/auxiliary#reactionevent))** | This event is triggered when a reaction is removed from a message in a user/group conversation. |
+| **onCardMessageReceived(message: CometChat.CardMessage)** | This event is triggered when a card message (`category: "card"`) is received. See [Campaigns → Card Messages](/sdk/react-native/campaigns#card-messages). |
To add the `MessageListener`:
@@ -402,6 +403,9 @@ CometChat.addMessageListener(
onMessageReactionRemoved: (reaction: CometChat.ReactionEvent) => {
console.log("Message Reaction removed", reaction);
},
+ onCardMessageReceived: (cardMessage: CometChat.CardMessage) => {
+ console.log("Card message received", cardMessage);
+ },
})
);
```
@@ -455,6 +459,9 @@ CometChat.addMessageListener(
onMessageReactionRemoved: (reaction) => {
console.log("Message Reaction removed", reaction);
},
+ onCardMessageReceived: (cardMessage) => {
+ console.log("Card message received", cardMessage);
+ },
})
);
```
diff --git a/sdk/react-native/receive-messages.mdx b/sdk/react-native/receive-messages.mdx
index 8e6499597..13328bc50 100644
--- a/sdk/react-native/receive-messages.mdx
+++ b/sdk/react-native/receive-messages.mdx
@@ -90,6 +90,44 @@ Always remove listeners when they're no longer needed (e.g., on component unmoun
As a sender, you will not receive your own message in a real-time event. However, if a user is logged in on multiple devices, they will receive the event on the other devices.
+## Card Messages
+
+A [`CardMessage`](/sdk/react-native/campaigns#card-messages) (`category: "card"`) is delivered through the `onCardMessageReceived` callback of the `MessageListener`. Card messages are **receive-only** — they are created and sent via the Platform (REST) API or Dashboard Bubble Builder, not from the SDK.
+
+
+
+```typescript
+CometChat.addMessageListener(
+ listenerID,
+ new CometChat.MessageListener({
+ onCardMessageReceived: (cardMessage: CometChat.CardMessage) => {
+ console.log("Card message received", cardMessage);
+ // getCard() returns the raw Card Schema JSON — pass it to your card renderer
+ const card = cardMessage.getCard();
+ },
+ })
+);
+```
+
+
+
+```javascript
+CometChat.addMessageListener(
+ listenerID,
+ new CometChat.MessageListener({
+ onCardMessageReceived: (cardMessage) => {
+ console.log("Card message received", cardMessage);
+ // getCard() returns the raw Card Schema JSON — pass it to your card renderer
+ const card = cardMessage.getCard();
+ },
+ })
+);
+```
+
+
+
+For the full `CardMessage` field reference, see [Campaigns → Card Messages](/sdk/react-native/campaigns#card-messages).
+
## Missed Messages
Fetch messages that were sent while the user was offline using `getLastDeliveredMessageId()` and `fetchNext()`.
diff --git a/sdk/react-native/send-message.mdx b/sdk/react-native/send-message.mdx
index d63cb2632..05f5518dd 100644
--- a/sdk/react-native/send-message.mdx
+++ b/sdk/react-native/send-message.mdx
@@ -874,6 +874,59 @@ customMessage.setConversationText("Shared a location");
---
+## Card Message
+
+A `CardMessage` is a structured, interactive message rendered as a card bubble. It belongs to the `card` category and carries a block of Card Schema JSON that the CometChat Cards library draws.
+
+
+**Card Messages cannot be sent through the SDK.** The `CardMessage` class is **receive-only** — it has no public constructor and the SDK exposes no `sendCardMessage()` method. Card Messages are created server-side via the **Platform (REST) API** or the **Dashboard Bubble Builder**, and delivered to clients like any other message, through the `onCardMessageReceived` callback of the `MessageListener`.
+
+To create and send a Card Message, use the REST API. See the [Send Message REST API reference](https://www.cometchat.com/docs/rest-api/messages/send-message) for the message creation flow.
+
+
+On the receiving end, the `CardMessage` object gives you access to the card payload and its related fields.
+
+
+
+```typescript
+const card: object = cardMessage.getCard(); // raw card schema/payload
+const text: string = cardMessage.getText(); // preview text for notifications and the conversation list
+const fallbackText: string = cardMessage.getFallbackText(); // shown when the card cannot be rendered
+const tags: Array = cardMessage.getTags(); // tags associated with the message
+```
+
+
+
+```javascript
+const card = cardMessage.getCard(); // raw card schema/payload
+const text = cardMessage.getText(); // preview text for notifications and the conversation list
+const fallbackText = cardMessage.getFallbackText(); // shown when the card cannot be rendered
+const tags = cardMessage.getTags(); // tags associated with the message
+```
+
+
+
+The `CardMessage` class provides the following methods:
+
+| Method | Returns | Description |
+| --- | --- | --- |
+| `getCard()` | `object` | The raw card schema/payload passed to the Cards renderer. |
+| `getText()` | `string` | Preview text for notifications and the conversation list. |
+| `getFallbackText()` | `string` | Text displayed when the card payload cannot be rendered. |
+| `getTags()` | `Array` | Tags associated with the message. |
+
+`CardMessage` extends `BaseMessage`, so it also exposes the standard message fields (`getId()`, `getSender()`, `getReceiverId()`, `getSentAt()`, `getCategory()` = `card`, etc.).
+
+To handle incoming Card Messages on the client, implement [`onCardMessageReceived`](/sdk/react-native/receive-messages#card-messages) on your `MessageListener`.
+
+### Render a Card Message
+
+A `CardMessage` carries raw Card Schema JSON in `getCard()`. To draw it natively, pass it to the **CometChat Cards** renderer (`@cometchat/cards-react-native`) via the [`CometChatCardView`](/sdk/react-native/campaigns#rendering-cards) component. It is a pure renderer: you hand it the card JSON and an action callback, and you own all action behavior (opening URLs, navigating to chats, API calls, etc.).
+
+See [Campaigns → Rendering Cards](/sdk/react-native/campaigns#rendering-cards) for the dependency setup, the `CometChatCardView` example, and the [supported card actions](/sdk/react-native/campaigns#supported-card-actions). If the card JSON is empty or invalid, fall back to `getFallbackText()` (then `getText()`).
+
+---
+
## Next Steps
diff --git a/ui-kit/react-native/events.mdx b/ui-kit/react-native/events.mdx
index d261a1112..3e9a15eda 100644
--- a/ui-kit/react-native/events.mdx
+++ b/ui-kit/react-native/events.mdx
@@ -14,7 +14,7 @@ description: "Listen to UI Kit events for conversations, users, groups, messages
| Group events | `ccGroupCreated`, `ccGroupDeleted`, `ccGroupLeft`, `ccGroupMemberScopeChanged`, `ccGroupMemberKicked`, `ccGroupMemberBanned`, `ccGroupMemberUnbanned`, `ccGroupMemberJoined`, `ccGroupMemberAdded`, `ccOwnershipChanged` |
| Message events | `ccMessageSent`, `ccMessageEdited`, `ccMessageDeleted`, `ccMessageRead`, `ccLiveReaction`, plus SDK listener events |
| Call events | `onIncomingCallAccepted`, `onIncomingCallRejected`, `onCallEnded`, `onCallInitiated`, `onOutgoingCallAccepted`, `onOutgoingCallRejected` |
-| UI events | `ccActiveChatChanged` |
+| UI events | `ccActiveChatChanged`, `ccCardActionClicked` |
| Purpose | Decoupled communication between UI Kit components — subscribe to events to react to changes without direct component references |
@@ -87,7 +87,7 @@ It consists of the following events:
| onTransientMessageReceived | This event is emitted when the CometChat SDK listener emits a transient message. |
| onInteractionGoalCompleted | This event is emitted when the CometChat SDK listener indicates that an interaction goal has been completed. |
| onFormMessageReceived | This event is emitted when an interactive message of 'form' type is received from the CometChat SDK listener. |
-| onCardMessageReceived | This event is emitted when an interactive message of 'card' type is received from the CometChat SDK listener. |
+| onCardMessageReceived | This event is emitted when a card message (`category: "card"`) is received from the CometChat SDK listener. Used by the Card message bubble to render developer-defined cards. |
| onSchedulerMessageReceived | This event is emitted when an interactive message of 'scheduler' type is received from the CometChat SDK listener. |
| onCustomInteractiveMessageReceived | This event is emitted when an interactive message of 'customInteractive' type is received from the CometChat SDK listener. |
@@ -115,6 +115,7 @@ It consists of the following events:
| Name | Description |
| ------------------- | ---------------------------------------------------------------------------- |
| ccActiveChatChanged | This event is triggered when the user navigates to a particular chat window. |
+| ccCardActionClicked | This event is triggered when the user clicks an action on a card message bubble. The event payload contains the `message` and the clicked `action`. |
---