diff --git a/CampaignAssets/BubbleBuilder.png b/CampaignAssets/BubbleBuilder.png
new file mode 100644
index 000000000..422e6dcd7
Binary files /dev/null and b/CampaignAssets/BubbleBuilder.png differ
diff --git a/CampaignAssets/Create-Chan.png b/CampaignAssets/Create-Chan.png
new file mode 100644
index 000000000..2b1a7799f
Binary files /dev/null and b/CampaignAssets/Create-Chan.png differ
diff --git a/CampaignAssets/Create-Temp.png b/CampaignAssets/Create-Temp.png
new file mode 100644
index 000000000..409206bb5
Binary files /dev/null and b/CampaignAssets/Create-Temp.png differ
diff --git a/CampaignAssets/CreateCategory.png b/CampaignAssets/CreateCategory.png
new file mode 100644
index 000000000..11bca26f7
Binary files /dev/null and b/CampaignAssets/CreateCategory.png differ
diff --git a/CampaignAssets/create-Camp.png b/CampaignAssets/create-Camp.png
new file mode 100644
index 000000000..a42dd807c
Binary files /dev/null and b/CampaignAssets/create-Camp.png differ
diff --git a/campaigns-apis.json b/campaigns-apis.json
new file mode 100644
index 000000000..efa38c740
--- /dev/null
+++ b/campaigns-apis.json
@@ -0,0 +1,3405 @@
+{
+ "openapi": "3.0.0",
+ "paths": {
+ "/notification-feed/unread-count": {
+ "get": {
+ "operationId": "NotificationFeedController_getUnreadCount",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of user making client request",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "templateCategory",
+ "required": false,
+ "in": "query",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": ""
+ }
+ },
+ "summary": "Get unread count for the requesting user",
+ "tags": [
+ "Notification Feed"
+ ]
+ }
+ },
+ "/notification-feed": {
+ "get": {
+ "operationId": "NotificationFeedController_findFeed",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of user making client request",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "readState",
+ "required": false,
+ "in": "query",
+ "description": "Filter by read state",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "read",
+ "unread",
+ "all"
+ ]
+ }
+ },
+ {
+ "name": "dateFrom",
+ "required": false,
+ "in": "query",
+ "description": "Start date filter (unix timestamp in seconds)",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "dateTo",
+ "required": false,
+ "in": "query",
+ "description": "End date filter (unix timestamp in seconds)",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "tags",
+ "required": false,
+ "in": "query",
+ "description": "Comma-separated tags to filter by",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "tagMatch",
+ "required": false,
+ "in": "query",
+ "description": "Tag matching strategy: 'any' (OR) or 'all' (AND)",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "any",
+ "all"
+ ]
+ }
+ },
+ {
+ "name": "templateCategory",
+ "required": false,
+ "in": "query",
+ "description": "Filter by templateCategory (per-app TemplateCategory.name)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "channelId",
+ "required": false,
+ "in": "query",
+ "description": "Filter by in-app channel instance ID",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "includeDeleted",
+ "required": false,
+ "in": "query",
+ "description": "Include soft-deleted feed items",
+ "schema": {
+ "default": false,
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "includeExpired",
+ "required": false,
+ "in": "query",
+ "description": "Include expired feed items",
+ "schema": {
+ "default": false,
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "sentAt",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: sentAt unix timestamp of last item from previous page",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "id",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: id of last item from previous page",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "affix",
+ "required": false,
+ "in": "query",
+ "description": "Cursor direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "append",
+ "prepend"
+ ]
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Number of items per page",
+ "schema": {
+ "minimum": 1,
+ "maximum": 100,
+ "type": "number",
+ "default": 20
+ }
+ },
+ {
+ "name": "sort",
+ "required": false,
+ "in": "query",
+ "description": "Field to sort by",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "sentAt",
+ "createdAt"
+ ]
+ }
+ },
+ {
+ "name": "order",
+ "required": false,
+ "in": "query",
+ "description": "Sort direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ]
+ }
+ },
+ {
+ "name": "receiver",
+ "required": false,
+ "in": "query",
+ "description": "Admin-only: scope to a specific user. Ignored when onbehalfof is present.",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "templateOnly",
+ "required": false,
+ "in": "query",
+ "description": "Pass \"true\" to opt out of server-side rendering. Default: false (server renders and returns content per item).",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": ""
+ }
+ },
+ "summary": "Query feed with filters and cursor pagination",
+ "tags": [
+ "Notification Feed"
+ ]
+ }
+ },
+ "/notification-feed/{id}": {
+ "get": {
+ "operationId": "NotificationFeedController_findById",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of user making client request",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "templateOnly",
+ "required": false,
+ "in": "query",
+ "description": "Pass \"true\" to opt out of server-side rendering. Default: false (server renders and returns content).",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": ""
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Get a feed item by ID",
+ "tags": [
+ "Notification Feed"
+ ]
+ },
+ "delete": {
+ "operationId": "NotificationFeedController_delete",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of user making client request",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": ""
+ }
+ },
+ "summary": "Soft-delete a feed item",
+ "tags": [
+ "Notification Feed"
+ ]
+ }
+ },
+ "/notification-feed/{id}/read": {
+ "post": {
+ "operationId": "NotificationFeedController_markAsRead",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of user making client request",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": ""
+ },
+ "400": {
+ "description": "Missing required onbehalfof header or invalid input."
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Mark a feed item as read",
+ "tags": [
+ "Notification Feed"
+ ]
+ }
+ },
+ "/notification-feed/{id}/delivered": {
+ "post": {
+ "operationId": "NotificationFeedController_markAsDelivered",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of user making client request",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": ""
+ },
+ "400": {
+ "description": "Missing required onbehalfof header or invalid input."
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Mark a feed item as delivered",
+ "tags": [
+ "Notification Feed"
+ ]
+ }
+ },
+ "/notification-feed/{id}/engagement": {
+ "post": {
+ "operationId": "NotificationFeedController_reportEngagement",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of user making client request",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/EngagementEventDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": ""
+ },
+ "400": {
+ "description": "Missing required onbehalfof header or invalid input."
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Report an interacted engagement event with optional topic discriminator",
+ "tags": [
+ "Notification Feed"
+ ]
+ }
+ },
+ "/templates/categories": {
+ "post": {
+ "operationId": "CategoriesController_create",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateCategoryDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Category created"
+ },
+ "400": {
+ "description": "Invalid input"
+ },
+ "409": {
+ "description": "Duplicate category name"
+ }
+ },
+ "summary": "Create a new template category",
+ "tags": [
+ "Template Categories"
+ ]
+ },
+ "get": {
+ "operationId": "CategoriesController_findAll",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "updatedAt",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: updatedAt unix timestamp of last item",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "id",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: id of last item",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "affix",
+ "required": false,
+ "in": "query",
+ "description": "Cursor direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "append",
+ "prepend"
+ ]
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Items per page",
+ "schema": {
+ "minimum": 1,
+ "maximum": 100,
+ "type": "number",
+ "default": 20
+ }
+ },
+ {
+ "name": "sort",
+ "required": false,
+ "in": "query",
+ "description": "Sort field",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "name",
+ "createdAt",
+ "updatedAt"
+ ]
+ }
+ },
+ {
+ "name": "order",
+ "required": false,
+ "in": "query",
+ "description": "Sort direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Categories list"
+ }
+ },
+ "summary": "List all template categories",
+ "tags": [
+ "Template Categories"
+ ]
+ }
+ },
+ "/templates/categories/{id}": {
+ "get": {
+ "operationId": "CategoriesController_findById",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Category found"
+ },
+ "404": {
+ "description": "Category not found"
+ }
+ },
+ "summary": "Get a template category by ID",
+ "tags": [
+ "Template Categories"
+ ]
+ },
+ "put": {
+ "operationId": "CategoriesController_update",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateCategoryDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Category updated"
+ },
+ "404": {
+ "description": "Category not found"
+ },
+ "409": {
+ "description": "Duplicate category name"
+ }
+ },
+ "summary": "Update a template category",
+ "tags": [
+ "Template Categories"
+ ]
+ },
+ "delete": {
+ "operationId": "CategoriesController_delete",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Category deleted"
+ },
+ "404": {
+ "description": "Category not found"
+ }
+ },
+ "summary": "Delete a template category",
+ "tags": [
+ "Template Categories"
+ ]
+ }
+ },
+ "/templates": {
+ "post": {
+ "operationId": "TemplatesController_create",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateTemplateDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Template created"
+ },
+ "400": {
+ "description": "Invalid input or variable schema"
+ },
+ "409": {
+ "description": "Duplicate channel type"
+ }
+ },
+ "summary": "Create a new template",
+ "tags": [
+ "Templates"
+ ]
+ },
+ "get": {
+ "operationId": "TemplatesController_findAll",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "updatedAt",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: updatedAt unix timestamp of last item",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "id",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: id of last item",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "affix",
+ "required": false,
+ "in": "query",
+ "description": "Cursor direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "append",
+ "prepend"
+ ]
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Items per page",
+ "schema": {
+ "minimum": 1,
+ "maximum": 100,
+ "type": "number",
+ "default": 20
+ }
+ },
+ {
+ "name": "sort",
+ "required": false,
+ "in": "query",
+ "description": "Sort field",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "updatedAt",
+ "createdAt",
+ "name"
+ ]
+ }
+ },
+ {
+ "name": "order",
+ "required": false,
+ "in": "query",
+ "description": "Sort direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ]
+ }
+ },
+ {
+ "name": "status",
+ "required": false,
+ "in": "query",
+ "description": "Filter by template status",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "search",
+ "required": false,
+ "in": "query",
+ "description": "Search by name or templateId",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "tags",
+ "required": false,
+ "in": "query",
+ "description": "Comma-separated tags to filter by",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "tagMatch",
+ "required": false,
+ "in": "query",
+ "description": "Tag matching strategy: 'any' (OR) or 'all' (AND)",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "any",
+ "all"
+ ]
+ }
+ },
+ {
+ "name": "templateCategory",
+ "required": false,
+ "in": "query",
+ "description": "Filter by templateCategory name",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Templates list"
+ }
+ },
+ "summary": "List all templates",
+ "tags": [
+ "Templates"
+ ]
+ }
+ },
+ "/templates/{id}": {
+ "get": {
+ "operationId": "TemplatesController_findById",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Template found"
+ },
+ "404": {
+ "description": "Template not found"
+ }
+ },
+ "summary": "Get a template by ID",
+ "tags": [
+ "Templates"
+ ]
+ },
+ "put": {
+ "operationId": "TemplatesController_update",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateTemplateDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Template updated"
+ },
+ "400": {
+ "description": "Invalid variable schema"
+ },
+ "404": {
+ "description": "Template not found"
+ }
+ },
+ "summary": "Update template metadata",
+ "tags": [
+ "Templates"
+ ]
+ },
+ "delete": {
+ "operationId": "TemplatesController_archive",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Template archived"
+ },
+ "404": {
+ "description": "Template not found"
+ }
+ },
+ "summary": "Archive a template",
+ "tags": [
+ "Templates"
+ ]
+ }
+ },
+ "/templates/{id}/channels/{channelType}": {
+ "put": {
+ "operationId": "TemplatesController_updateChannelContent",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "channelType",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateChannelContentDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Channel content updated"
+ },
+ "404": {
+ "description": "Template not found"
+ }
+ },
+ "summary": "Update channel content for a template",
+ "tags": [
+ "Templates"
+ ]
+ }
+ },
+ "/templates/{id}/versions": {
+ "post": {
+ "operationId": "TemplatesController_createVersion",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Version created"
+ },
+ "404": {
+ "description": "Template not found"
+ }
+ },
+ "summary": "Create a new template version",
+ "tags": [
+ "Templates"
+ ]
+ }
+ },
+ "/channels": {
+ "post": {
+ "operationId": "ChannelsController_create",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateChannelDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Channel created"
+ },
+ "400": {
+ "description": "Called with onbehalfof"
+ },
+ "403": {
+ "description": "Channel type restricted"
+ },
+ "409": {
+ "description": "Duplicate channelId or limit reached"
+ }
+ },
+ "summary": "Create a new channel",
+ "tags": [
+ "Channels"
+ ]
+ },
+ "get": {
+ "operationId": "ChannelsController_list",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "updatedAt",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: updatedAt unix timestamp of last item",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "id",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: id of last item",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "affix",
+ "required": false,
+ "in": "query",
+ "description": "Cursor direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "append",
+ "prepend"
+ ]
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Items per page",
+ "schema": {
+ "minimum": 1,
+ "maximum": 100,
+ "type": "number",
+ "default": 20
+ }
+ },
+ {
+ "name": "sort",
+ "required": false,
+ "in": "query",
+ "description": "Sort field",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "channelType",
+ "createdAt",
+ "updatedAt"
+ ]
+ }
+ },
+ {
+ "name": "order",
+ "required": false,
+ "in": "query",
+ "description": "Sort direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ]
+ }
+ },
+ {
+ "name": "search",
+ "required": false,
+ "in": "query",
+ "description": "Search by name or channelId",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Paginated channel list"
+ }
+ },
+ "summary": "List channels for the app",
+ "tags": [
+ "Channels"
+ ]
+ }
+ },
+ "/channels/availability": {
+ "get": {
+ "operationId": "ChannelsController_getAvailability",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Channel type availability list"
+ },
+ "400": {
+ "description": "Called with onbehalfof"
+ }
+ },
+ "summary": "Get channel type availability",
+ "tags": [
+ "Channels"
+ ]
+ }
+ },
+ "/channels/{id}": {
+ "get": {
+ "operationId": "ChannelsController_getById",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Channel found"
+ },
+ "404": {
+ "description": "Channel not found"
+ }
+ },
+ "summary": "Get a channel by ID",
+ "tags": [
+ "Channels"
+ ]
+ },
+ "put": {
+ "operationId": "ChannelsController_update",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateChannelDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Channel updated"
+ },
+ "400": {
+ "description": "Called with onbehalfof"
+ },
+ "404": {
+ "description": "Channel not found"
+ }
+ },
+ "summary": "Update a channel",
+ "tags": [
+ "Channels"
+ ]
+ }
+ },
+ "/push-notifications": {
+ "get": {
+ "operationId": "PushNotificationsController_findByReceiver",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of the user",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "schema": {
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Push notifications list"
+ }
+ },
+ "summary": "List push notifications for a user",
+ "tags": [
+ "Push Notifications"
+ ]
+ }
+ },
+ "/push-notifications/{id}": {
+ "get": {
+ "operationId": "PushNotificationsController_findById",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of the user",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Push notification details"
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Get a push notification by ID",
+ "tags": [
+ "Push Notifications"
+ ]
+ }
+ },
+ "/push-notifications/{id}/delivered": {
+ "put": {
+ "operationId": "PushNotificationsController_markDelivered",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of the user",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Marked as delivered"
+ },
+ "400": {
+ "description": "Missing required onbehalfof header or invalid input."
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Mark push notification as delivered",
+ "tags": [
+ "Push Notifications"
+ ]
+ }
+ },
+ "/push-notifications/{id}/clicked": {
+ "put": {
+ "operationId": "PushNotificationsController_markClicked",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of the user",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Marked as clicked"
+ },
+ "400": {
+ "description": "Missing required onbehalfof header or invalid input."
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Mark push notification as clicked",
+ "tags": [
+ "Push Notifications"
+ ]
+ }
+ },
+ "/push-notifications/{id}/engagement": {
+ "post": {
+ "operationId": "PushNotificationsController_reportEngagement",
+ "parameters": [
+ {
+ "name": "onbehalfof",
+ "in": "header",
+ "description": "UID of the user",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PushEngagementEventDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Engagement recorded"
+ },
+ "400": {
+ "description": "Missing required onbehalfof header or invalid input."
+ },
+ "404": {
+ "description": "Item not found."
+ }
+ },
+ "summary": "Report push notification engagement event (interacted with optional topic)",
+ "tags": [
+ "Push Notifications"
+ ]
+ }
+ },
+ "/campaigns": {
+ "post": {
+ "operationId": "CampaignsController_create",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateCampaignDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Campaign created"
+ }
+ },
+ "summary": "Create a new campaign",
+ "tags": [
+ "Campaigns"
+ ]
+ },
+ "get": {
+ "operationId": "CampaignsController_findAll",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "updatedAt",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: updatedAt unix timestamp of last item",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "id",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: id of last item",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "affix",
+ "required": false,
+ "in": "query",
+ "description": "Cursor direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "append",
+ "prepend"
+ ]
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Items per page",
+ "schema": {
+ "minimum": 1,
+ "maximum": 100,
+ "type": "number",
+ "default": 20
+ }
+ },
+ {
+ "name": "sort",
+ "required": false,
+ "in": "query",
+ "description": "Sort field",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "updatedAt",
+ "createdAt",
+ "name"
+ ]
+ }
+ },
+ {
+ "name": "order",
+ "required": false,
+ "in": "query",
+ "description": "Sort direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ]
+ }
+ },
+ {
+ "name": "status",
+ "required": false,
+ "in": "query",
+ "description": "Filter by campaign status",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "search",
+ "required": false,
+ "in": "query",
+ "description": "Search by name",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Campaign list"
+ }
+ },
+ "summary": "List campaigns",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}": {
+ "get": {
+ "operationId": "CampaignsController_findById",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Campaign found"
+ },
+ "404": {
+ "description": "Campaign not found"
+ }
+ },
+ "summary": "Get a campaign by ID",
+ "tags": [
+ "Campaigns"
+ ]
+ },
+ "put": {
+ "operationId": "CampaignsController_update",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateCampaignDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Campaign updated"
+ },
+ "400": {
+ "description": "Campaign not in draft status"
+ },
+ "404": {
+ "description": "Campaign not found"
+ }
+ },
+ "summary": "Update a campaign (draft only)",
+ "tags": [
+ "Campaigns"
+ ]
+ },
+ "delete": {
+ "operationId": "CampaignsController_delete",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Campaign deleted"
+ },
+ "400": {
+ "description": "Campaign not in draft status"
+ },
+ "404": {
+ "description": "Campaign not found"
+ }
+ },
+ "summary": "Delete a campaign (draft only)",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/recipients": {
+ "post": {
+ "operationId": "CampaignsController_addRecipients",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AddRecipientsDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Recipients added"
+ },
+ "400": {
+ "description": "Campaign not in draft status"
+ }
+ },
+ "summary": "Add recipients manually (user IDs)",
+ "tags": [
+ "Campaigns"
+ ]
+ },
+ "get": {
+ "operationId": "CampaignsController_getRecipients",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "updatedAt",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: updatedAt unix timestamp of last item",
+ "schema": {
+ "type": "number"
+ }
+ },
+ {
+ "name": "id",
+ "required": false,
+ "in": "query",
+ "description": "Cursor: id of last item",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "affix",
+ "required": false,
+ "in": "query",
+ "description": "Cursor direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "append",
+ "prepend"
+ ]
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Items per page",
+ "schema": {
+ "minimum": 1,
+ "maximum": 100,
+ "type": "number",
+ "default": 20
+ }
+ },
+ {
+ "name": "sort",
+ "required": false,
+ "in": "query",
+ "description": "Sort field",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "updatedAt",
+ "createdAt"
+ ]
+ }
+ },
+ {
+ "name": "order",
+ "required": false,
+ "in": "query",
+ "description": "Sort direction",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ]
+ }
+ },
+ {
+ "name": "status",
+ "required": false,
+ "in": "query",
+ "description": "Filter by status",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "pending",
+ "processing",
+ "completed",
+ "failed"
+ ]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Recipient list"
+ }
+ },
+ "summary": "List campaign recipients",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/recipients/summary": {
+ "get": {
+ "operationId": "CampaignsController_getRecipientSummary",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Recipient summary"
+ }
+ },
+ "summary": "Get recipient status summary",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/recipients/upload-url": {
+ "post": {
+ "operationId": "CampaignsController_getUploadUrl",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Presigned URL generated"
+ },
+ "400": {
+ "description": "Campaign not in draft status"
+ }
+ },
+ "summary": "Get S3 presigned URL for CSV upload",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/import-csv": {
+ "post": {
+ "operationId": "CampaignsController_importCsv",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CsvUploadDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "202": {
+ "description": "Import job created"
+ },
+ "400": {
+ "description": "Campaign not in draft status"
+ },
+ "409": {
+ "description": "Import already in progress"
+ }
+ },
+ "summary": "Start async CSV import for campaign recipients",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/import-status": {
+ "get": {
+ "operationId": "CampaignsController_getImportStatus",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Import status returned"
+ },
+ "404": {
+ "description": "No active import found"
+ }
+ },
+ "summary": "Get current import job status for a campaign",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/send": {
+ "post": {
+ "operationId": "CampaignsController_send",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Campaign send enqueued"
+ },
+ "400": {
+ "description": "Campaign not in sendable status or no recipients"
+ }
+ },
+ "summary": "Trigger campaign send",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/schedule": {
+ "post": {
+ "operationId": "CampaignsController_schedule",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ScheduleCampaignDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Campaign scheduled"
+ },
+ "400": {
+ "description": "Campaign not in draft status or invalid time"
+ }
+ },
+ "summary": "Schedule campaign send",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/sequence-metrics": {
+ "get": {
+ "operationId": "CampaignsController_getSequenceMetrics",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Sequence metrics returned"
+ }
+ },
+ "summary": "Get per-step sequence delivery metrics for a campaign",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/campaigns/{id}/cancel": {
+ "post": {
+ "operationId": "CampaignsController_cancel",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "id",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Campaign cancelled"
+ },
+ "400": {
+ "description": "Campaign not in cancellable status"
+ }
+ },
+ "summary": "Cancel a campaign",
+ "tags": [
+ "Campaigns"
+ ]
+ }
+ },
+ "/notifications/messages": {
+ "post": {
+ "description": "Sendbird-equivalent POST /notifications/messages. 1\u201310 receivers = realtime (synchronous, returns notificationId). 11\u201310,000 receivers = inline batch (asynchronous, returns batchId). Larger sends should use the Campaigns CSV flow.",
+ "operationId": "NotificationSendController_send",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SendNotificationDto"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Realtime: { notificationId, channels[], mode: \"realtime\" }. Batch: { batchId, total, channels[], mode: \"batch\" }."
+ },
+ "400": {
+ "description": "Template not found, template not approved, or recipient count exceeds limits"
+ },
+ "403": {
+ "description": "Admin-only \u2014 onbehalfof header must not be present"
+ }
+ },
+ "summary": "Send a notification using a template",
+ "tags": [
+ "Notifications"
+ ]
+ }
+ },
+ "/analytics/overview": {
+ "get": {
+ "operationId": "AnalyticsController_getOverview",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "dimension",
+ "required": false,
+ "in": "query",
+ "description": "Rollup dimension to query",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "campaign",
+ "template",
+ "channel"
+ ]
+ }
+ },
+ {
+ "name": "dimensionId",
+ "required": false,
+ "in": "query",
+ "description": "Dimension ID (campaign ID, template ID, or channel type)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "period",
+ "required": false,
+ "in": "query",
+ "description": "Rollup period",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "hourly",
+ "daily"
+ ]
+ }
+ },
+ {
+ "name": "startDate",
+ "required": false,
+ "in": "query",
+ "description": "Start date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "endDate",
+ "required": false,
+ "in": "query",
+ "description": "End date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Maximum number of results",
+ "schema": {
+ "default": 50,
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Analytics overview"
+ }
+ },
+ "summary": "Get aggregated analytics overview",
+ "tags": [
+ "Analytics"
+ ]
+ }
+ },
+ "/analytics/campaigns/{campaignId}": {
+ "get": {
+ "operationId": "AnalyticsController_getCampaignAnalytics",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "campaignId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "dimension",
+ "required": false,
+ "in": "query",
+ "description": "Rollup dimension to query",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "campaign",
+ "template",
+ "channel"
+ ]
+ }
+ },
+ {
+ "name": "dimensionId",
+ "required": false,
+ "in": "query",
+ "description": "Dimension ID (campaign ID, template ID, or channel type)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "period",
+ "required": false,
+ "in": "query",
+ "description": "Rollup period",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "hourly",
+ "daily"
+ ]
+ }
+ },
+ {
+ "name": "startDate",
+ "required": false,
+ "in": "query",
+ "description": "Start date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "endDate",
+ "required": false,
+ "in": "query",
+ "description": "End date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Maximum number of results",
+ "schema": {
+ "default": 50,
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Campaign analytics"
+ }
+ },
+ "summary": "Get campaign analytics drill-down",
+ "tags": [
+ "Analytics"
+ ]
+ }
+ },
+ "/analytics/templates/{templateId}": {
+ "get": {
+ "operationId": "AnalyticsController_getTemplateAnalytics",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "templateId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "dimension",
+ "required": false,
+ "in": "query",
+ "description": "Rollup dimension to query",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "campaign",
+ "template",
+ "channel"
+ ]
+ }
+ },
+ {
+ "name": "dimensionId",
+ "required": false,
+ "in": "query",
+ "description": "Dimension ID (campaign ID, template ID, or channel type)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "period",
+ "required": false,
+ "in": "query",
+ "description": "Rollup period",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "hourly",
+ "daily"
+ ]
+ }
+ },
+ {
+ "name": "startDate",
+ "required": false,
+ "in": "query",
+ "description": "Start date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "endDate",
+ "required": false,
+ "in": "query",
+ "description": "End date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Maximum number of results",
+ "schema": {
+ "default": 50,
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Template analytics"
+ }
+ },
+ "summary": "Get template analytics drill-down",
+ "tags": [
+ "Analytics"
+ ]
+ }
+ },
+ "/analytics/channels": {
+ "get": {
+ "operationId": "AnalyticsController_getChannelBreakdown",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "dimension",
+ "required": false,
+ "in": "query",
+ "description": "Rollup dimension to query",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "campaign",
+ "template",
+ "channel"
+ ]
+ }
+ },
+ {
+ "name": "dimensionId",
+ "required": false,
+ "in": "query",
+ "description": "Dimension ID (campaign ID, template ID, or channel type)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "period",
+ "required": false,
+ "in": "query",
+ "description": "Rollup period",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "hourly",
+ "daily"
+ ]
+ }
+ },
+ {
+ "name": "startDate",
+ "required": false,
+ "in": "query",
+ "description": "Start date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "endDate",
+ "required": false,
+ "in": "query",
+ "description": "End date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Maximum number of results",
+ "schema": {
+ "default": 50,
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Channel breakdown"
+ }
+ },
+ "summary": "Get channel breakdown analytics",
+ "tags": [
+ "Analytics"
+ ]
+ }
+ },
+ "/analytics/users/{userId}": {
+ "get": {
+ "operationId": "AnalyticsController_getUserInsights",
+ "parameters": [
+ {
+ "name": "appid",
+ "in": "header",
+ "description": "Tenant application ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "userId",
+ "required": true,
+ "in": "path",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "dimension",
+ "required": false,
+ "in": "query",
+ "description": "Rollup dimension to query",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "campaign",
+ "template",
+ "channel"
+ ]
+ }
+ },
+ {
+ "name": "dimensionId",
+ "required": false,
+ "in": "query",
+ "description": "Dimension ID (campaign ID, template ID, or channel type)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "period",
+ "required": false,
+ "in": "query",
+ "description": "Rollup period",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "hourly",
+ "daily"
+ ]
+ }
+ },
+ {
+ "name": "startDate",
+ "required": false,
+ "in": "query",
+ "description": "Start date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "endDate",
+ "required": false,
+ "in": "query",
+ "description": "End date filter (ISO 8601)",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Maximum number of results",
+ "schema": {
+ "default": 50,
+ "type": "number"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Aggregated counts: viewed, clicked, interacted, lastEngagement (unix seconds or null)"
+ }
+ },
+ "summary": "Get user-level engagement insights",
+ "tags": [
+ "Analytics"
+ ]
+ }
+ }
+ },
+ "info": {
+ "title": "Campaigns Service API",
+ "description": "Campaigns Service REST API",
+ "version": "1.0",
+ "contact": {}
+ },
+ "tags": [
+ {
+ "name": "Notification Feed",
+ "description": "Operations on the per-user in-app notification feed."
+ },
+ {
+ "name": "Channels",
+ "description": "Manage channel instances and per-type availability."
+ },
+ {
+ "name": "Templates",
+ "description": "Manage templates and their versions."
+ },
+ {
+ "name": "Template Categories",
+ "description": "Manage template categories for feed filtering."
+ },
+ {
+ "name": "Campaigns",
+ "description": "Create, schedule, and manage notification campaigns."
+ },
+ {
+ "name": "Notifications",
+ "description": "Send notifications directly via API."
+ },
+ {
+ "name": "Push Notifications",
+ "description": "Manage push notification delivery and engagement."
+ },
+ {
+ "name": "Analytics",
+ "description": "Delivery and engagement analytics."
+ }
+ ],
+ "servers": [
+ {
+ "url": "https://{appId}.api-{region}.cometchat.io/v3/campaigns",
+ "variables": {
+ "appId": {
+ "default": "appId",
+ "description": "(Required) App ID"
+ },
+ "region": {
+ "enum": [
+ "us",
+ "eu",
+ "in"
+ ],
+ "default": "us",
+ "description": "Select Region"
+ }
+ }
+ }
+ ],
+ "components": {
+ "securitySchemes": {
+ "apikey": {
+ "type": "apiKey",
+ "in": "header",
+ "name": "apikey",
+ "description": "Your CometChat REST API Key."
+ }
+ },
+ "schemas": {
+ "EngagementEventDto": {
+ "type": "object",
+ "properties": {
+ "topic": {
+ "type": "string",
+ "description": "Freeform topic discriminator for the interacted event. Well-known value: \"clicked\". Custom values: any alphanumeric + underscore/hyphen string \u226464 chars.",
+ "example": "clicked",
+ "maxLength": 64
+ }
+ }
+ },
+ "CreateCategoryDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Category name"
+ },
+ "description": {
+ "type": "string",
+ "description": "Category description"
+ }
+ },
+ "required": [
+ "name"
+ ]
+ },
+ "UpdateCategoryDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Category name"
+ },
+ "description": {
+ "type": "string",
+ "description": "Category description"
+ }
+ }
+ },
+ "ChannelContentDto": {
+ "type": "object",
+ "properties": {
+ "channelId": {
+ "type": "string",
+ "description": "Channel instance ID (links to Channel entity)"
+ },
+ "channelType": {
+ "type": "string",
+ "description": "Channel type identifier"
+ },
+ "content": {
+ "type": "object",
+ "description": "Channel content (defaults applied if omitted)"
+ },
+ "dataType": {
+ "type": "string",
+ "description": "Data type",
+ "enum": [
+ "ui_template",
+ "data_template"
+ ]
+ },
+ "categoryFilterEnabled": {
+ "type": "boolean",
+ "description": "Enable category filter (in-app only)"
+ },
+ "templateLabelEnabled": {
+ "type": "boolean",
+ "description": "Enable template label (in-app only)"
+ },
+ "messageRetentionHours": {
+ "type": "number",
+ "description": "Message retention in hours"
+ },
+ "sequenceOrder": {
+ "type": "number",
+ "description": "Position in sequence (1, 2, 3...)"
+ },
+ "stopCondition": {
+ "type": "string",
+ "description": "Stop condition for sequence step. Allowed values depend on channelType: feed channels (in_app) accept `delivered` | `read` | `engaged`; push channels accept `delivered` | `clicked`. Validated per-channel at template save (ENG-35239)."
+ },
+ "waitMinutes": {
+ "type": "number",
+ "description": "Wait time in minutes before advancing to next step",
+ "enum": [
+ 5,
+ 10,
+ 30,
+ 60,
+ 240,
+ 1440
+ ]
+ }
+ },
+ "required": [
+ "channelType"
+ ]
+ },
+ "CreateTemplateDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Template name"
+ },
+ "templateId": {
+ "type": "string",
+ "description": "Human-readable slug (auto-generated from name if omitted)"
+ },
+ "templateCategory": {
+ "type": "string",
+ "description": "Template category",
+ "enum": [
+ "Onboarding",
+ "Transactional",
+ "Marketing",
+ "Product_Showcase",
+ "Alerts",
+ "Polls",
+ "Custom"
+ ]
+ },
+ "label": {
+ "type": "string",
+ "description": "Display label shown on notification (e.g. Promo, Alert)"
+ },
+ "alternativeText": {
+ "type": "string",
+ "description": "Plain-text fallback when push is suppressed or rich content cannot render. Sendbird-parity field."
+ },
+ "tags": {
+ "description": "First-class tags for filtering/segmentation",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "status": {
+ "type": "string",
+ "description": "Template status",
+ "enum": [
+ "draft",
+ "approved",
+ "archived"
+ ]
+ },
+ "channels": {
+ "description": "Channel configurations",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ChannelContentDto"
+ }
+ },
+ "variableSchema": {
+ "description": "Variable schema definitions",
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "config": {
+ "type": "object",
+ "description": "Additional configuration"
+ }
+ },
+ "required": [
+ "name",
+ "channels"
+ ]
+ },
+ "UpdateTemplateDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "templateCategory": {
+ "type": "string"
+ },
+ "label": {
+ "type": "string",
+ "description": "Display label shown on notification"
+ },
+ "alternativeText": {
+ "type": "string",
+ "description": "Plain-text fallback when push is suppressed or rich content cannot render. Sendbird-parity field."
+ },
+ "tags": {
+ "description": "First-class tags for filtering/segmentation",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "status": {
+ "type": "string"
+ },
+ "variableSchema": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "config": {
+ "type": "object"
+ }
+ }
+ },
+ "UpdateChannelContentDto": {
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "object",
+ "description": "Channel content payload"
+ }
+ },
+ "required": [
+ "content"
+ ]
+ },
+ "CreateChannelDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Channel display name",
+ "example": "My Push Channel"
+ },
+ "type": {
+ "type": "string",
+ "description": "Channel type",
+ "enum": [
+ "in_app",
+ "push",
+ "sms",
+ "email",
+ "whatsapp",
+ "custom"
+ ],
+ "example": "push"
+ },
+ "channelId": {
+ "type": "string",
+ "description": "Channel slug (auto-generated from name if omitted)",
+ "example": "cc-notification-channel-my-push"
+ },
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether the channel is enabled",
+ "default": false
+ },
+ "metadata": {
+ "type": "object",
+ "description": "Channel-specific metadata",
+ "example": {
+ "apiKey": "xxx",
+ "senderId": "yyy"
+ }
+ }
+ },
+ "required": [
+ "name",
+ "type"
+ ]
+ },
+ "UpdateChannelDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Channel display name"
+ },
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether the channel is enabled"
+ },
+ "metadata": {
+ "type": "object",
+ "description": "Channel-specific metadata",
+ "example": {
+ "apiKey": "xxx",
+ "senderId": "yyy"
+ }
+ }
+ }
+ },
+ "PushEngagementEventDto": {
+ "type": "object",
+ "properties": {
+ "topic": {
+ "type": "string",
+ "description": "Freeform topic discriminator for the interacted event. Custom values: any alphanumeric + underscore/hyphen string \u226464 chars.",
+ "example": "dismissed",
+ "maxLength": 64
+ }
+ }
+ },
+ "CreateCampaignDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Campaign name"
+ },
+ "templateId": {
+ "type": "string",
+ "description": "Template ID (CUID or templateId slug)"
+ },
+ "templateVersion": {
+ "type": "number",
+ "description": "Template version number to pin",
+ "minimum": 1
+ },
+ "variables": {
+ "type": "object",
+ "description": "Campaign-level default variables \u2014 applied to every recipient as a fallback layer below per-user CSV values and above template variableSchema defaults. Example: `{ \"promoCode\": \"SUMMER25\", \"supportEmail\": \"help@acme.io\" }`.",
+ "example": {
+ "promoCode": "SUMMER25"
+ }
+ },
+ "config": {
+ "type": "object",
+ "description": "Additional campaign configuration (free-form)"
+ }
+ },
+ "required": [
+ "name",
+ "templateId",
+ "templateVersion"
+ ]
+ },
+ "UpdateCampaignDto": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Campaign name"
+ },
+ "config": {
+ "type": "object",
+ "description": "Additional campaign configuration"
+ }
+ }
+ },
+ "AddRecipientsDto": {
+ "type": "object",
+ "properties": {
+ "userIds": {
+ "description": "Array of user IDs to add as recipients",
+ "minItems": 1,
+ "maxItems": 10000,
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "userVariables": {
+ "type": "object",
+ "description": "Per-user variables, keyed by userId. Persisted on each `CampaignRecipient.variables` row at insert time. Renderer substitutes these into template content per recipient. Example: `{ \"user_42\": { \"name\": \"Ajay\" }, \"user_43\": { \"name\": \"Sam\" } }`.",
+ "example": {
+ "user_42": {
+ "name": "Ajay"
+ }
+ }
+ }
+ },
+ "required": [
+ "userIds"
+ ]
+ },
+ "CsvUploadDto": {
+ "type": "object",
+ "properties": {
+ "s3Key": {
+ "type": "string",
+ "description": "S3 object key of the uploaded CSV file"
+ }
+ },
+ "required": [
+ "s3Key"
+ ]
+ },
+ "ScheduleCampaignDto": {
+ "type": "object",
+ "properties": {
+ "scheduledAt": {
+ "type": "number",
+ "description": "Scheduled send time as Unix timestamp (seconds)",
+ "example": 1714000000
+ }
+ },
+ "required": [
+ "scheduledAt"
+ ]
+ },
+ "SendNotificationDto": {
+ "type": "object",
+ "properties": {
+ "templateId": {
+ "type": "string",
+ "description": "Template CUID or templateId slug. Template must be in approved status.",
+ "example": "order_update"
+ },
+ "receivers": {
+ "description": "Array of target user IDs. 1\u201310 = realtime (synchronous, returns notificationId immediately). 11\u201310,000 = batch (asynchronous, returns batchId; processing happens via queue).",
+ "minItems": 1,
+ "maxItems": 10000,
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "variables": {
+ "type": "object",
+ "description": "Per-user variables. Keyed by userId; values are { variableName: value } objects. Variables are applied to the template content at delivery time.",
+ "example": {
+ "user_42": {
+ "user_name": "John",
+ "order_id": "12345"
+ },
+ "user_43": {
+ "user_name": "Sarah",
+ "order_id": "12346"
+ }
+ }
+ },
+ "tag": {
+ "type": "string",
+ "description": "Optional analytics tag attached to the send (passes through to delivery records)."
+ }
+ },
+ "required": [
+ "templateId",
+ "receivers"
+ ]
+ }
+ }
+ },
+ "security": [
+ {
+ "apikey": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/campaigns.mdx b/campaigns.mdx
new file mode 100644
index 000000000..5241de0e5
--- /dev/null
+++ b/campaigns.mdx
@@ -0,0 +1,143 @@
+---
+title: "Overview"
+sidebarTitle: "Overview"
+description: "Send transactional notifications, run scheduled campaigns, and manage the per-user in-app feed across in-app and push channels."
+---
+
+CometChat Campaigns is a notification management system that lets you design, target, and deliver in-app and push notifications to your users. It provides a visual dashboard for creating notification templates, managing delivery channels, and sending targeted campaigns — either immediately or on a schedule. Your backend can also send notifications programmatically via API.
+
+## Key Capabilities
+
+- **Send transactional notifications** — Deliver a templated message to one or many users in real time, with per-recipient variable resolution.
+- **Organise content with templates** — Author reusable templates with typed variables, categories, and tags. Every notification flows through a template.
+- **Immediate and scheduled campaigns** — Send notifications right away or schedule them for a future date and time.
+- **Track delivery and engagement** — Capture delivered, read, clicked, and interacted signals for analytics and unread counts.
+
+## Supported Channels
+
+| Channel | Description |
+|---------|-------------|
+| `in_app` | In-app notification feed rendered client-side via SDK |
+| `push` | Push notifications delivered via FCM/APNs through CometChat's push infrastructure |
+
+## Prerequisites
+
+- A CometChat app with `appId` and `apiKey`
+- At least one enabled channel created
+- At least one approved template
+- Users registered in CometChat (targeted by their UID)
+
+## Setup Flow
+
+The typical setup to send your first notification from the CometChat Dashboard:
+
+
+
+ Go to Channels → Create Channel → Select type (`in_app` or `push`) → Name it → Save.
+
+
+
+
+
+ Go to Categories → Create → Name it (e.g., "Marketing", "Alerts"). Categories are used for filter chips in the notification feed UI.
+
+
+
+
+
+ Go to Templates → Create Template → Enter name → Select channel(s) → Design content in the visual builder.
+
+
+
+
+
+ Design the card bubble — this is the Card Schema JSON that gets rendered natively in the notification feed.
+
+
+
+
+
+ Define your audience, select the channel, category, and template, then send or schedule the campaign.
+
+
+
+
+
+
+## Limits
+
+| Resource | Limit |
+|----------|-------|
+| Recipients per API call | 10,000 |
+
+## Common Use Cases
+
+### Transactional alerts
+Order shipped, payment receipt, password reset, security alert. One templated message per event, dispatched in real time, with per-recipient variable substitution (`{{order_id}}`, `{{user_name}}`).
+
+### Marketing campaigns
+Product launches, promotional offers, re-engagement nudges. Schedule the send, upload a recipient list, and deliver across in-app and push channels.
+
+### Operational messages
+Maintenance windows, policy updates, account changes. Dispatched to a broad audience and routed through the in-app feed so users can revisit them.
+
+## Frontend Integration
+
+Integrate Campaigns into your client application using either the pre-built UI Kit components or the Chat SDK for custom implementations.
+
+### UI Kit
+
+
+
+ Pre-built React components for notifications
+
+
+ Native mobile components for React Native
+
+
+ Swift UI Kit for iOS notifications
+
+
+ Android UI Kit for notifications
+
+
+ Flutter widgets for notifications
+
+
+
+### SDK
+
+
+
+ JavaScript SDK integration
+
+
+ React Native SDK integration
+
+
+ Swift SDK integration
+
+
+ Android SDK integration
+
+
+ Flutter SDK integration
+
+
+
+## Quick Navigation
+
+
+
+ Configure delivery channels for in-app and push notifications
+
+
+ Organize notifications with categories for filtering
+
+
+ Create reusable notification templates with variables and versioning
+
+
+ Create and manage targeted notification campaigns
+
+
diff --git a/campaigns/analytics.mdx b/campaigns/analytics.mdx
new file mode 100644
index 000000000..e723d6b2d
--- /dev/null
+++ b/campaigns/analytics.mdx
@@ -0,0 +1,41 @@
+---
+title: "Analytics"
+description: "Track notification delivery and engagement metrics from the Campaigns dashboard."
+---
+
+The Analytics page gives you a snapshot of how your notifications are performing — how many were delivered, read, and engaged with.
+
+## Metrics
+
+| Metric | Description |
+|--------|-------------|
+| Delivered | Total notifications confirmed delivered |
+| Read | Total feed items marked as read |
+| Engagement | Total interactions (interacted events) |
+
+## Engagement Funnel
+
+A visual breakdown showing the progression from sent to engaged:
+
+- **Sent** — total notifications dispatched
+- **Delivered** — as a percentage of sent
+- **Read** — as a percentage of sent
+- **Engagement** — as a percentage of sent
+
+## Recent Campaigns
+
+A table showing your most recent campaigns. Clicking a campaign row opens the campaign details page.
+
+## Campaign Details
+
+Shows per-campaign metrics: sent, delivered, read, and interacted breakdown for a specific campaign.
+
+## Template Details
+
+Available from the templates list under analytics. Shows how a specific template performs across all sends that use it.
+
+## Per-User Engagement
+
+Per-user engagement data is not on this page — it's available on the individual User Detail page.
+
+For programmatic access to analytics data, see the [Analytics API](/rest-api/campaigns-apis/analytics/overview-metrics).
diff --git a/campaigns/campaigns.mdx b/campaigns/campaigns.mdx
new file mode 100644
index 000000000..d45f3cc4f
--- /dev/null
+++ b/campaigns/campaigns.mdx
@@ -0,0 +1,63 @@
+---
+title: "Campaigns"
+sidebarTitle: "Campaigns"
+description: "Create, schedule, and manage targeted notification campaigns for batch user delivery."
+---
+
+A campaign lets you send notifications to a targeted group of users using a pre-designed template. You select recipients, choose a template, personalize content with variables, and either send immediately or schedule for later. The dashboard tracks delivery progress and shows how many notifications were sent, delivered, and failed.
+
+## Creating a Campaign
+
+The dashboard wizard walks you through four steps:
+
+
+
+ Choose your input mode:
+ - **CSV** — Upload a file with a `user_id` column and optional variable columns
+ - **User picker** — Select from the Users list
+
+
+ Pick an approved template. The template determines the content and delivery channels.
+
+
+ Enter campaign name, set per-user variables, and add an optional analytics tag.
+
+
+ Review the summary. Choose **Send Now** or **Schedule** for a future date/time.
+
+
+
+## Scheduling
+
+| Option | Description |
+|--------|-------------|
+| Send Now | Immediate dispatch |
+| Schedule | Set a future date/time |
+
+
+You can click "Send Now" on a scheduled campaign to override the schedule and send immediately.
+
+
+## Campaign Status Flow
+
+| Status | Description |
+|--------|-------------|
+| `draft` | Campaign created but not yet sent or scheduled |
+| `scheduled` | Queued for future delivery |
+| `sending` | Currently dispatching to recipients |
+| `completed` | All recipients processed successfully |
+| `failed` | All recipients failed |
+| `partially_failed` | Some recipients succeeded, some failed |
+| `cancelled` | Cancelled before send time |
+
+## Cancel and Delete
+
+- **Cancel** — Cancels a scheduled or draft campaign. Cannot cancel once `sending` has started.
+- **Delete** — Only draft campaigns can be deleted.
+
+## Limits
+
+| Limit | Value |
+|-------|-------|
+| Recipients per API call | 10,000 |
+
diff --git a/campaigns/categories.mdx b/campaigns/categories.mdx
new file mode 100644
index 000000000..ae5166b1c
--- /dev/null
+++ b/campaigns/categories.mdx
@@ -0,0 +1,28 @@
+---
+title: "Categories"
+sidebarTitle: "Categories"
+description: "Organize notifications with categories for filtering and user-facing feed segmentation."
+---
+
+Categories are labels used to organize templates and filter notification feed items. When a template has a category assigned, all notifications sent with that template carry the category — allowing end-users to filter their feed by category.
+
+## Managing Categories
+
+1. Go to **Categories** in the sidebar
+2. **Create** — Click "Create Category" → Enter name and optional description → Save
+3. **Edit** — Click a category → Update name or description → Save
+4. **Delete** — Click delete → Confirm removal
+
+## Category Properties
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `name` | string | Category name (unique per app) |
+| `description` | string | Optional description |
+
+## Feed Filtering
+
+End-users can filter their notification feed by category using the SDK or feed API. See [List Feed](/rest-api/campaigns-apis/notification-feed/list-feed) for the full API reference.
+
+This only works when `categoryFilterEnabled` is set to `true` on the template's channel configuration.
+
diff --git a/campaigns/channels.mdx b/campaigns/channels.mdx
new file mode 100644
index 000000000..675337bde
--- /dev/null
+++ b/campaigns/channels.mdx
@@ -0,0 +1,52 @@
+---
+title: "Channels"
+sidebarTitle: "Channels"
+description: "Configure delivery channels for your campaign notifications — in-app feed and push notifications."
+---
+
+Channels define where notifications are delivered. Each template references one or more channel instances, and each channel instance is tied to a specific delivery type.
+
+## Supported Channel Types
+
+| Type | Description |
+|------|-------------|
+| `in_app` | Feed notifications rendered by SDK |
+| `push` | Lock-screen notifications via FCM/APNs |
+
+## Creating a Channel
+
+1. Go to **Channels** → Click **Create Channel**
+2. Select the channel type (`in_app` or `push`)
+3. Configure the channel:
+ - **Name** — Display name (e.g., "Default Feed", "Promotions Feed")
+ - **Channel ID** — Auto-generated slug: `cc-notification-channel-` (immutable after creation)
+ - **Enabled** — Toggle on/off (defaults to disabled)
+4. Click **Create Channel**
+
+## Channel Properties
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `name` | string | Display name |
+| `channelId` | string | Unique slug (immutable after creation) |
+| `type` | enum | `in_app` \| `push` |
+| `enabled` | boolean | Whether channel is available for template assignment (defaults to `false`) |
+
+
+Push notification configuration (FCM/APNs keys) is managed at the CometChat app level, not per-channel in Campaigns.
+
+
+## Multiple Channels
+
+You can create multiple `in_app` channels. For example:
+- "Promotions Feed" (`in_app`) — for marketing notifications
+- "Alerts Feed" (`in_app`) — for transactional alerts
+
+Push channels are limited to one per app. Templates reference a specific channel instance by its `channelId`.
+
+## Limits
+
+| Limit | Value |
+|-------|-------|
+| `in_app` channels per app | No hard limit |
+| `push` channels per app | 1 |
diff --git a/campaigns/notification-settings.mdx b/campaigns/notification-settings.mdx
new file mode 100644
index 000000000..153896772
--- /dev/null
+++ b/campaigns/notification-settings.mdx
@@ -0,0 +1,13 @@
+---
+title: "Notification Settings"
+sidebarTitle: "Notification Settings"
+description: "Configure push notification providers for delivering campaign notifications."
+---
+
+The Notification Settings page lets you configure push notification providers (FCM, APNs, Custom) for your app. These credentials are required to deliver push notifications through campaigns.
+
+For detailed setup instructions, provider configuration, and credential management, visit the Push Notifications documentation.
+
+
+ Configure FCM, APNs, and custom push providers
+
diff --git a/campaigns/sequences.mdx b/campaigns/sequences.mdx
new file mode 100644
index 000000000..23f596046
--- /dev/null
+++ b/campaigns/sequences.mdx
@@ -0,0 +1,49 @@
+---
+title: "Sequences"
+description: "Control the order and timing of notifications across channels with channel sequencing."
+---
+
+Sequences let you define the order in which notification channels fire and the conditions that stop the sequence early. Instead of broadcasting the same message across all channels simultaneously, you deliver in a prioritised chain where each step waits for a result before deciding whether to proceed.
+
+### Why use sequences
+
+- **Improve deliverability.** Target the channel most likely to reach the user based on their prior engagement pattern.
+- **Avoid notification fatigue.** Stop the chain as soon as the user has seen or acted on the message, rather than hitting them on every channel.
+
+### How it works
+
+Sequences are configured at the template level. When you create or edit a template in the Dashboard:
+
+1. Add two or more channels to the template.
+2. Enable the **Sequence** toggle in the template settings.
+3. Arrange the channels in the desired delivery order. The first channel fires immediately at send time.
+4. For each subsequent step, configure:
+ - **Stop condition.** The engagement signal that halts the sequence. Available conditions depend on the channel:
+ - **In-app:** `delivered` | `read` | `engaged`
+ - **Push:** `delivered` | `clicked`
+ - **Wait window.** How long to wait for the stop condition before moving to the next step: 5, 10, 30, 60, 240, or 1440 minutes.
+
+If the stop condition is met within the wait window, the remaining steps are skipped. If the window expires without the condition being met, the next channel fires.
+
+### Example
+
+A template with two channels configured as a sequence:
+
+| Step | Channel | Stop condition | Wait |
+| ---- | -------- | -------------- | ------ |
+| 1 | In-app | delivered | 30 min |
+| 2 | Push | n/a | n/a |
+
+At send time, the in-app notification is dispatched immediately. If the item is marked delivered within 30 minutes, the sequence stops and push is never sent. If 30 minutes pass without a delivery signal, push fires as a fallback.
+
+### Metrics
+
+When a template uses sequencing, per-step delivery metrics are available through the Dashboard and the analytics API. Which metrics are reported depends on the channel:
+
+| Channel | Requested | Delivered | Clicked |
+| -------- | :-------: | :-------: | :-----: |
+| In-app | ✓ | ✓ | ✓ |
+| Push | ✓ | ✓ | ✓ |
+
+These metrics help you evaluate whether your sequence order and wait windows are tuned correctly, and where users are dropping off.
+
diff --git a/campaigns/templates.mdx b/campaigns/templates.mdx
new file mode 100644
index 000000000..2cdee5430
--- /dev/null
+++ b/campaigns/templates.mdx
@@ -0,0 +1,85 @@
+---
+title: "Templates"
+sidebarTitle: "Templates"
+description: "Create reusable notification templates with variables, versioning, and multi-channel support."
+---
+
+A template is a reusable notification design that defines the content, delivery channels, and personalization variables for a notification. Every notification — whether sent via campaign or API — flows through a template.
+
+## Template Properties
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `name` | string | Yes | Display name |
+| `templateId` | string | No (auto-generated) | Slug: `cc-template-` (immutable) |
+| `templateCategory` | string | No | Category name (e.g., "Marketing") |
+| `label` | string | No | Display label shown on notification (e.g., "Promo") |
+| `alternativeText` | string | No | Plain-text fallback when rich content can't render |
+| `tags` | string[] | No | Tags for filtering and segmentation |
+| `status` | enum | Yes | `draft` \| `approved` \| `archived` |
+| `channels` | array | Yes | Channel configurations (at least one) |
+| `variableSchema` | array | No | Variable definitions |
+
+## Content Types
+
+| dataType | Description |
+|----------|-------------|
+| `ui_template` | Visual template designed in the Bubble Builder (drag-and-drop editor) |
+
+## Variables
+
+Variables allow per-recipient personalization in notification content.
+
+- **Syntax**: `{{variable_name}}` in template content
+- **Naming**: Letters, numbers, and underscores only (`^[a-zA-Z_][a-zA-Z0-9_]*$`)
+- **Type**: Selected from a dropdown with the following options:
+
+| Type | Value Format |
+|------|-------------|
+| `string` | Plain text string (e.g., `"Hello World"`) |
+| `image` | Object: `{ "url": "https://...", "width": 300, "height": 200 }` — all fields required |
+| `action` | Object: `{ "type": "web" or "custom", "data": "https://..." }` — both fields required |
+
+
+`defaultValue` is only supported for `string` type variables. Image and action variables do not support defaults.
+
+- **Resolution**: Per-user values are passed at send time in the `variables` field
+
+## Template Versioning
+
+Templates are versioned to maintain a history of changes:
+
+- Templates start at version 1
+- Editing an **approved** template auto-bumps the version (creates a new snapshot)
+- You can manually create a version via the "New Version" button
+- Campaigns pin to a specific `templateVersion` at send time
+- Old versions are immutable — safe for historical reference
+
+To access old versions via API, use [Get Template](/rest-api/campaigns-apis/templates/get-template) — the response includes a `versions` array with all previous versions.
+
+## Channel Configuration
+
+Each template has one or more channel configurations:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `channelType` | string | `in_app` \| `push` |
+| `channelId` | string | Links to a specific Channel entity |
+| `content` | object | Notification content (Bubble Builder JSON or custom) |
+| `dataType` | string | `ui_template` |
+| `messageRetentionHours` | number | Hours before feed item expires (0 = never) |
+| `categoryFilterEnabled` | boolean | Enable category-based filtering in feed |
+| `templateLabelEnabled` | boolean | Show label badge on notification |
+
+## Template Statuses
+
+| Status | Description |
+|--------|-------------|
+| `draft` | Work in progress — cannot be used to send |
+| `approved` | Ready to use — can be selected for campaigns and notifications |
+| `archived` | Soft-deleted — hidden from lists, not usable |
+
+
+Only templates with `approved` status can be used to send notifications. Archived templates are hidden and cannot be used.
+
+
diff --git a/campaigns/users.mdx b/campaigns/users.mdx
new file mode 100644
index 000000000..eb55c06d2
--- /dev/null
+++ b/campaigns/users.mdx
@@ -0,0 +1,40 @@
+---
+title: "Users"
+sidebarTitle: "Users"
+description: "Browse and target CometChat users for campaign notifications."
+---
+
+A user in Campaigns is the same as a CometChat user — identified by their `uid`. The Users page in the dashboard lets you browse users registered in your CometChat app. No separate user creation is needed for Campaigns.
+
+## Targeting Users
+
+When creating a campaign, you can target users through the following methods:
+
+| Method | Description |
+|--------|-------------|
+| CSV upload | Upload a CSV file with a `user_id` column and optional per-user variables |
+| User picker | Select from the Users list in the dashboard |
+
+
+## Filtering Users
+
+The Users page supports filtering by:
+
+| Filter | Description |
+|--------|-------------|
+| Search | Filter by UID or name |
+| Role | Filter by CometChat user role |
+| Status | Filter by online / offline status |
+| Created At | Filter by user creation date |
+
+## Sending Notifications to Users
+
+You don't always need to create a campaign to reach your users. You can send notifications directly — either from the dashboard or via API for multiple users.
+
+For the API reference, see [Send Notification](/rest-api/campaigns-apis/notifications/send-notification).
+
+## Limits
+
+| Limit | Value |
+|-------|-------|
+| Recipients per API call | 10,000 |
diff --git a/campaigns/webhooks.mdx b/campaigns/webhooks.mdx
new file mode 100644
index 000000000..416a12d03
--- /dev/null
+++ b/campaigns/webhooks.mdx
@@ -0,0 +1,13 @@
+---
+title: "Webhooks"
+sidebarTitle: "Webhooks"
+description: "Track campaign delivery and engagement in real-time with webhook events."
+---
+
+Campaign webhook events are triggered during the lifecycle of campaigns, notifications, feed items, and push notifications. These events allow you to track delivery and engagement in real-time.
+
+Webhook configuration and event payloads are documented in the main Webhooks section.
+
+
+ View webhook setup, trigger list, and payload examples
+
diff --git a/docs.json b/docs.json
index 172825868..33faee4bc 100644
--- a/docs.json
+++ b/docs.json
@@ -33,7 +33,8 @@
"management-apis.json",
"data-import-apis.json",
"calls.json",
- "ai-agent-service.json"
+ "ai-agent-service.json",
+ "campaigns-apis.json"
],
"navigation": {
"products": [
@@ -509,7 +510,8 @@
"ui-kit/react/ai-features"
]
},
- "ui-kit/react/call-features"
+ "ui-kit/react/call-features",
+ "ui-kit/react/campaigns"
]
},
{
@@ -541,7 +543,8 @@
"ui-kit/react/call-buttons",
"ui-kit/react/call-logs",
"ui-kit/react/search",
- "ui-kit/react/ai-assistant-chat"
+ "ui-kit/react/ai-assistant-chat",
+ "ui-kit/react/notification-feed"
]
},
{
@@ -956,7 +959,8 @@
"ui-kit/react-native/extensions"
]
},
- "ui-kit/react-native/call-features"
+ "ui-kit/react-native/call-features",
+ "ui-kit/react-native/campaigns"
]
},
{
@@ -987,7 +991,8 @@
"ui-kit/react-native/outgoing-call",
"ui-kit/react-native/call-buttons",
"ui-kit/react-native/call-logs",
- "ui-kit/react-native/ai-assistant-chat-history"
+ "ui-kit/react-native/ai-assistant-chat-history",
+ "ui-kit/react-native/notification-feed"
]
},
{
@@ -1268,7 +1273,8 @@
"ui-kit/ios/ai-features"
]
},
- "ui-kit/ios/call-features"
+ "ui-kit/ios/call-features",
+ "ui-kit/ios/campaigns"
]
},
{
@@ -1301,7 +1307,8 @@
"ui-kit/ios/call-buttons",
"ui-kit/ios/call-logs",
"ui-kit/ios/ai-assistant-chat-history",
- "ui-kit/ios/search"
+ "ui-kit/ios/search",
+ "ui-kit/ios/notification-feed"
]
},
{
@@ -1590,7 +1597,8 @@
"ui-kit/android/v6/ai-features"
]
},
- "ui-kit/android/v6/call-features"
+ "ui-kit/android/v6/call-features",
+ "ui-kit/android/v6/campaigns"
]
},
{
@@ -1636,7 +1644,8 @@
"ui-kit/android/v6/call-buttons",
"ui-kit/android/v6/call-logs",
"ui-kit/android/v6/search",
- "ui-kit/android/v6/ai-assistant-chat-history"
+ "ui-kit/android/v6/ai-assistant-chat-history",
+ "ui-kit/android/v6/notification-feed"
]
},
{
@@ -2032,7 +2041,8 @@
"ui-kit/flutter/extensions"
]
},
- "ui-kit/flutter/call-features"
+ "ui-kit/flutter/call-features",
+ "ui-kit/flutter/campaigns"
]
},
{
@@ -2062,7 +2072,8 @@
"ui-kit/flutter/outgoing-call",
"ui-kit/flutter/call-buttons",
"ui-kit/flutter/call-logs",
- "ui-kit/flutter/search"
+ "ui-kit/flutter/search",
+ "ui-kit/flutter/notification-feed"
]
},
{
@@ -3057,6 +3068,7 @@
"sdk/javascript/ai-moderation",
"sdk/javascript/ai-agents",
"sdk/javascript/ai-copilot",
+ "sdk/javascript/campaigns",
"sdk/javascript/webhooks",
{
"group": "Resources",
@@ -3375,6 +3387,7 @@
},
"sdk/react-native/ai-moderation",
"sdk/react-native/ai-agents",
+ "sdk/react-native/campaigns",
{
"group": "Resources",
"pages": [
@@ -3680,6 +3693,7 @@
},
"sdk/ios/ai-moderation",
"sdk/ios/ai-agents",
+ "sdk/ios/campaigns",
{
"group": "Resources",
"pages": [
@@ -4009,6 +4023,7 @@
},
"sdk/android/v5/ai-moderation",
"sdk/android/v5/ai-agents",
+ "sdk/android/v5/campaigns",
{
"group": "Resources",
"pages": [
@@ -4418,6 +4433,7 @@
},
"sdk/flutter/ai-moderation",
"sdk/flutter/ai-agents",
+ "sdk/flutter/campaigns",
{
"group": "Resources",
"pages": [
@@ -6494,6 +6510,162 @@
}
]
},
+ {
+ "product": "Campaigns",
+ "tabs": [
+ {
+ "tab": "Campaigns",
+ "pages": [
+ "campaigns",
+ {
+ "group": "Guides",
+ "icon": "grid-2",
+ "pages": [
+ "campaigns/users",
+ "campaigns/channels",
+ "campaigns/templates",
+ "campaigns/campaigns",
+ "campaigns/categories",
+ "campaigns/notification-settings"
+ ]
+ },
+ {
+ "group": "Features",
+ "icon": "puzzle-piece",
+ "pages": [
+ "campaigns/sequences",
+ "campaigns/analytics"
+ ]
+ }
+ ]
+ },
+ {
+ "tab": "APIs",
+ "groups": [
+ {
+ "group": "Campaigns API",
+ "pages": [
+ "rest-api/campaigns-apis/overview",
+ "rest-api/campaigns-apis/setup-and-authentication",
+ {
+ "group": "Notifications",
+ "expanded": false,
+ "icon": "paper-plane",
+ "pages": [
+ "rest-api/campaigns-apis/notifications/send-notification"
+ ]
+ },
+ {
+ "group": "Campaigns",
+ "expanded": false,
+ "icon": "bullhorn",
+ "pages": [
+ "rest-api/campaigns-apis/campaigns/create-campaign",
+ "rest-api/campaigns-apis/campaigns/list-campaigns",
+ "rest-api/campaigns-apis/campaigns/get-campaign",
+ "rest-api/campaigns-apis/campaigns/update-campaign",
+ "rest-api/campaigns-apis/campaigns/delete-campaign",
+ "rest-api/campaigns-apis/campaigns/add-recipients",
+ "rest-api/campaigns-apis/campaigns/list-recipients",
+ "rest-api/campaigns-apis/campaigns/recipient-summary",
+ "rest-api/campaigns-apis/campaigns/upload-url",
+ "rest-api/campaigns-apis/campaigns/import-csv",
+ "rest-api/campaigns-apis/campaigns/import-status",
+ "rest-api/campaigns-apis/campaigns/send-campaign",
+ "rest-api/campaigns-apis/campaigns/schedule-campaign",
+ "rest-api/campaigns-apis/campaigns/cancel-campaign"
+ ]
+ },
+ {
+ "group": "Templates",
+ "expanded": false,
+ "icon": "file-lines",
+ "pages": [
+ "rest-api/campaigns-apis/templates/create-template",
+ "rest-api/campaigns-apis/templates/list-templates",
+ "rest-api/campaigns-apis/templates/get-template",
+ "rest-api/campaigns-apis/templates/update-template",
+ "rest-api/campaigns-apis/templates/archive-template",
+ "rest-api/campaigns-apis/templates/update-channel-content",
+ "rest-api/campaigns-apis/templates/create-version"
+ ]
+ },
+ {
+ "group": "Template Categories",
+ "expanded": false,
+ "icon": "folder",
+ "pages": [
+ "rest-api/campaigns-apis/categories/create-category",
+ "rest-api/campaigns-apis/categories/list-categories",
+ "rest-api/campaigns-apis/categories/get-category",
+ "rest-api/campaigns-apis/categories/update-category",
+ "rest-api/campaigns-apis/categories/delete-category"
+ ]
+ },
+ {
+ "group": "Channels",
+ "expanded": false,
+ "icon": "tower-broadcast",
+ "pages": [
+ "rest-api/campaigns-apis/channels/create-channel",
+ "rest-api/campaigns-apis/channels/list-channels",
+ "rest-api/campaigns-apis/channels/get-channel",
+ "rest-api/campaigns-apis/channels/update-channel",
+ "rest-api/campaigns-apis/channels/check-availability"
+ ]
+ },
+ {
+ "group": "Notification Feed",
+ "expanded": false,
+ "icon": "bell",
+ "pages": [
+ "rest-api/campaigns-apis/notification-feed/list-feed",
+ "rest-api/campaigns-apis/notification-feed/get-feed-item",
+ "rest-api/campaigns-apis/notification-feed/get-unread-count",
+ "rest-api/campaigns-apis/notification-feed/mark-as-delivered",
+ "rest-api/campaigns-apis/notification-feed/mark-as-read",
+ "rest-api/campaigns-apis/notification-feed/report-engagement",
+ "rest-api/campaigns-apis/notification-feed/delete-feed-item"
+ ]
+ },
+ {
+ "group": "Push Notifications",
+ "expanded": false,
+ "icon": "mobile",
+ "pages": [
+ "rest-api/campaigns-apis/push-notifications/list-push-notifications",
+ "rest-api/campaigns-apis/push-notifications/get-push-notification",
+ "rest-api/campaigns-apis/push-notifications/mark-delivered",
+ "rest-api/campaigns-apis/push-notifications/mark-clicked",
+ "rest-api/campaigns-apis/push-notifications/report-engagement"
+ ]
+ },
+ {
+ "group": "Sequences",
+ "expanded": false,
+ "icon": "arrow-progress",
+ "pages": [
+ "rest-api/campaigns-apis/sequences/sequence-metrics"
+ ]
+ },
+ {
+ "group": "Analytics",
+ "expanded": false,
+ "icon": "chart-line",
+ "pages": [
+ "rest-api/campaigns-apis/analytics/overview-metrics",
+ "rest-api/campaigns-apis/analytics/campaign-metrics",
+ "rest-api/campaigns-apis/analytics/template-metrics",
+ "rest-api/campaigns-apis/analytics/channel-metrics",
+ "rest-api/campaigns-apis/analytics/user-metrics"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
{
"product": "Insights",
"tabs": [
@@ -9683,4 +9855,4 @@
"redirect": true
}
}
-}
+}
\ No newline at end of file
diff --git a/images/BubbleBuilder.png b/images/BubbleBuilder.png
new file mode 100644
index 000000000..65e01d45f
Binary files /dev/null and b/images/BubbleBuilder.png differ
diff --git a/images/Campaigns.png b/images/Campaigns.png
new file mode 100644
index 000000000..6877cfce3
Binary files /dev/null and b/images/Campaigns.png differ
diff --git a/images/Create-Chan.png b/images/Create-Chan.png
new file mode 100644
index 000000000..90aa852ce
Binary files /dev/null and b/images/Create-Chan.png differ
diff --git a/images/Create-Temp.png b/images/Create-Temp.png
new file mode 100644
index 000000000..b3dab20ca
Binary files /dev/null and b/images/Create-Temp.png differ
diff --git a/images/CreateCategory.png b/images/CreateCategory.png
new file mode 100644
index 000000000..60df6af3a
Binary files /dev/null and b/images/CreateCategory.png differ
diff --git a/images/announcement.png b/images/announcement.png
new file mode 100644
index 000000000..db37af243
Binary files /dev/null and b/images/announcement.png differ
diff --git a/images/campaigns-overview-web.png b/images/campaigns-overview-web.png
new file mode 100644
index 000000000..24a418c9a
Binary files /dev/null and b/images/campaigns-overview-web.png differ
diff --git a/images/campaigns-overview.png b/images/campaigns-overview.png
new file mode 100644
index 000000000..6877cfce3
Binary files /dev/null and b/images/campaigns-overview.png differ
diff --git a/images/create-Camp.png b/images/create-Camp.png
new file mode 100644
index 000000000..79d1a73d6
Binary files /dev/null and b/images/create-Camp.png differ
diff --git a/images/create-bubble.png b/images/create-bubble.png
new file mode 100644
index 000000000..65e01d45f
Binary files /dev/null and b/images/create-bubble.png differ
diff --git a/images/create-campaign.png b/images/create-campaign.png
new file mode 100644
index 000000000..79d1a73d6
Binary files /dev/null and b/images/create-campaign.png differ
diff --git a/images/create-category.png b/images/create-category.png
new file mode 100644
index 000000000..60df6af3a
Binary files /dev/null and b/images/create-category.png differ
diff --git a/images/create-channel.png b/images/create-channel.png
new file mode 100644
index 000000000..90aa852ce
Binary files /dev/null and b/images/create-channel.png differ
diff --git a/images/create-template.png b/images/create-template.png
new file mode 100644
index 000000000..b3dab20ca
Binary files /dev/null and b/images/create-template.png differ
diff --git a/index.mdx b/index.mdx
index b161b7282..0f73e9119 100644
--- a/index.mdx
+++ b/index.mdx
@@ -52,6 +52,15 @@ canonical: "https://cometchat.com/docs"
Automate conversations using AI-powered chatbot technology.
+}
+ iconType="solid"
+ href="/campaigns"
+>
+ Send transactional notifications and run campaigns with an in-app feed and push.
+
+
diff --git a/rest-api/campaigns-apis/analytics/campaign-metrics.mdx b/rest-api/campaigns-apis/analytics/campaign-metrics.mdx
new file mode 100644
index 000000000..bc27d5982
--- /dev/null
+++ b/rest-api/campaigns-apis/analytics/campaign-metrics.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /analytics/campaigns/{campaignId}
+description: "Get campaign analytics drill-down."
+---
diff --git a/rest-api/campaigns-apis/analytics/channel-metrics.mdx b/rest-api/campaigns-apis/analytics/channel-metrics.mdx
new file mode 100644
index 000000000..e958fa6d2
--- /dev/null
+++ b/rest-api/campaigns-apis/analytics/channel-metrics.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /analytics/channels
+description: "Get channel breakdown analytics."
+---
diff --git a/rest-api/campaigns-apis/analytics/overview-metrics.mdx b/rest-api/campaigns-apis/analytics/overview-metrics.mdx
new file mode 100644
index 000000000..11c336895
--- /dev/null
+++ b/rest-api/campaigns-apis/analytics/overview-metrics.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /analytics/overview
+description: "Get aggregated analytics overview."
+---
diff --git a/rest-api/campaigns-apis/analytics/template-metrics.mdx b/rest-api/campaigns-apis/analytics/template-metrics.mdx
new file mode 100644
index 000000000..0bd86b05f
--- /dev/null
+++ b/rest-api/campaigns-apis/analytics/template-metrics.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /analytics/templates/{templateId}
+description: "Get template analytics drill-down."
+---
diff --git a/rest-api/campaigns-apis/analytics/user-metrics.mdx b/rest-api/campaigns-apis/analytics/user-metrics.mdx
new file mode 100644
index 000000000..8041d8a93
--- /dev/null
+++ b/rest-api/campaigns-apis/analytics/user-metrics.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /analytics/users/{userId}
+description: "Get user-level engagement insights."
+---
diff --git a/rest-api/campaigns-apis/campaigns/add-recipients.mdx b/rest-api/campaigns-apis/campaigns/add-recipients.mdx
new file mode 100644
index 000000000..1081eb56a
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/add-recipients.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /campaigns/{id}/recipients
+description: "Add recipients manually."
+---
diff --git a/rest-api/campaigns-apis/campaigns/cancel-campaign.mdx b/rest-api/campaigns-apis/campaigns/cancel-campaign.mdx
new file mode 100644
index 000000000..19cf72e94
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/cancel-campaign.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /campaigns/{id}/cancel
+description: "Cancel a campaign."
+---
diff --git a/rest-api/campaigns-apis/campaigns/create-campaign.mdx b/rest-api/campaigns-apis/campaigns/create-campaign.mdx
new file mode 100644
index 000000000..2a1c92ea5
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/create-campaign.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /campaigns
+description: "Create a new campaign."
+---
diff --git a/rest-api/campaigns-apis/campaigns/delete-campaign.mdx b/rest-api/campaigns-apis/campaigns/delete-campaign.mdx
new file mode 100644
index 000000000..bf1a66313
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/delete-campaign.mdx
@@ -0,0 +1,4 @@
+---
+openapi: delete /campaigns/{id}
+description: "Delete a campaign (draft only)."
+---
diff --git a/rest-api/campaigns-apis/campaigns/get-campaign.mdx b/rest-api/campaigns-apis/campaigns/get-campaign.mdx
new file mode 100644
index 000000000..38c3631c3
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/get-campaign.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /campaigns/{id}
+description: "Get a campaign by ID."
+---
diff --git a/rest-api/campaigns-apis/campaigns/import-csv.mdx b/rest-api/campaigns-apis/campaigns/import-csv.mdx
new file mode 100644
index 000000000..03756099e
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/import-csv.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /campaigns/{id}/import-csv
+description: "Start async CSV import for campaign recipients."
+---
diff --git a/rest-api/campaigns-apis/campaigns/import-status.mdx b/rest-api/campaigns-apis/campaigns/import-status.mdx
new file mode 100644
index 000000000..b26d2360d
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/import-status.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /campaigns/{id}/import-status
+description: "Get current import job status."
+---
diff --git a/rest-api/campaigns-apis/campaigns/list-campaigns.mdx b/rest-api/campaigns-apis/campaigns/list-campaigns.mdx
new file mode 100644
index 000000000..d15792d82
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/list-campaigns.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /campaigns
+description: "List campaigns."
+---
diff --git a/rest-api/campaigns-apis/campaigns/list-recipients.mdx b/rest-api/campaigns-apis/campaigns/list-recipients.mdx
new file mode 100644
index 000000000..649072fb3
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/list-recipients.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /campaigns/{id}/recipients
+description: "List campaign recipients."
+---
diff --git a/rest-api/campaigns-apis/campaigns/recipient-summary.mdx b/rest-api/campaigns-apis/campaigns/recipient-summary.mdx
new file mode 100644
index 000000000..1672640cd
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/recipient-summary.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /campaigns/{id}/recipients/summary
+description: "Get recipient status summary."
+---
diff --git a/rest-api/campaigns-apis/campaigns/schedule-campaign.mdx b/rest-api/campaigns-apis/campaigns/schedule-campaign.mdx
new file mode 100644
index 000000000..25dcc0c04
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/schedule-campaign.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /campaigns/{id}/schedule
+description: "Schedule campaign send."
+---
diff --git a/rest-api/campaigns-apis/campaigns/send-campaign.mdx b/rest-api/campaigns-apis/campaigns/send-campaign.mdx
new file mode 100644
index 000000000..4e23e915a
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/send-campaign.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /campaigns/{id}/send
+description: "Trigger campaign send."
+---
diff --git a/rest-api/campaigns-apis/campaigns/update-campaign.mdx b/rest-api/campaigns-apis/campaigns/update-campaign.mdx
new file mode 100644
index 000000000..45b0f14d7
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/update-campaign.mdx
@@ -0,0 +1,4 @@
+---
+openapi: put /campaigns/{id}
+description: "Update a campaign (draft only)."
+---
diff --git a/rest-api/campaigns-apis/campaigns/upload-url.mdx b/rest-api/campaigns-apis/campaigns/upload-url.mdx
new file mode 100644
index 000000000..23490a75f
--- /dev/null
+++ b/rest-api/campaigns-apis/campaigns/upload-url.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /campaigns/{id}/recipients/upload-url
+description: "Get presigned URL for CSV upload."
+---
diff --git a/rest-api/campaigns-apis/categories/create-category.mdx b/rest-api/campaigns-apis/categories/create-category.mdx
new file mode 100644
index 000000000..69ac7e006
--- /dev/null
+++ b/rest-api/campaigns-apis/categories/create-category.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /templates/categories
+description: "Create a new template category."
+---
diff --git a/rest-api/campaigns-apis/categories/delete-category.mdx b/rest-api/campaigns-apis/categories/delete-category.mdx
new file mode 100644
index 000000000..96fc14014
--- /dev/null
+++ b/rest-api/campaigns-apis/categories/delete-category.mdx
@@ -0,0 +1,4 @@
+---
+openapi: delete /templates/categories/{id}
+description: "Delete a template category."
+---
diff --git a/rest-api/campaigns-apis/categories/get-category.mdx b/rest-api/campaigns-apis/categories/get-category.mdx
new file mode 100644
index 000000000..40dafc476
--- /dev/null
+++ b/rest-api/campaigns-apis/categories/get-category.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /templates/categories/{id}
+description: "Get a template category by ID."
+---
diff --git a/rest-api/campaigns-apis/categories/list-categories.mdx b/rest-api/campaigns-apis/categories/list-categories.mdx
new file mode 100644
index 000000000..e19db0bd5
--- /dev/null
+++ b/rest-api/campaigns-apis/categories/list-categories.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /templates/categories
+description: "List all template categories."
+---
diff --git a/rest-api/campaigns-apis/categories/update-category.mdx b/rest-api/campaigns-apis/categories/update-category.mdx
new file mode 100644
index 000000000..fae2fc23e
--- /dev/null
+++ b/rest-api/campaigns-apis/categories/update-category.mdx
@@ -0,0 +1,4 @@
+---
+openapi: put /templates/categories/{id}
+description: "Update a template category."
+---
diff --git a/rest-api/campaigns-apis/channels/check-availability.mdx b/rest-api/campaigns-apis/channels/check-availability.mdx
new file mode 100644
index 000000000..c800ba034
--- /dev/null
+++ b/rest-api/campaigns-apis/channels/check-availability.mdx
@@ -0,0 +1,8 @@
+---
+openapi: get /channels/availability
+description: "Return per-type channel counts and configured limits."
+---
+
+Used by the Dashboard to gate channel creation against per-type quotas.
+
+For the complete error reference, see [Error Guide](/articles/error-guide).
diff --git a/rest-api/campaigns-apis/channels/create-channel.mdx b/rest-api/campaigns-apis/channels/create-channel.mdx
new file mode 100644
index 000000000..62fafe298
--- /dev/null
+++ b/rest-api/campaigns-apis/channels/create-channel.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /channels
+description: "Create a new channel."
+---
diff --git a/rest-api/campaigns-apis/channels/get-channel.mdx b/rest-api/campaigns-apis/channels/get-channel.mdx
new file mode 100644
index 000000000..9f8e35585
--- /dev/null
+++ b/rest-api/campaigns-apis/channels/get-channel.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /channels/{id}
+description: "Get a channel by ID."
+---
diff --git a/rest-api/campaigns-apis/channels/list-channels.mdx b/rest-api/campaigns-apis/channels/list-channels.mdx
new file mode 100644
index 000000000..db26ca1ab
--- /dev/null
+++ b/rest-api/campaigns-apis/channels/list-channels.mdx
@@ -0,0 +1,8 @@
+---
+openapi: get /channels
+description: "List channel instances configured for this app."
+---
+
+Paginated. Filter by `type` or `enabled` to narrow the result set.
+
+For the complete error reference, see [Error Guide](/articles/error-guide).
diff --git a/rest-api/campaigns-apis/channels/update-channel.mdx b/rest-api/campaigns-apis/channels/update-channel.mdx
new file mode 100644
index 000000000..93bf8cb58
--- /dev/null
+++ b/rest-api/campaigns-apis/channels/update-channel.mdx
@@ -0,0 +1,4 @@
+---
+openapi: put /channels/{id}
+description: "Update a channel."
+---
diff --git a/rest-api/campaigns-apis/notification-feed/delete-feed-item.mdx b/rest-api/campaigns-apis/notification-feed/delete-feed-item.mdx
new file mode 100644
index 000000000..963145519
--- /dev/null
+++ b/rest-api/campaigns-apis/notification-feed/delete-feed-item.mdx
@@ -0,0 +1,8 @@
+---
+openapi: delete /notification-feed/{id}
+description: "Soft-delete a feed item."
+---
+
+Sets `deletedAt`. The row remains until the retention purge hard-deletes it.
+
+For the complete error reference, see [Error Guide](/articles/error-guide).
diff --git a/rest-api/campaigns-apis/notification-feed/get-feed-item.mdx b/rest-api/campaigns-apis/notification-feed/get-feed-item.mdx
new file mode 100644
index 000000000..9c57da9ee
--- /dev/null
+++ b/rest-api/campaigns-apis/notification-feed/get-feed-item.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /notification-feed/{id}
+description: "Get a feed item by ID."
+---
diff --git a/rest-api/campaigns-apis/notification-feed/get-unread-count.mdx b/rest-api/campaigns-apis/notification-feed/get-unread-count.mdx
new file mode 100644
index 000000000..3684db9f0
--- /dev/null
+++ b/rest-api/campaigns-apis/notification-feed/get-unread-count.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /notification-feed/unread-count
+description: "Get unread count for the requesting user."
+---
diff --git a/rest-api/campaigns-apis/notification-feed/list-feed.mdx b/rest-api/campaigns-apis/notification-feed/list-feed.mdx
new file mode 100644
index 000000000..436a8e0ff
--- /dev/null
+++ b/rest-api/campaigns-apis/notification-feed/list-feed.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /notification-feed
+description: "Query feed with filters and cursor pagination."
+---
diff --git a/rest-api/campaigns-apis/notification-feed/mark-as-delivered.mdx b/rest-api/campaigns-apis/notification-feed/mark-as-delivered.mdx
new file mode 100644
index 000000000..a71be5648
--- /dev/null
+++ b/rest-api/campaigns-apis/notification-feed/mark-as-delivered.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /notification-feed/{id}/delivered
+description: "Mark a feed item as delivered."
+---
diff --git a/rest-api/campaigns-apis/notification-feed/mark-as-read.mdx b/rest-api/campaigns-apis/notification-feed/mark-as-read.mdx
new file mode 100644
index 000000000..444acd3ee
--- /dev/null
+++ b/rest-api/campaigns-apis/notification-feed/mark-as-read.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /notification-feed/{id}/read
+description: "Mark a feed item as read."
+---
diff --git a/rest-api/campaigns-apis/notification-feed/report-engagement.mdx b/rest-api/campaigns-apis/notification-feed/report-engagement.mdx
new file mode 100644
index 000000000..232848544
--- /dev/null
+++ b/rest-api/campaigns-apis/notification-feed/report-engagement.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /notification-feed/{id}/engagement
+description: "Report an interacted engagement event."
+---
diff --git a/rest-api/campaigns-apis/notifications/send-notification.mdx b/rest-api/campaigns-apis/notifications/send-notification.mdx
new file mode 100644
index 000000000..53c1d806f
--- /dev/null
+++ b/rest-api/campaigns-apis/notifications/send-notification.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /notifications/messages
+description: "Send a notification using a template."
+---
diff --git a/rest-api/campaigns-apis/overview.mdx b/rest-api/campaigns-apis/overview.mdx
new file mode 100644
index 000000000..fb2b225d9
--- /dev/null
+++ b/rest-api/campaigns-apis/overview.mdx
@@ -0,0 +1,98 @@
+---
+title: "Overview"
+description: "REST surface for the Campaigns product. Send transactional notifications, manage templates and channels, and administer the in-app feed."
+---
+
+The Campaigns REST API is the server-side surface of the product. It covers transactional sends, template and channel look-ups, and admin operations on the notification feed. All endpoints share a single response envelope, a single error envelope, and a single authentication model.
+
+### Base URL
+
+```
+https://{appId}.api-{region}.cometchat.io/v3/campaigns
+```
+
+Regions: `us`, `eu`, `in`. Replace `{appId}` with your application ID. See [Setup & Authentication](/rest-api/campaigns-apis/setup-and-authentication) for credentials and headers.
+
+### Response envelope
+
+#### Success
+
+Single-entity responses wrap the resource under `data`:
+
+```json
+{ "data": { "id": "feed-cl9xyz123", "deletedAt": "2026-05-04T10:35:00.000Z" } }
+```
+
+Paginated lists return an array under `data` and cursor metadata under `meta`:
+
+```json
+{
+ "data": [ /* items */ ],
+ "meta": {
+ "current": { "limit": 20, "count": 5 },
+ "next": { "affix": "append", "sentAt": 1730301000, "id": "feed-cl9xyz123" }
+ }
+}
+```
+
+Pagination is cursor-based. Pass `affix`, `sentAt` (or the configured sort field), and `id` from `meta.next` back into the next request to page forward.
+
+#### Error envelope (canonical)
+
+Every non-2xx response from the Campaigns service uses exactly this shape, wrapped under `error`:
+
+```json
+{
+ "error": {
+ "code": "ERR_FEED_ITEM_NOT_FOUND",
+ "message": "Feed item not found",
+ "devMessage": "Feed item not found: feed-cl9xyz123",
+ "details": { "feedItemId": "feed-cl9xyz123" },
+ "source": "campaigns-service"
+ }
+}
+```
+
+| Field | Convention |
+| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `code` | Always `ERR_`-prefixed, SCREAMING_SNAKE. Domain codes (`ERR_TEMPLATE_NOT_FOUND`) and framework codes (`ERR_VALIDATION`, `ERR_UNAUTHORIZED`) share the prefix. |
+| `message` | User-facing, identifier-free, safe to render in a UI. Never carries IDs or PII. |
+| `devMessage` | Developer-facing. Carries identifiers and extra context for debugging. Defaults to `message` when omitted. |
+| `details` | Structured drill-down. Object for domain errors (`{ "channelId": "feed-1" }`); array of strings for validation failures (one per failed field). |
+| `source` | Always `"campaigns-service"`. Lets gateway-fronted callers attribute failures when multiple services share the gateway. |
+
+Unhandled internal errors fail closed; no stack trace or internal detail leaks:
+
+```json
+{
+ "error": {
+ "code": "ERR_INTERNAL",
+ "message": "An unexpected error occurred.",
+ "devMessage": "An unexpected error occurred.",
+ "source": "campaigns-service"
+ }
+}
+```
+
+### Error code reference
+
+Non-exhaustive. These are the codes a typical caller will encounter.
+
+| Class | Codes |
+| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| Domain | `ERR_TEMPLATE_NOT_FOUND`, `ERR_CHANNEL_NOT_FOUND`, `ERR_FEED_ITEM_NOT_FOUND` |
+| Framework | `ERR_VALIDATION` (400), `ERR_UNAUTHORIZED` (401), `ERR_NOT_FOUND` (404), `ERR_CONFLICT` (409), `ERR_TOO_MANY_REQUESTS` (429), `ERR_BAD_REQUEST` (4xx fallback), `ERR_INTERNAL` (500) |
+
+### Variable resolution
+
+Within template content, variables are referenced with `{{variableKey}}` syntax (for example `Hi {{user_name}}, your order #{{order_id}} shipped`) and resolved at send-time.
+
+For templated sends and campaign deliveries, three sources contribute to the final value substituted into channel content. Higher rows win:
+
+| Priority | Source | Where it lives |
+| ----------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
+| 1 (highest) | Per-user variables | The `variables` map on a transactional send request, or per-row from a campaign's CSV import |
+| 2 | Campaign-level defaults | The campaign's `config.variables` |
+| 3 | Template variable-schema defaults | The template's `variableSchema[].default` field |
+
+If none of the three has a value, the token is left unresolved (`{{key}}`) in the rendered output.
diff --git a/rest-api/campaigns-apis/push-notifications/get-push-notification.mdx b/rest-api/campaigns-apis/push-notifications/get-push-notification.mdx
new file mode 100644
index 000000000..26602d559
--- /dev/null
+++ b/rest-api/campaigns-apis/push-notifications/get-push-notification.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /push-notifications/{id}
+description: "Get a push notification by ID."
+---
diff --git a/rest-api/campaigns-apis/push-notifications/list-push-notifications.mdx b/rest-api/campaigns-apis/push-notifications/list-push-notifications.mdx
new file mode 100644
index 000000000..6b0bced57
--- /dev/null
+++ b/rest-api/campaigns-apis/push-notifications/list-push-notifications.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /push-notifications
+description: "List push notifications for a user."
+---
diff --git a/rest-api/campaigns-apis/push-notifications/mark-clicked.mdx b/rest-api/campaigns-apis/push-notifications/mark-clicked.mdx
new file mode 100644
index 000000000..88bb87a68
--- /dev/null
+++ b/rest-api/campaigns-apis/push-notifications/mark-clicked.mdx
@@ -0,0 +1,4 @@
+---
+openapi: put /push-notifications/{id}/clicked
+description: "Mark push notification as clicked."
+---
diff --git a/rest-api/campaigns-apis/push-notifications/mark-delivered.mdx b/rest-api/campaigns-apis/push-notifications/mark-delivered.mdx
new file mode 100644
index 000000000..08ef35773
--- /dev/null
+++ b/rest-api/campaigns-apis/push-notifications/mark-delivered.mdx
@@ -0,0 +1,4 @@
+---
+openapi: put /push-notifications/{id}/delivered
+description: "Mark push notification as delivered."
+---
diff --git a/rest-api/campaigns-apis/push-notifications/report-engagement.mdx b/rest-api/campaigns-apis/push-notifications/report-engagement.mdx
new file mode 100644
index 000000000..5b3d9cd06
--- /dev/null
+++ b/rest-api/campaigns-apis/push-notifications/report-engagement.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /push-notifications/{id}/engagement
+description: "Report push notification engagement event."
+---
diff --git a/rest-api/campaigns-apis/sequences/sequence-metrics.mdx b/rest-api/campaigns-apis/sequences/sequence-metrics.mdx
new file mode 100644
index 000000000..c24c8029f
--- /dev/null
+++ b/rest-api/campaigns-apis/sequences/sequence-metrics.mdx
@@ -0,0 +1,4 @@
+---
+openapi: get /campaigns/{id}/sequence-metrics
+description: "Get per-step sequence delivery metrics for a campaign."
+---
diff --git a/rest-api/campaigns-apis/setup-and-authentication.mdx b/rest-api/campaigns-apis/setup-and-authentication.mdx
new file mode 100644
index 000000000..fdac1a4a3
--- /dev/null
+++ b/rest-api/campaigns-apis/setup-and-authentication.mdx
@@ -0,0 +1,58 @@
+---
+title: "Setup & Authentication"
+description: "Credentials, headers, and base URL for the Campaigns REST API."
+---
+
+The Campaigns REST API is a **server-side API**. It runs behind the CometChat Gateway, which authenticates and authorizes every request before forwarding it to the service. You authenticate by attaching a REST API key on every request. The service itself never validates tokens.
+
+### Server-only
+
+These endpoints are intended for your back-end, the CometChat Dashboard, and your ops tools. Do not call them from a browser or a mobile client. Shipping a REST API key to an end-user client would expose full admin access to your app.
+
+End-user clients (mobile apps, in-app bell widgets) should integrate with the Campaigns SDK, which is the client-safe consumer surface for feed reads, unread counts, and engagement tracking.
+
+### Base URL
+
+```
+https://{appId}.api-{region}.cometchat.io/v3/campaigns
+```
+
+Regions: `us`, `eu`, `in`. Replace `{appId}` with your application ID.
+
+### Credentials
+
+Generate a **REST API key** with `fullAccess` scope from your CometChat Dashboard. Send it on every request as the `apikey` header.
+
+```http
+apikey:
+```
+
+App scoping is encoded in the base URL (`https://{appId}.api-{region}.cometchat.io/v3/campaigns`), so you do not need a separate `appid` header.
+
+### Sample headers
+
+```http
+apikey:
+content-type: application/json
+```
+
+### Common auth errors
+
+```jsonc
+// 401. apikey missing or invalid
+{
+ "error": {
+ "code": "ERR_UNAUTHORIZED",
+ "message": "apikey header is required",
+ "source": "campaigns-service"
+ }
+}
+```
+
+### Multi-tenancy
+
+Every entity is scoped to the `{appId}` in the base URL. Two apps cannot read each other's feed items, templates, or channels. Don't reuse an API key across apps; generate one per app from the Dashboard.
+
+For the full surface and the canonical response and error envelopes, see [Overview](/rest-api/campaigns-apis/overview).
+
+For the shared CometChat authentication model (API key scopes, auth tokens, security best practices), see [Authentication](/rest-api/authentication).
diff --git a/rest-api/campaigns-apis/templates/archive-template.mdx b/rest-api/campaigns-apis/templates/archive-template.mdx
new file mode 100644
index 000000000..23367dfdd
--- /dev/null
+++ b/rest-api/campaigns-apis/templates/archive-template.mdx
@@ -0,0 +1,4 @@
+---
+openapi: delete /templates/{id}
+description: "Archive a template."
+---
diff --git a/rest-api/campaigns-apis/templates/create-template.mdx b/rest-api/campaigns-apis/templates/create-template.mdx
new file mode 100644
index 000000000..14d53bd77
--- /dev/null
+++ b/rest-api/campaigns-apis/templates/create-template.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /templates
+description: "Create a new template."
+---
diff --git a/rest-api/campaigns-apis/templates/create-version.mdx b/rest-api/campaigns-apis/templates/create-version.mdx
new file mode 100644
index 000000000..c4fd1b235
--- /dev/null
+++ b/rest-api/campaigns-apis/templates/create-version.mdx
@@ -0,0 +1,4 @@
+---
+openapi: post /templates/{id}/versions
+description: "Create a new template version."
+---
diff --git a/rest-api/campaigns-apis/templates/get-template.mdx b/rest-api/campaigns-apis/templates/get-template.mdx
new file mode 100644
index 000000000..03861bdeb
--- /dev/null
+++ b/rest-api/campaigns-apis/templates/get-template.mdx
@@ -0,0 +1,8 @@
+---
+openapi: get /templates/{id}
+description: "Fetch a single template by CUID or slug."
+---
+
+Returns all versions and the current version's channels and variable schema. Versions are immutable, so saving an edit auto-bumps the `currentVersion`.
+
+For the complete error reference, see [Error Guide](/articles/error-guide).
diff --git a/rest-api/campaigns-apis/templates/list-templates.mdx b/rest-api/campaigns-apis/templates/list-templates.mdx
new file mode 100644
index 000000000..33dd10f15
--- /dev/null
+++ b/rest-api/campaigns-apis/templates/list-templates.mdx
@@ -0,0 +1,8 @@
+---
+openapi: get /templates
+description: "List templates for this app."
+---
+
+Paginated. Combine `category`, `status`, `search`, and `tags` to narrow the result set.
+
+For the complete error reference, see [Error Guide](/articles/error-guide).
diff --git a/rest-api/campaigns-apis/templates/update-channel-content.mdx b/rest-api/campaigns-apis/templates/update-channel-content.mdx
new file mode 100644
index 000000000..dfcc62129
--- /dev/null
+++ b/rest-api/campaigns-apis/templates/update-channel-content.mdx
@@ -0,0 +1,4 @@
+---
+openapi: put /templates/{id}/channels/{channelType}
+description: "Update channel content for a template."
+---
diff --git a/rest-api/campaigns-apis/templates/update-template.mdx b/rest-api/campaigns-apis/templates/update-template.mdx
new file mode 100644
index 000000000..fc544048e
--- /dev/null
+++ b/rest-api/campaigns-apis/templates/update-template.mdx
@@ -0,0 +1,4 @@
+---
+openapi: put /templates/{id}
+description: "Update template metadata."
+---
diff --git a/sdk/android/v5/campaigns.mdx b/sdk/android/v5/campaigns.mdx
new file mode 100644
index 000000000..71fa9efd8
--- /dev/null
+++ b/sdk/android/v5/campaigns.mdx
@@ -0,0 +1,663 @@
+---
+title: "Campaigns"
+---
+
+CometChat Campaigns lets you deliver targeted, rich notifications to users via an in-app notification feed. Each notification is a **Card Schema JSON** — a structured layout rendered natively by the CometChat Cards library.
+
+The SDK provides APIs to fetch feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and retrieve unread counts.
+
+
+Before using the SDK, set up your channels, categories, templates, and campaigns in the CometChat Dashboard. See the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions.
+
+
+---
+
+## Key Concepts
+
+| Concept | Description |
+| --- | --- |
+| **NotificationFeedItem** | A single notification in the feed. Contains Card Schema JSON in its `content` field, a `category` for filtering, timestamps, and metadata. |
+| **NotificationCategory** | A category label used for filter chips (e.g., "Promotions", "Updates"). |
+| **Card Schema JSON** | The fully rendered card layout (images, text, buttons) inside `NotificationFeedItem.getContent()`. Passed directly to the CometChat Cards renderer. |
+| **PushNotification** | Represents a campaign push notification payload received via FCM. |
+
+---
+
+## Rendering Cards
+
+The `content` field of each `NotificationFeedItem` is a Card Schema JSON object. To render it natively, use the CometChat Cards library.
+
+### Add the Cards Dependency
+
+Add the Cloudsmith repository and the cards library to your project:
+
+```groovy
+// settings.gradle or project-level build.gradle
+repositories {
+ maven { url "https://dl.cloudsmith.io/public/cometchat/cometchat/maven/" }
+}
+```
+
+```groovy
+// app/build.gradle
+dependencies {
+ implementation "com.cometchat:cards-android:1.0.0"
+}
+```
+
+
+Requires `minSdk 24`, Kotlin, and internet permission in your AndroidManifest.xml.
+
+
+### Render a Card from a Feed Item
+
+
+
+```kotlin
+import com.cometchat.cards.CometChatCardComposable
+import com.cometchat.cards.core.CometChatCardThemeMode
+
+@Composable
+fun NotificationCard(item: NotificationFeedItem) {
+ CometChatCardComposable(
+ cardJson = item.content.toString(),
+ themeMode = CometChatCardThemeMode.AUTO,
+ onAction = { event ->
+ when (event.action) {
+ is CometChatCardOpenUrlAction -> {
+ // Open URL in browser
+ }
+ is CometChatCardChatWithUserAction -> {
+ // Navigate to chat
+ }
+ }
+ }
+ )
+}
+```
+
+
+```kotlin
+import com.cometchat.cards.CometChatCardView
+import com.cometchat.cards.core.CometChatCardThemeMode
+
+val cardView = CometChatCardView(context)
+cardView.setCardSchema(item.content.toString())
+cardView.setThemeMode(CometChatCardThemeMode.AUTO)
+cardView.setActionCallback { event ->
+ // Handle action: event.action, event.elementId
+}
+parentLayout.addView(cardView)
+```
+
+
+
+
+The Cards library is a pure renderer — it does not execute actions. Your code must handle action callbacks (opening URLs, navigating to chats, making API calls, etc.).
+
+
+---
+
+## How Cards Render in the Notification Feed
+
+Each `NotificationFeedItem` has a `content` field containing a `JSONObject` — this is the **Card Schema JSON**. This JSON is passed directly to the **CometChat Cards** renderer library (`com.cometchat:cards-android`).
+
+The rendering flow:
+
+1. Fetch feed items via `NotificationFeedRequest`
+2. For each item, extract `item.getContent()` — this is the Card Schema JSON
+3. Convert to string: `item.getContent().toString()`
+4. Pass to the Cards renderer (`CometChatCardView` or `CometChatCardComposable`)
+5. The renderer produces a native Android view from the JSON
+
+### Card Schema JSON Structure
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ {
+ "type": "column",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "gap": 5,
+ "items": [
+ {
+ "type": "text",
+ "content": "📢 Announcement",
+ "variant": "heading2",
+ "id": "txt_99323141-2459-4e33-88d3-ca39c5fd2f50"
+ },
+ {
+ "type": "text",
+ "content": "Your announcement message here.",
+ "variant": "body",
+ "id": "txt_61a417bc-5e4a-4ba2-bfe7-b7bc64dbaf35"
+ },
+ {
+ "type": "divider",
+ "id": "div_80f5c7fb-fd10-41d1-8c2f-51498f0f62d0"
+ },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "textColor": {
+ "light": "#6C5CE7",
+ "dark": "#6C5CE7"
+ },
+ "size": 40,
+ "fontSize": 13,
+ "borderRadius": 6,
+ "padding": {
+ "top": 0,
+ "right": 16,
+ "bottom": 0,
+ "left": 16
+ },
+ "action": {
+ "type": "openUrl",
+ "url": ""
+ },
+ "id": "btn_9b87a3f1-b0c6-45b9-a4c2-e22ea590f17f"
+ }
+ ],
+ "id": "col_98fed9bd-1a95-4cee-aa81-84a9016e41f2"
+ }
+ ],
+ "fallbackText": "",
+ "style": {
+ "background": {
+ "light": "#E8E8E8",
+ "dark": "#E8E8E8"
+ },
+ "borderRadius": 16,
+ "borderColor": {
+ "light": "#DFE6E9",
+ "dark": "#DFE6E9"
+ },
+ "padding": 12
+ }
+}
+```
+
+The `body` array contains elements (text, image, button, row, column, etc.) rendered top-to-bottom. Interactive elements like buttons emit actions via a callback — the consumer handles navigation, deep links, or API calls.
+
+---
+
+## Retrieve Notification Feed Items
+
+Use `NotificationFeedRequest` to fetch a paginated list of feed items. Uses cursor-based pagination internally.
+
+### Build the Request
+
+
+
+```java
+NotificationFeedRequest request = new NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .setLimit(20)
+ .build();
+```
+
+
+```kotlin
+val request = NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .setLimit(20)
+ .build()
+```
+
+
+
+### Builder Parameters
+
+| Method | Type | Default | Description |
+| --- | --- | --- | --- |
+| `setLimit(int)` | int | 20 | Items per page (max 100) |
+| `setReadState(FeedReadState)` | enum | `ALL` | Filter by `READ`, `UNREAD`, or `ALL` |
+| `setCategory(String)` | String | null | Filter by category ID |
+| `setChannelId(String)` | String | null | Filter by channel |
+| `setTags(List)` | List | null | Filter by tags |
+| `setDateFrom(String)` | String | null | ISO 8601 date — items sent on or after |
+| `setDateTo(String)` | String | null | ISO 8601 date — items sent on or before |
+
+### Fetch Items
+
+
+
+```java
+request.fetchNext(new CometChat.CallbackListener>() {
+ @Override
+ public void onSuccess(List items) {
+ for (NotificationFeedItem item : items) {
+ String cardJson = item.getContent().toString();
+ // Pass cardJson to CometChatCardView or CometChatCardComposable
+ }
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e("Feed", "Error: " + e.getMessage());
+ }
+});
+```
+
+
+```kotlin
+request.fetchNext(object : CometChat.CallbackListener>() {
+ override fun onSuccess(items: List) {
+ items.forEach { item ->
+ val cardJson = item.content.toString()
+ // Pass cardJson to CometChatCardView or CometChatCardComposable
+ }
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e("Feed", "Error: ${e.message}")
+ }
+})
+```
+
+
+
+Call `fetchNext()` repeatedly for pagination. When the server has no more items, subsequent calls return an empty list.
+
+### NotificationFeedItem Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `id` | String | Unique item identifier |
+| `category` | String | Notification category (e.g., "promotions") |
+| `content` | JSONObject | Card Schema JSON — pass to CometChat Cards renderer |
+| `readAt` | Long? | Unix timestamp when read, or null if unread |
+| `deliveredAt` | Long? | Unix timestamp when delivered, or null |
+| `sentAt` | long | Unix timestamp when sent |
+| `metadata` | HashMap | Custom key-value metadata |
+| `tags` | List\ | Tags for filtering |
+| `receiver` | String | Receiver identifier |
+
+---
+
+## Retrieve Notification Categories
+
+Use `NotificationCategoriesRequest` to fetch available categories for filter chips.
+
+
+
+```java
+NotificationCategoriesRequest categoriesRequest = new NotificationCategoriesRequest
+ .NotificationCategoriesRequestBuilder()
+ .setLimit(50)
+ .build();
+
+categoriesRequest.fetchNext(new CometChat.CallbackListener>() {
+ @Override
+ public void onSuccess(List categories) {
+ for (NotificationCategory category : categories) {
+ Log.d("Feed", "Category: " + category.getName());
+ }
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e("Feed", "Error: " + e.getMessage());
+ }
+});
+```
+
+
+```kotlin
+val categoriesRequest = NotificationCategoriesRequest.NotificationCategoriesRequestBuilder()
+ .setLimit(50)
+ .build()
+
+categoriesRequest.fetchNext(object : CometChat.CallbackListener>() {
+ override fun onSuccess(categories: List) {
+ categories.forEach { category ->
+ Log.d("Feed", "Category: ${category.name}")
+ }
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e("Feed", "Error: ${e.message}")
+ }
+})
+```
+
+
+
+### NotificationCategory Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `id` | String | Category identifier |
+| `name` | String | Display name for filter UI |
+| `description` | String | Category description |
+| `appId` | String | Associated app ID |
+
+---
+
+## Real-Time Notification Feed Listener
+
+Listen for new feed items arriving via WebSocket. This listener is independent from `MessageListener`, `GroupListener`, and `CallListener`.
+
+
+
+```java
+CometChat.addNotificationFeedListener("feedListener", new NotificationFeedListener() {
+ @Override
+ public void onFeedItemReceived(NotificationFeedItem feedItem) {
+ Log.d("Feed", "New item: " + feedItem.getId());
+ String cardJson = feedItem.getContent().toString();
+ // Insert at top of feed and render
+ }
+});
+```
+
+
+```kotlin
+CometChat.addNotificationFeedListener("feedListener", object : NotificationFeedListener() {
+ override fun onFeedItemReceived(feedItem: NotificationFeedItem) {
+ Log.d("Feed", "New item: ${feedItem.id}")
+ val cardJson = feedItem.content.toString()
+ // Insert at top of feed and render
+ }
+})
+```
+
+
+
+Remove the listener when no longer needed:
+
+
+
+```java
+CometChat.removeNotificationFeedListener("feedListener");
+```
+
+
+```kotlin
+CometChat.removeNotificationFeedListener("feedListener")
+```
+
+
+
+---
+
+## Mark Feed Item as Read
+
+Mark a single item as read. Idempotent — safe to call multiple times.
+
+
+
+```java
+CometChat.markFeedItemAsRead(feedItem, new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(Void unused) {
+ Log.d("Feed", "Marked as read");
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e("Feed", "Error: " + e.getMessage());
+ }
+});
+```
+
+
+```kotlin
+CometChat.markFeedItemAsRead(feedItem, object : CometChat.CallbackListener() {
+ override fun onSuccess(result: Void?) {
+ Log.d("Feed", "Marked as read")
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e("Feed", "Error: ${e.message}")
+ }
+})
+```
+
+
+
+---
+
+## Mark Feed Item as Delivered
+
+Mark a single item as delivered. Idempotent.
+
+
+
+```java
+CometChat.markFeedItemAsDelivered(feedItem, new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(Void unused) {
+ // Success
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e("Feed", "Error: " + e.getMessage());
+ }
+});
+```
+
+
+```kotlin
+CometChat.markFeedItemAsDelivered(feedItem, object : CometChat.CallbackListener() {
+ override fun onSuccess(result: Void?) { /* Success */ }
+ override fun onError(e: CometChatException) {
+ Log.e("Feed", "Error: ${e.message}")
+ }
+})
+```
+
+
+
+---
+
+## Report Engagement
+
+Report that a user engaged with a feed item (e.g., viewed, clicked, interacted). Idempotent.
+
+
+
+```java
+CometChat.reportFeedEngagement(feedItem, "clicked", new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(Void unused) { }
+
+ @Override
+ public void onError(CometChatException e) { }
+});
+```
+
+
+```kotlin
+CometChat.reportFeedEngagement(feedItem, "clicked", object : CometChat.CallbackListener() {
+ override fun onSuccess(result: Void?) { }
+ override fun onError(e: CometChatException) { }
+})
+```
+
+
+
+The `interactionString` parameter is a free-form string describing the engagement (e.g., `"viewed"`, `"clicked"`, `"interacted"`).
+
+---
+
+## Get Unread Count
+
+Fetch the total number of unread notification feed items.
+
+
+
+```java
+CometChat.getNotificationFeedUnreadCount(new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(Integer count) {
+ Log.d("Feed", "Unread: " + count);
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e("Feed", "Error: " + e.getMessage());
+ }
+});
+```
+
+
+```kotlin
+CometChat.getNotificationFeedUnreadCount(object : CometChat.CallbackListener() {
+ override fun onSuccess(count: Int) {
+ Log.d("Feed", "Unread: $count")
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e("Feed", "Error: ${e.message}")
+ }
+})
+```
+
+
+
+---
+
+## Fetch Single Feed Item
+
+Fetch a specific item by ID — useful for deep linking from push notifications.
+
+
+
+```java
+CometChat.getNotificationFeedItem("item-id-123", new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(NotificationFeedItem item) {
+ String cardJson = item.getContent().toString();
+ // Render the card
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e("Feed", "Error: " + e.getMessage());
+ }
+});
+```
+
+
+```kotlin
+CometChat.getNotificationFeedItem("item-id-123", object : CometChat.CallbackListener() {
+ override fun onSuccess(item: NotificationFeedItem) {
+ val cardJson = item.content.toString()
+ // Render the card
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e("Feed", "Error: ${e.message}")
+ }
+})
+```
+
+
+
+---
+
+## Push Notification Tracking
+
+When a campaign push notification arrives via FCM, use these methods to report delivery and click engagement.
+
+### Mark Push Notification as Delivered
+
+Call this in your `FirebaseMessagingService.onMessageReceived()`:
+
+
+
+```java
+PushNotification pushNotification = PushNotification.fromJson(pushPayloadJson);
+
+CometChat.markPushNotificationDelivered(pushNotification, new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(Void unused) { }
+
+ @Override
+ public void onError(CometChatException e) { }
+});
+```
+
+
+```kotlin
+val pushNotification = PushNotification.fromJson(pushPayloadJson)
+
+CometChat.markPushNotificationDelivered(pushNotification, object : CometChat.CallbackListener() {
+ override fun onSuccess(result: Void?) { }
+ override fun onError(e: CometChatException) { }
+})
+```
+
+
+
+### Mark Push Notification as Clicked
+
+Call this when the user taps the push notification:
+
+
+
+```java
+CometChat.markPushNotificationClicked(pushNotification, new CometChat.CallbackListener() {
+ @Override
+ public void onSuccess(Void unused) { }
+
+ @Override
+ public void onError(CometChatException e) { }
+});
+```
+
+
+```kotlin
+CometChat.markPushNotificationClicked(pushNotification, object : CometChat.CallbackListener() {
+ override fun onSuccess(result: Void?) { }
+ override fun onError(e: CometChatException) { }
+})
+```
+
+
+
+### PushNotification Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `id` | String | Announcement ID from the push payload |
+| `announcementId` | String | Same as id (for clarity) |
+| `campaignId` | String? | Campaign ID if from a campaign |
+| `source` | String | Always `"campaign"` for notification feed pushes |
+
+---
+
+## FeedReadState Enum
+
+| Value | Description |
+| --- | --- |
+| `READ` | Only read items |
+| `UNREAD` | Only unread items |
+| `ALL` | All items (default) |
+
+---
+
+## Supported Card Actions
+
+When a user taps a button or link inside a card, the action callback receives one of these action types:
+
+| Action Type | Parameters | Description |
+| --- | --- | --- |
+| `openUrl` | url, openIn | Open a URL in browser or webview |
+| `chatWithUser` | uid | Navigate to 1:1 chat |
+| `chatWithGroup` | guid | Navigate to group chat |
+| `sendMessage` | text, receiverUid, receiverGuid | Send a text message |
+| `copyToClipboard` | value | Copy text to clipboard |
+| `downloadFile` | url, filename | Download a file |
+| `initiateCall` | callType (audio/video), uid, guid | Start a call |
+| `apiCall` | url, method, headers, body | Make an HTTP request |
+| `customCallback` | callbackId, payload | App-specific logic |
diff --git a/sdk/flutter/campaigns.mdx b/sdk/flutter/campaigns.mdx
new file mode 100644
index 000000000..3f4f003ad
--- /dev/null
+++ b/sdk/flutter/campaigns.mdx
@@ -0,0 +1,493 @@
+---
+title: "Campaigns"
+description: "Fetch notification feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and retrieve unread counts in Flutter."
+---
+
+CometChat Campaigns lets you deliver targeted, rich notifications to users via an in-app notification feed. Each notification is a **Card Schema JSON** — a structured layout rendered natively by the CometChat Cards library.
+
+The SDK provides APIs to fetch feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and retrieve unread counts.
+
+
+Before using the SDK, set up your channels, categories, templates, and campaigns in the CometChat Dashboard. See the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions.
+
+
+---
+
+## Key Concepts
+
+| Concept | Description |
+| --- | --- |
+| **NotificationFeedItem** | A single notification in the feed. Contains Card Schema JSON in its `content` field, a `category` for filtering, timestamps, and metadata. |
+| **NotificationCategory** | A category label used for filter chips (e.g., "Promotions", "Updates"). |
+| **Card Schema JSON** | The fully rendered card layout (images, text, buttons) inside `NotificationFeedItem.content`. Passed directly to the CometChat Cards renderer. |
+| **PushNotification** | Represents a campaign push notification payload received via FCM/APNs. |
+
+---
+
+## How Cards Render in the Notification Feed
+
+Each `NotificationFeedItem` has a `content` field containing a `Map` — this is the **Card Schema JSON**. This JSON is passed directly to the **CometChat Cards** renderer package (`cometchat_cards`).
+
+The rendering flow:
+
+1. Fetch feed items via `NotificationFeedRequest`
+2. For each item, extract `item.content` — this is the Card Schema JSON
+3. Convert to string: `jsonEncode(item.content)`
+4. Pass to the Cards renderer (`CometChatCardView`)
+5. The renderer produces a native Flutter widget from the JSON
+
+### Card Schema JSON Structure
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ {
+ "type": "column",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "gap": 5,
+ "items": [
+ {
+ "type": "text",
+ "content": "📢 Announcement",
+ "variant": "heading2",
+ "id": "txt_99323141-2459-4e33-88d3-ca39c5fd2f50"
+ },
+ {
+ "type": "text",
+ "content": "Your announcement message here.",
+ "variant": "body",
+ "id": "txt_61a417bc-5e4a-4ba2-bfe7-b7bc64dbaf35"
+ },
+ {
+ "type": "divider",
+ "id": "div_80f5c7fb-fd10-41d1-8c2f-51498f0f62d0"
+ },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "textColor": {
+ "light": "#6C5CE7",
+ "dark": "#6C5CE7"
+ },
+ "size": 40,
+ "fontSize": 13,
+ "borderRadius": 6,
+ "padding": {
+ "top": 0,
+ "right": 16,
+ "bottom": 0,
+ "left": 16
+ },
+ "action": {
+ "type": "openUrl",
+ "url": ""
+ },
+ "id": "btn_9b87a3f1-b0c6-45b9-a4c2-e22ea590f17f"
+ }
+ ],
+ "id": "col_98fed9bd-1a95-4cee-aa81-84a9016e41f2"
+ }
+ ],
+ "fallbackText": "",
+ "style": {
+ "background": {
+ "light": "#E8E8E8",
+ "dark": "#E8E8E8"
+ },
+ "borderRadius": 16,
+ "borderColor": {
+ "light": "#DFE6E9",
+ "dark": "#DFE6E9"
+ },
+ "padding": 12
+ }
+}
+```
+
+The `body` array contains elements (text, image, button, row, column, etc.) rendered top-to-bottom. Interactive elements like buttons emit actions via a callback — the consumer handles navigation, deep links, or API calls.
+
+---
+
+## Retrieve Notification Feed Items
+
+Use `NotificationFeedRequest` to fetch a paginated list of feed items. Uses cursor-based pagination internally.
+
+### Build the Request
+
+
+
+```dart
+final request = (NotificationFeedRequestBuilder()
+ ..setLimit(20))
+ .build();
+```
+
+
+
+### Builder Parameters
+
+| Method | Type | Default | Description |
+| --- | --- | --- | --- |
+| `setLimit(int)` | int | 20 | Items per page (max 100) |
+| `setReadState(FeedReadState)` | enum | `FeedReadState.all` | Filter by `read`, `unread`, or `all` |
+| `setCategory(String)` | String | null | Filter by category ID |
+| `setChannelId(String)` | String | null | Filter by channel |
+| `setTags(List)` | List | null | Filter by tags |
+| `setDateFrom(String)` | String | null | ISO 8601 date — items sent on or after |
+| `setDateTo(String)` | String | null | ISO 8601 date — items sent on or before |
+
+### Fetch Items
+
+
+
+```dart
+request.fetchNext(
+ onSuccess: (List items) {
+ for (final item in items) {
+ final cardJson = jsonEncode(item.content);
+ // Pass cardJson to CometChatCardView
+ }
+ },
+ onError: (CometChatException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+
+
+Call `fetchNext()` repeatedly for pagination. When the server has no more items, subsequent calls return an empty list.
+
+### NotificationFeedItem Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `id` | String | Unique item identifier |
+| `category` | String | Notification category (e.g., "promotions") |
+| `categoryId` | String? | Category ID for programmatic filtering |
+| `content` | Map\ | Card Schema JSON — pass to CometChat Cards renderer |
+| `readAt` | int? | Unix timestamp when read, or null if unread |
+| `deliveredAt` | int? | Unix timestamp when delivered, or null |
+| `sentAt` | int | Unix timestamp when sent |
+| `metadata` | Map\ | Custom key-value metadata |
+| `tags` | List\ | Tags for filtering |
+| `sender` | String | Sender identifier |
+| `receiver` | String | Receiver identifier |
+| `receiverType` | String | Receiver type |
+| `isRead` | bool | Computed property — `true` if `readAt != null` |
+
+---
+
+## Retrieve Notification Categories
+
+Use `NotificationCategoriesRequest` to fetch available categories for filter chips.
+
+
+
+```dart
+final categoriesRequest = (NotificationCategoriesRequestBuilder()
+ ..setLimit(50))
+ .build();
+
+categoriesRequest.fetchNext(
+ onSuccess: (List categories) {
+ for (final category in categories) {
+ debugPrint("Category: ${category.name}");
+ }
+ },
+ onError: (CometChatException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+
+
+### NotificationCategory Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `id` | String | Category identifier |
+| `name` | String | Display name for filter UI |
+| `description` | String | Category description |
+| `appId` | String | Associated app ID |
+
+---
+
+## Real-Time Notification Feed Listener
+
+Listen for new feed items arriving via WebSocket. This listener is independent from `MessageListener`, `GroupListener`, and `CallListener`.
+
+
+
+```dart
+class MyNotificationFeedListener with NotificationFeedListener {
+ @override
+ void onFeedItemReceived(NotificationFeedItem feedItem) {
+ debugPrint("New item: ${feedItem.id}");
+ final cardJson = jsonEncode(feedItem.content);
+ // Insert at top of feed and render
+ }
+}
+
+CometChat.addNotificationFeedListener(
+ "feedListener",
+ MyNotificationFeedListener(),
+);
+```
+
+
+
+Remove the listener when no longer needed:
+
+
+
+```dart
+CometChat.removeNotificationFeedListener("feedListener");
+```
+
+
+
+---
+
+## Mark Feed Item as Read
+
+Mark a single item as read. Idempotent — safe to call multiple times.
+
+
+
+```dart
+CometChat.markFeedItemAsRead(
+ feedItem,
+ onSuccess: (result) {
+ debugPrint("Marked as read");
+ },
+ onError: (CometChatException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+
+
+---
+
+## Mark Feed Item as Delivered
+
+Mark a single item as delivered. Idempotent.
+
+
+
+```dart
+CometChat.markFeedItemAsDelivered(
+ feedItem,
+ onSuccess: (result) {
+ // Success
+ },
+ onError: (CometChatException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+
+
+---
+
+## Report Engagement
+
+Report that a user engaged with a feed item (e.g., viewed, clicked, interacted). Idempotent.
+
+
+
+```dart
+CometChat.reportFeedEngagement(
+ feedItem,
+ "clicked",
+ onSuccess: (result) {},
+ onError: (CometChatException e) {},
+);
+```
+
+
+
+The `interactionString` parameter is a free-form string describing the engagement (e.g., `"viewed"`, `"clicked"`, `"interacted"`).
+
+---
+
+## Get Unread Count
+
+Fetch the total number of unread notification feed items.
+
+
+
+```dart
+CometChat.getNotificationFeedUnreadCount(
+ onSuccess: (int count) {
+ debugPrint("Unread: $count");
+ },
+ onError: (CometChatException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+
+
+---
+
+## Fetch Single Feed Item
+
+Fetch a specific item by ID — useful for deep linking from push notifications.
+
+
+
+```dart
+CometChat.getNotificationFeedItem(
+ "item-id-123",
+ onSuccess: (NotificationFeedItem item) {
+ final cardJson = jsonEncode(item.content);
+ // Render the card
+ },
+ onError: (CometChatException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+
+
+---
+
+## Push Notification Tracking
+
+When a campaign push notification arrives via FCM/APNs, use these methods to report delivery and click engagement.
+
+### Mark Push Notification as Delivered
+
+Call this when you receive the push notification payload:
+
+
+
+```dart
+final pushNotification = PushNotification(
+ id: pushPayload['announcementId'],
+ announcementId: pushPayload['announcementId'],
+ campaignId: pushPayload['campaignId'],
+ source: "campaign",
+);
+
+CometChat.markPushNotificationDelivered(
+ pushNotification,
+ onSuccess: (result) {},
+ onError: (CometChatException e) {},
+);
+```
+
+
+
+### Mark Push Notification as Clicked
+
+Call this when the user taps the push notification:
+
+
+
+```dart
+CometChat.markPushNotificationClicked(
+ pushNotification,
+ onSuccess: (result) {},
+ onError: (CometChatException e) {},
+);
+```
+
+
+
+### PushNotification Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `id` | String | Announcement ID from the push payload |
+| `announcementId` | String | Same as id (for clarity) |
+| `campaignId` | String? | Campaign ID if from a campaign |
+| `source` | String | Always `"campaign"` for notification feed pushes |
+
+---
+
+## FeedReadState Enum
+
+| Value | Description |
+| --- | --- |
+| `FeedReadState.read` | Only read items |
+| `FeedReadState.unread` | Only unread items |
+| `FeedReadState.all` | All items (default) |
+
+---
+
+## Rendering Cards
+
+The `content` field of each `NotificationFeedItem` is a Card Schema JSON map. To render it natively, use the CometChat Cards library.
+
+### Add the Cards Dependency
+
+Add the cards package to your `pubspec.yaml`:
+
+```yaml
+dependencies:
+ cometchat_cards: ^1.0.0
+```
+
+### Render a Card from a Feed Item
+
+
+
+```dart
+import 'package:cometchat_cards/cometchat_cards.dart';
+import 'dart:convert';
+
+Widget buildNotificationCard(NotificationFeedItem item) {
+ return CometChatCardView(
+ cardJson: jsonEncode(item.content),
+ themeMode: CometChatCardThemeMode.auto,
+ onAction: (CometChatCardActionEvent event) {
+ // Handle action: event.action, event.elementId
+ if (event.action is CometChatCardOpenUrlAction) {
+ // Open URL in browser
+ } else if (event.action is CometChatCardChatWithUserAction) {
+ // Navigate to chat
+ }
+ },
+ );
+}
+```
+
+
+
+
+The Cards library is a pure renderer — it does not execute actions. Your code must handle action callbacks (opening URLs, navigating to chats, making API calls, etc.).
+
+
+---
+
+## Supported Card Actions
+
+When a user taps a button or link inside a card, the action callback receives one of these action types:
+
+| Action Type | Parameters | Description |
+| --- | --- | --- |
+| `openUrl` | url, openIn | Open a URL in browser or webview |
+| `chatWithUser` | uid | Navigate to 1:1 chat |
+| `chatWithGroup` | guid | Navigate to group chat |
+| `sendMessage` | text, receiverUid, receiverGuid | Send a text message |
+| `copyToClipboard` | value | Copy text to clipboard |
+| `downloadFile` | url, filename | Download a file |
+| `initiateCall` | callType (audio/video), uid, guid | Start a call |
+| `apiCall` | url, method, headers, body | Make an HTTP request |
+| `customCallback` | callbackId, payload | App-specific logic |
diff --git a/sdk/ios/campaigns.mdx b/sdk/ios/campaigns.mdx
new file mode 100644
index 000000000..30c0ce259
--- /dev/null
+++ b/sdk/ios/campaigns.mdx
@@ -0,0 +1,503 @@
+---
+title: "Campaigns"
+description: "CometChat Campaigns SDK APIs — fetching feed items, managing engagement state, reporting interactions, and receiving real-time notifications."
+---
+
+CometChat Campaigns lets you deliver targeted, rich notifications to users via an in-app notification feed. Each notification is a Card Schema JSON — a structured layout rendered natively by the CometChat Cards library. The SDK provides APIs to fetch feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and retrieve unread counts.
+
+
+Before using the SDK, set up your channels, categories, templates, and campaigns in the CometChat Dashboard. See the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions.
+
+
+---
+
+## CometChatCardsSwift
+
+`CometChatCardsSwift` is the rendering library for Campaigns. The `NotificationFeedItem.content` field contains a Card Schema JSON — a structured layout definition. `CometChatCardsSwift` takes this JSON and produces a native UIKit view. Without it, `content` is just a raw dictionary.
+
+The library:
+
+- Parses the JSON schema into typed Swift models.
+- Renders native UIKit views (no web views) for each element (text, images, buttons, rows, etc.).
+- Handles light/dark mode color resolution from themed color pairs.
+- Manages interactive actions — button taps emit structured `CometChatCardActionEvent` objects with the action type and element ID.
+- Supports dynamic content — accordions expand/collapse, tabs switch panels, all with proper auto-layout height changes.
+
+If you use `CometChatNotificationFeed` (UI Kit), the Cards library is used internally and you don't interact with it directly. If you're building a custom feed UI, you import `CometChatCardsSwift` and use `CometChatCardView` to render each item's content.
+
+---
+
+## Setting Up CometChatCardsSwift
+
+### Installation
+
+**Swift Package Manager**
+
+In Xcode: **File → Add Package Dependencies** → enter:
+
+```
+https://github.com/cometchat/cometchat-cards-sdk-ios
+```
+
+Add `CometChatCardsSwift` to your target.
+
+**CocoaPods**
+
+```ruby
+pod 'CometChatCardsSwift'
+```
+
+```bash
+pod install
+```
+
+No initialization is required. The library is stateless — import it and start rendering.
+
+### Rendering Feed Items as Cards
+
+```swift
+import CometChatSDK
+import CometChatCardsSwift
+
+// After fetching items via NotificationFeedRequest
+request.fetchNext(onSuccess: { items in
+ for item in items {
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: item.content),
+ let jsonString = String(data: jsonData, encoding: .utf8) else { continue }
+
+ let cardView = CometChatCardView(frame: .zero)
+ cardView.translatesAutoresizingMaskIntoConstraints = false
+ cardView.themeMode = .auto
+ cardView.cardJson = jsonString
+
+ cardView.actionCallback = { actionEvent in
+ // Report engagement on every action
+ CometChat.reportFeedEngagement(item, interactionString: actionEvent.elementId, onSuccess: {}, onError: { _ in })
+
+ switch actionEvent.action {
+ case .openUrl(let url, _):
+ if let link = URL(string: url) {
+ UIApplication.shared.open(link)
+ }
+ case .chatWithUser(let uid):
+ // Navigate to 1-on-1 chat
+ break
+ case .chatWithGroup(let guid):
+ // Navigate to group chat
+ break
+ default:
+ break
+ }
+ }
+
+ container.addSubview(cardView)
+ }
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+### CometChatCardView Properties
+
+| Property | Type | Description |
+| -------- | ---- | ----------- |
+| `cardJson` | `String?` | Set this to render a card. Triggers a full re-render. |
+| `themeMode` | `CometChatCardThemeMode` | `.auto` (follows system), `.light`, or `.dark`. |
+| `actionCallback` | `((CometChatCardActionEvent) -> Void)?` | Called when user taps a button, link, or icon button inside the card. |
+| `themeOverride` | `CometChatCardThemeOverride?` | Override default theme colors without modifying card JSON. |
+
+### Theme Override
+
+Override default card theme colors to match your app's brand:
+
+```swift
+var override = CometChatCardThemeOverride()
+override.textColor = CometChatCardColorValue(light: "#1A1A1A", dark: "#F0F0F0")
+override.buttonFilledBg = CometChatCardColorValue(light: "#6852D6", dark: "#8B7AE8")
+override.buttonFilledText = CometChatCardColorValue(light: "#FFFFFF", dark: "#FFFFFF")
+override.fontFamily = "Avenir"
+
+cardView.themeOverride = override
+```
+
+### Content Size Changes
+
+Cards with expandable content (accordions, tabs) change height dynamically. Observe size changes to update your layout:
+
+```swift
+NotificationCenter.default.addObserver(
+ forName: CometChatCardView.contentSizeDidChangeNotification,
+ object: cardView,
+ queue: .main
+) { _ in
+ tableView.beginUpdates()
+ tableView.endUpdates()
+}
+```
+
+---
+
+## Key Concepts
+
+| Concept | Description |
+| ------- | ----------- |
+| NotificationFeedItem | A single notification in the feed. Contains Card Schema JSON in its `content` field, a category for filtering, timestamps, and metadata. |
+| NotificationCategory | A category label used for filter chips (e.g., "Promotions", "Updates"). |
+| Card Schema JSON | The fully rendered card layout (images, text, buttons) inside `NotificationFeedItem.content`. Passed directly to the CometChat Cards renderer. |
+| PushNotification | Represents a campaign push notification payload received via APNs. |
+
+---
+
+## How Cards Render in the Notification Feed
+
+Each `NotificationFeedItem` has a `content` field containing a `[String: Any]` dictionary — this is the Card Schema JSON. This JSON is passed directly to the CometChat Cards renderer library.
+
+The rendering flow:
+
+1. Fetch feed items via `NotificationFeedRequest`
+2. For each item, extract `item.content` — this is the Card Schema JSON
+3. Pass to the Cards renderer (`CometChatCardView`)
+4. The renderer produces a native UIKit view from the JSON
+
+### Card Schema JSON Structure
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ { "type": "image", "id": "img_1", "url": "https://...", "height": 200 },
+ { "type": "text", "id": "txt_1", "content": "Flash Sale!", "variant": "heading2" },
+ { "type": "button", "id": "btn_1", "label": "Shop Now", "action": { "type": "openUrl", "url": "https://..." } }
+ ],
+ "style": { "background": {"light": "#FFFFFF", "dark": "#1E1E1E"}, "borderRadius": 12, "padding": 16 },
+ "fallbackText": "Flash Sale! Shop Now: https://..."
+}
+```
+
+The `body` array contains elements (text, image, button, row, column, etc.) rendered top-to-bottom. Interactive elements like buttons emit actions via a callback — the consumer handles navigation, deep links, or API calls.
+
+---
+
+## Retrieve Notification Feed Items
+
+Use `NotificationFeedRequest` to fetch a paginated list of feed items. Uses cursor-based pagination internally.
+
+### Build the Request
+
+```swift
+let request = NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .set(limit: 20)
+ .set(readState: .unread)
+ .set(category: "Updates")
+ .build()
+```
+
+### Builder Parameters
+
+| Method | Type | Default | Description |
+| ------ | ---- | ------- | ----------- |
+| `set(limit:)` | `Int` | 20 | Items per page. |
+| `set(readState:)` | `FeedReadState` | All | Filter by `.read` or `.unread`. Omit for all. |
+| `set(category:)` | `String` | nil | Filter by category name. Sent as `templateCategory` query param. If `categoryId` is also set, `categoryId` takes priority. |
+| `set(categoryId:)` | `String` | nil | Filter by category ID. Sent as `templateCategory` query param. Takes priority over `category` if both are set. |
+| `set(channelId:)` | `String` | nil | Filter by channel. |
+| `set(tags:)` | `[String]` | nil | Filter by tags (comma-joined). |
+| `set(dateFrom:)` | `String` | nil | ISO 8601 date — items sent on or after. |
+| `set(dateTo:)` | `String` | nil | ISO 8601 date — items sent on or before. |
+
+
+`set(category:)` and `set(categoryId:)` both map to the same server-side filter (`templateCategory`). Use one or the other — not both. If both are provided, `categoryId` silently overwrites `category`.
+
+
+### Fetch Items
+
+```swift
+request.fetchNext(onSuccess: { items in
+ for item in items {
+ let cardJson = item.content
+ // Pass cardJson to CometChatCardView
+ }
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+Call `fetchNext()` repeatedly for pagination. When the server has no more items, subsequent calls return an empty array.
+
+### NotificationFeedItem Fields
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `id` | `String` | Unique item identifier. |
+| `category` | `String` | Notification category (e.g., "promotions"). |
+| `categoryId` | `String` | Category ID. |
+| `content` | `[String: Any]` | Card Schema JSON — pass to CometChat Cards renderer. |
+| `readAt` | `Double` | Unix timestamp when read, or 0 if unread. |
+| `deliveredAt` | `Double` | Unix timestamp when delivered, or 0. |
+| `sentAt` | `Double` | Unix timestamp when sent. |
+| `metadata` | `[String: Any]` | Custom key-value metadata. |
+| `tags` | `[String]` | Tags for filtering. |
+| `sender` | `String` | Sender identifier. |
+| `receiver` | `String` | Receiver identifier. |
+| `receiverType` | `String` | Receiver type. |
+| `isRead` | `Bool` | Computed — `true` if `readAt != 0`. |
+
+---
+
+## Retrieve Notification Categories
+
+Use `NotificationCategoriesRequest` to fetch available categories for filter chips.
+
+```swift
+let categoriesRequest = NotificationCategoriesRequest.NotificationCategoriesRequestBuilder()
+ .set(limit: 50)
+ .build()
+
+categoriesRequest.fetchNext(onSuccess: { categories in
+ for category in categories {
+ print("Category: \(category.label)")
+ }
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+### NotificationCategory Fields
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `id` | `String` | Category identifier. |
+| `label` | `String` | Display label for filter UI. |
+
+---
+
+## Real-Time Notification Feed Listener
+
+Listen for new feed items arriving via WebSocket. This listener is independent from `CometChatMessageDelegate`, `CometChatGroupDelegate`, and `CometChatCallDelegate`.
+
+```swift
+CometChat.addNotificationFeedListener("feedListener", self)
+
+// Implement CometChatNotificationFeedDelegate
+extension MyViewController: CometChatNotificationFeedDelegate {
+ func onFeedItemReceived(feedItem: NotificationFeedItem) {
+ print("New item: \(feedItem.id)")
+ let cardJson = feedItem.content
+ // Insert at top of feed and render
+ }
+}
+```
+
+Remove the listener when no longer needed:
+
+```swift
+CometChat.removeNotificationFeedListener("feedListener")
+```
+
+---
+
+## Mark Feed Item as Read
+
+Mark a single item as read. Idempotent — safe to call multiple times.
+
+```swift
+CometChat.markFeedItemAsRead(feedItem, onSuccess: {
+ print("Marked as read")
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+---
+
+## Mark Feed Item as Delivered
+
+Mark a single item as delivered. Idempotent.
+
+```swift
+CometChat.markFeedItemAsDelivered(feedItem, onSuccess: {
+ // Success
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+---
+
+## Report Engagement
+
+Report that a user engaged with a feed item (e.g., viewed, clicked, interacted). Idempotent per topic.
+
+```swift
+CometChat.reportFeedEngagement(feedItem, interactionString: "clicked", onSuccess: {
+ // Success
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+The `interactionString` parameter is a free-form string describing the engagement (e.g., "viewed", "clicked", "interacted").
+
+---
+
+## Get Unread Count
+
+Fetch the total number of unread notification feed items. Optionally filter by category.
+
+```swift
+CometChat.getNotificationFeedUnreadCount(category: nil, onSuccess: { count in
+ print("Unread: \(count)")
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+---
+
+## Fetch Single Feed Item
+
+Fetch a specific item by ID — useful for deep linking from push notifications.
+
+```swift
+CometChat.getNotificationFeedItem(id: "item-id-123", onSuccess: { item in
+ let cardJson = item.content
+ // Render the card
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+---
+
+## Push Notification Tracking
+
+When a campaign push notification arrives via APNs, use these methods to report delivery and click engagement.
+
+### Setting Up Notification Service Extension
+
+1. In Xcode: **File → New → Target** → select **Notification Service Extension**.
+2. Enable **App Groups** on both your main app target and the extension target with the same group ID (e.g., `group.com.yourapp.cometchat`).
+3. Add `CometChatSDK` as a dependency to the extension target.
+
+### Identifying Campaign Push Notifications
+
+Campaign pushes contain a `cometchat` dictionary with `"type": "business_messaging"`:
+
+```swift
+func isCampaignNotification(userInfo: [AnyHashable: Any]) -> Bool {
+ if let cometchat = userInfo["cometchat"] as? [String: Any],
+ let type = cometchat["type"] as? String,
+ type == "business_messaging" {
+ return true
+ }
+ return false
+}
+```
+
+### Push Payload Structure
+
+```json
+{
+ "aps": { "alert": { "title": "...", "body": "..." }, "badge": 1, "mutable-content": 1 },
+ "cometchat": {
+ "type": "business_messaging",
+ "appId": "your-app-id",
+ "campaignId": "campaign-cuid",
+ "notificationId": "notification-cuid",
+ "pushNotificationId": "uuid-for-push-tracking",
+ "uid": "target-user-id",
+ "sentAt": "2026-05-21T12:43:55.206Z"
+ }
+}
+```
+
+### Mark Push Notification as Delivered
+
+Call this in your Notification Service Extension:
+
+```swift
+import UserNotifications
+import CometChatSDK
+
+class NotificationService: UNNotificationServiceExtension {
+ override func didReceive(_ request: UNNotificationRequest,
+ withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
+ let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)!
+
+ // Required for extensions to share auth state via App Group container
+ CometChat.setExtensionGroupID(id: "group.your.app.group")
+
+ let userInfo = bestAttemptContent.userInfo
+ if let cometchat = userInfo["cometchat"] as? [String: Any],
+ let type = cometchat["type"] as? String,
+ type == "business_messaging" {
+ let push = PushNotification.fromPayload(cometchat)
+ CometChat.markPushNotificationDelivered(push, onSuccess: {}, onError: { _ in })
+ }
+
+ contentHandler(bestAttemptContent)
+ }
+}
+```
+
+### Mark Push Notification as Clicked
+
+Call this when the user taps the push notification:
+
+```swift
+func userNotificationCenter(_ center: UNUserNotificationCenter,
+ didReceive response: UNNotificationResponse,
+ withCompletionHandler completionHandler: @escaping () -> Void) {
+ let userInfo = response.notification.request.content.userInfo
+
+ if let cometchat = userInfo["cometchat"] as? [String: Any],
+ let type = cometchat["type"] as? String,
+ type == "business_messaging" {
+ let push = PushNotification.fromPayload(cometchat)
+ CometChat.markPushNotificationClicked(push, onSuccess: {}, onError: { _ in })
+ navigateToNotificationsTab()
+ completionHandler()
+ return
+ }
+
+ completionHandler()
+}
+```
+
+### PushNotification Fields
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `id` | `String` | Push notification ID from the push payload. |
+| `announcementId` | `String` | Notification/announcement ID. |
+| `campaignId` | `String?` | Campaign ID if from a campaign. |
+| `source` | `String` | Always "campaign" for notification feed pushes. |
+
+---
+
+## FeedReadState Enum
+
+| Case | Value | Description |
+| ---- | ----- | ----------- |
+| `.read` | `"read"` | Only read items. |
+| `.unread` | `"unread"` | Only unread items. |
+
+If not set, returns all items (default).
+
+---
+
+## Supported Card Actions
+
+When a user taps a button or link inside a card, the action callback receives one of these action types:
+
+| Action Type | Parameters | Description |
+| ----------- | ---------- | ----------- |
+| `openUrl` | url, openIn | Open a URL in browser or webview. |
+| `chatWithUser` | uid | Navigate to 1:1 chat. |
+| `chatWithGroup` | guid | Navigate to group chat. |
+| `sendMessage` | text, receiverUid, receiverGuid | Send a text message. |
+| `copyToClipboard` | value | Copy text to clipboard. |
+| `downloadFile` | url, filename | Download a file. |
+| `initiateCall` | callType (audio/video), uid, guid | Start a call. |
+| `apiCall` | url, method, headers, body | Make an HTTP request. |
+| `customCallback` | callbackId, payload | App-specific logic. |
diff --git a/sdk/javascript/campaigns.mdx b/sdk/javascript/campaigns.mdx
new file mode 100644
index 000000000..a2394204c
--- /dev/null
+++ b/sdk/javascript/campaigns.mdx
@@ -0,0 +1,583 @@
+---
+title: "Campaigns"
+description: "Fetch notification feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and track push notifications using the CometChat JavaScript SDK."
+---
+
+CometChat Campaigns lets you deliver targeted, rich notifications to users via an in-app notification feed. Each notification is a **Card Schema JSON** — a structured layout rendered natively by the CometChat Cards library.
+
+The SDK provides APIs to fetch feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and retrieve unread counts.
+
+
+Before using the SDK, set up your channels, categories, templates, and campaigns in the CometChat Dashboard. See the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions.
+
+
+---
+
+## Key Concepts
+
+| Concept | Description |
+| --- | --- |
+| **NotificationFeedItem** | A single notification in the feed. Contains Card Schema JSON in its `content` field, a `category` for filtering, timestamps, and metadata. |
+| **NotificationCategory** | A category label used for filter chips (e.g., "Promotions", "Updates"). |
+| **Card Schema JSON** | The fully rendered card layout (images, text, buttons) inside `NotificationFeedItem.getContent()`. Passed directly to the CometChat Cards renderer. |
+| **PushNotification** | Represents a campaign push notification payload received via FCM/APNs or Web Push. |
+
+---
+
+## How Cards Render in the Notification Feed
+
+Each `NotificationFeedItem` has a `content` field containing an object — this is the **Card Schema JSON**. This JSON is passed directly to the **CometChat Cards** renderer library (`@cometchat/cards-react`).
+
+The rendering flow:
+
+1. Fetch feed items via `NotificationFeedRequestBuilder`
+2. For each item, extract `item.getContent()` — this is the Card Schema JSON
+3. Convert to string: `JSON.stringify(item.getContent())`
+4. Pass to the Cards renderer (`CometChatCardView`)
+5. The renderer produces a native DOM element from the JSON
+
+### Card Schema JSON Structure
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ {
+ "type": "column",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "gap": 5,
+ "items": [
+ {
+ "type": "text",
+ "content": "📢 Announcement",
+ "variant": "heading2",
+ "id": "txt_99323141-2459-4e33-88d3-ca39c5fd2f50"
+ },
+ {
+ "type": "text",
+ "content": "Your announcement message here.",
+ "variant": "body",
+ "id": "txt_61a417bc-5e4a-4ba2-bfe7-b7bc64dbaf35"
+ },
+ {
+ "type": "divider",
+ "id": "div_80f5c7fb-fd10-41d1-8c2f-51498f0f62d0"
+ },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "textColor": {
+ "light": "#6C5CE7",
+ "dark": "#6C5CE7"
+ },
+ "size": 40,
+ "fontSize": 13,
+ "borderRadius": 6,
+ "padding": {
+ "top": 0,
+ "right": 16,
+ "bottom": 0,
+ "left": 16
+ },
+ "action": {
+ "type": "openUrl",
+ "url": ""
+ },
+ "id": "btn_9b87a3f1-b0c6-45b9-a4c2-e22ea590f17f"
+ }
+ ],
+ "id": "col_98fed9bd-1a95-4cee-aa81-84a9016e41f2"
+ }
+ ],
+ "fallbackText": "",
+ "style": {
+ "background": {
+ "light": "#E8E8E8",
+ "dark": "#E8E8E8"
+ },
+ "borderRadius": 16,
+ "borderColor": {
+ "light": "#DFE6E9",
+ "dark": "#DFE6E9"
+ },
+ "padding": 12
+ }
+}
+```
+
+The `body` array contains elements (text, image, button, row, column, etc.) rendered top-to-bottom. Interactive elements like buttons emit actions via a callback — the consumer handles navigation, deep links, or API calls.
+
+---
+
+## Retrieve Notification Feed Items
+
+Use `NotificationFeedRequestBuilder` to fetch a paginated list of feed items. Uses cursor-based pagination internally.
+
+### Build the Request
+
+
+
+```javascript
+const request = new CometChat.NotificationFeedRequestBuilder()
+ .setLimit(20)
+ .build();
+```
+
+
+```typescript
+const request: CometChat.NotificationFeedRequest = new CometChat.NotificationFeedRequestBuilder()
+ .setLimit(20)
+ .build();
+```
+
+
+
+### Builder Parameters
+
+| Method | Type | Default | Description |
+| --- | --- | --- | --- |
+| `setLimit(limit)` | number | 20 | Items per page (max 100) |
+| `setReadState(state)` | FeedReadState | `"all"` | Filter by `"read"`, `"unread"`, or `"all"` |
+| `setCategory(category)` | string | null | Filter by category label |
+| `setChannelId(channelId)` | string | null | Filter by channel |
+| `setTags(tags)` | string[] | null | Filter by tags |
+| `setDateFrom(date)` | string | null | ISO 8601 date — items sent on or after |
+| `setDateTo(date)` | string | null | ISO 8601 date — items sent on or before |
+
+### Fetch Items
+
+
+
+```javascript
+request.fetchNext().then(
+ (items) => {
+ for (const item of items) {
+ const cardJson = JSON.stringify(item.getContent());
+ // Pass cardJson to CometChatCardView
+ }
+ },
+ (error) => {
+ console.error("Feed fetch error:", error.message);
+ }
+);
+```
+
+
+```typescript
+request.fetchNext().then(
+ (items: CometChat.NotificationFeedItem[]) => {
+ for (const item of items) {
+ const cardJson: string = JSON.stringify(item.getContent());
+ // Pass cardJson to CometChatCardView
+ }
+ },
+ (error: CometChat.CometChatException) => {
+ console.error("Feed fetch error:", error.message);
+ }
+);
+```
+
+
+
+Call `fetchNext()` repeatedly for pagination. When the server has no more items, subsequent calls return an empty array.
+
+### NotificationFeedItem Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `getId()` | string | Unique item identifier |
+| `getCategory()` | string | Notification category (e.g., "promotions") |
+| `getContent()` | object | Card Schema JSON — pass to CometChat Cards renderer |
+| `getReadAt()` | number \| null | Unix timestamp when read, or null if unread |
+| `getDeliveredAt()` | number \| null | Unix timestamp when delivered, or null |
+| `getSentAt()` | number | Unix timestamp when sent |
+| `getMetadata()` | `Record` | Custom key-value metadata |
+| `getTags()` | string[] | Tags for filtering |
+| `getSender()` | string | Sender identifier |
+| `getReceiver()` | string | Receiver identifier |
+| `getReceiverType()` | string | Receiver type |
+| `getIsRead()` | boolean | Whether the item has been read |
+
+---
+
+## Retrieve Notification Categories
+
+Use `NotificationCategoriesRequestBuilder` to fetch available categories for filter chips.
+
+
+
+```javascript
+const categoriesRequest = new CometChat.NotificationCategoriesRequestBuilder()
+ .setLimit(50)
+ .build();
+
+categoriesRequest.fetchNext().then(
+ (categories) => {
+ for (const category of categories) {
+ console.log("Category:", category.getLabel());
+ }
+ },
+ (error) => {
+ console.error("Categories fetch error:", error.message);
+ }
+);
+```
+
+
+```typescript
+const categoriesRequest: CometChat.NotificationCategoriesRequest =
+ new CometChat.NotificationCategoriesRequestBuilder()
+ .setLimit(50)
+ .build();
+
+categoriesRequest.fetchNext().then(
+ (categories: CometChat.NotificationCategory[]) => {
+ for (const category of categories) {
+ console.log("Category:", category.getLabel());
+ }
+ },
+ (error: CometChat.CometChatException) => {
+ console.error("Categories fetch error:", error.message);
+ }
+);
+```
+
+
+
+### NotificationCategory Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `getId()` | string | Category identifier |
+| `getLabel()` | string | Display name for filter UI |
+
+---
+
+## Real-Time Notification Feed Listener
+
+Listen for new feed items arriving via WebSocket. This listener is independent from `MessageListener`, `GroupListener`, and `CallListener`.
+
+
+
+```javascript
+CometChat.addNotificationFeedListener("feedListener", {
+ onFeedItemReceived: (feedItem) => {
+ console.log("New item:", feedItem.getId());
+ const cardJson = JSON.stringify(feedItem.getContent());
+ // Insert at top of feed and render
+ },
+});
+```
+
+
+```typescript
+CometChat.addNotificationFeedListener("feedListener", {
+ onFeedItemReceived: (feedItem: CometChat.NotificationFeedItem) => {
+ console.log("New item:", feedItem.getId());
+ const cardJson: string = JSON.stringify(feedItem.getContent());
+ // Insert at top of feed and render
+ },
+});
+```
+
+
+
+Remove the listener when no longer needed:
+
+```javascript
+CometChat.removeNotificationFeedListener("feedListener");
+```
+
+---
+
+## Mark Feed Item as Read
+
+Mark a single item as read. Idempotent — safe to call multiple times.
+
+
+
+```javascript
+CometChat.markFeedItemAsRead(feedItem).then(
+ () => { console.log("Marked as read"); },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.markFeedItemAsRead(feedItem).then(
+ () => { console.log("Marked as read"); },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+---
+
+## Mark Feed Item as Delivered
+
+Mark a single item as delivered. Idempotent.
+
+
+
+```javascript
+CometChat.markFeedItemAsDelivered(feedItem).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.markFeedItemAsDelivered(feedItem).then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+### Mark Multiple Items as Delivered (Batch)
+
+```javascript
+CometChat.markFeedItemsAsDelivered(feedItems).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+---
+
+## Report Engagement
+
+Report that a user engaged with a feed item (e.g., viewed, clicked, interacted). Idempotent.
+
+
+
+```javascript
+CometChat.reportFeedEngagement(feedItem, "clicked").then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.reportFeedEngagement(feedItem, "clicked").then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+The `interactionString` parameter is a free-form string describing the engagement (e.g., `"viewed"`, `"clicked"`, `"interacted"`).
+
+---
+
+## Get Unread Count
+
+Fetch the total number of unread notification feed items.
+
+
+
+```javascript
+CometChat.getNotificationFeedUnreadCount().then(
+ (result) => { console.log("Unread:", result.count); },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.getNotificationFeedUnreadCount().then(
+ (result: { count: number }) => { console.log("Unread:", result.count); },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+---
+
+## Fetch Single Feed Item
+
+Fetch a specific item by ID — useful for deep linking from push notifications.
+
+
+
+```javascript
+CometChat.getNotificationFeedItem("item-id-123").then(
+ (item) => {
+ const cardJson = JSON.stringify(item.getContent());
+ // Render the card
+ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.getNotificationFeedItem("item-id-123").then(
+ (item: CometChat.NotificationFeedItem) => {
+ const cardJson: string = JSON.stringify(item.getContent());
+ // Render the card
+ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+---
+
+## Push Notification Tracking
+
+When a campaign push notification arrives via Web Push or FCM, use these methods to report delivery and click engagement.
+
+### Mark Push Notification as Delivered
+
+Call this when the push notification is received:
+
+
+
+```javascript
+const pushNotification = new CometChat.PushNotification(pushPayloadJson);
+
+CometChat.markPushNotificationDelivered(pushNotification).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+const pushNotification = new CometChat.PushNotification(pushPayloadJson);
+
+CometChat.markPushNotificationDelivered(pushNotification).then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+### Mark Push Notification as Clicked
+
+Call this when the user taps the push notification:
+
+
+
+```javascript
+CometChat.markPushNotificationClicked(pushNotification).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.markPushNotificationClicked(pushNotification).then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+### PushNotification Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `getId()` | string | Announcement ID from the push payload |
+| `getAnnouncementId()` | string | Same as id (for clarity) |
+| `getCampaignId()` | string \| null | Campaign ID if from a campaign |
+| `getSource()` | string | Always `"campaign"` for notification feed pushes |
+
+---
+
+## FeedReadState
+
+| Value | Description |
+| --- | --- |
+| `"read"` | Only read items |
+| `"unread"` | Only unread items |
+| `"all"` | All items (default) |
+
+---
+
+## Rendering Cards
+
+The `content` field of each `NotificationFeedItem` is a Card Schema JSON object. To render it natively, use the CometChat Cards library.
+
+### Add the Cards Dependency
+
+```bash
+npm install @cometchat/cards-react
+```
+
+
+If you're using `@cometchat/chat-uikit-react` v6.5.0+, the cards library is included automatically as a dependency.
+
+
+### Render a Card from a Feed Item
+
+```tsx lines
+import { CometChatCardView } from "@cometchat/cards-react";
+
+function NotificationCard({ item }) {
+ const cardJson = JSON.stringify(item.getContent());
+
+ return (
+ {
+ const { action, elementId } = event;
+ switch (action.type) {
+ case "openUrl":
+ // Open URL in browser
+ window.open(action.url, "_blank");
+ break;
+ case "chatWithUser":
+ // Navigate to chat with action.uid
+ break;
+ case "chatWithGroup":
+ // Navigate to group chat with action.guid
+ break;
+ }
+ }}
+ />
+ );
+}
+```
+
+
+The Cards library is a pure renderer — it does not execute actions. Your code must handle action callbacks (opening URLs, navigating to chats, making API calls, etc.).
+
+
+---
+
+## Supported Card Actions
+
+When a user taps a button or link inside a card, the action callback receives one of these action types:
+
+| Action Type | Parameters | Description |
+| --- | --- | --- |
+| `openUrl` | url, openIn | Open a URL in browser or webview |
+| `chatWithUser` | uid | Navigate to 1:1 chat |
+| `chatWithGroup` | guid | Navigate to group chat |
+| `sendMessage` | text, receiverUid, receiverGuid | Send a text message |
+| `copyToClipboard` | value | Copy text to clipboard |
+| `downloadFile` | url, filename | Download a file |
+| `initiateCall` | callType (audio/video), uid, guid | Start a call |
+| `apiCall` | url, method, headers, body | Make an HTTP request |
+| `customCallback` | callbackId, payload | App-specific logic |
diff --git a/sdk/react-native/campaigns.mdx b/sdk/react-native/campaigns.mdx
new file mode 100644
index 000000000..2c340bbdc
--- /dev/null
+++ b/sdk/react-native/campaigns.mdx
@@ -0,0 +1,520 @@
+---
+title: "Campaigns"
+description: "Fetch notification feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and track push notifications using the CometChat React Native SDK."
+---
+
+CometChat Campaigns lets you deliver targeted, rich notifications to users via an in-app notification feed. Each notification is a **Card Schema JSON** — a structured layout rendered natively by the CometChat Cards library.
+
+The SDK provides APIs to fetch feed items, listen for real-time delivery, mark items as read/delivered, report engagement, and retrieve unread counts.
+
+
+Before using the SDK, set up your channels, categories, templates, and campaigns in the CometChat Dashboard. See the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions.
+
+
+---
+
+## Key Concepts
+
+| Concept | Description |
+| --- | --- |
+| **NotificationFeedItem** | A single notification in the feed. Contains Card Schema JSON in its `content` field, a `category` for filtering, timestamps, and metadata. |
+| **NotificationCategory** | A category label used for filter chips (e.g., "Promotions", "Updates"). |
+| **Card Schema JSON** | The fully rendered card layout (images, text, buttons) inside `NotificationFeedItem.getContent()`. Passed directly to the CometChat Cards renderer. |
+| **PushNotification** | Represents a campaign push notification payload received via FCM/APNs. |
+
+---
+
+## How Cards Render in the Notification Feed
+
+Each `NotificationFeedItem` has a `content` field containing an object — this is the **Card Schema JSON**. This JSON is passed directly to the **CometChat Cards** renderer library (`@cometchat/cards-react-native`).
+
+The rendering flow:
+
+1. Fetch feed items via `NotificationFeedRequestBuilder`
+2. For each item, extract `item.getContent()` — this is the Card Schema JSON
+3. Convert to string: `JSON.stringify(item.getContent())`
+4. Pass to the Cards renderer (`CometChatCardView`)
+5. The renderer produces a native React Native view from the JSON
+
+### Card Schema JSON Structure
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ { "type": "image", "id": "img_1", "url": "https://...", "height": 200 },
+ { "type": "text", "id": "txt_1", "content": "Flash Sale!", "variant": "heading2" },
+ { "type": "button", "id": "btn_1", "label": "Shop Now", "action": { "type": "openUrl", "url": "https://..." } }
+ ],
+ "style": { "background": {"light": "#FFFFFF", "dark": "#1E1E1E"}, "borderRadius": 12, "padding": 16 },
+ "fallbackText": "Flash Sale! Shop Now: https://..."
+}
+```
+
+The `body` array contains elements (text, image, button, row, column, etc.) rendered top-to-bottom. Interactive elements like buttons emit actions via a callback — the consumer handles navigation, deep links, or API calls.
+
+---
+
+## Retrieve Notification Feed Items
+
+Use `NotificationFeedRequestBuilder` to fetch a paginated list of feed items. Uses cursor-based pagination internally.
+
+### Build the Request
+
+
+
+```javascript
+const request = new CometChat.NotificationFeedRequestBuilder()
+ .setLimit(20)
+ .build();
+```
+
+
+```typescript
+const request: CometChat.NotificationFeedRequest = new CometChat.NotificationFeedRequestBuilder()
+ .setLimit(20)
+ .build();
+```
+
+
+
+### Builder Parameters
+
+| Method | Type | Default | Description |
+| --- | --- | --- | --- |
+| `setLimit(limit)` | number | 20 | Items per page (max 100) |
+| `setReadState(state)` | FeedReadState | `FeedReadState.ALL` | Filter by `READ`, `UNREAD`, or `ALL` |
+| `setCategory(category)` | string | null | Filter by category label |
+| `setChannelId(channelId)` | string | null | Filter by channel |
+| `setTags(tags)` | string[] | null | Filter by tags |
+| `setDateFrom(date)` | string | null | ISO 8601 date — items sent on or after |
+| `setDateTo(date)` | string | null | ISO 8601 date — items sent on or before |
+
+### Fetch Items
+
+
+
+```javascript
+request.fetchNext().then(
+ (items) => {
+ for (const item of items) {
+ const cardJson = JSON.stringify(item.getContent());
+ // Pass cardJson to CometChatCardView
+ }
+ },
+ (error) => {
+ console.error("Feed fetch error:", error.message);
+ }
+);
+```
+
+
+```typescript
+request.fetchNext().then(
+ (items: CometChat.NotificationFeedItem[]) => {
+ for (const item of items) {
+ const cardJson: string = JSON.stringify(item.getContent());
+ // Pass cardJson to CometChatCardView
+ }
+ },
+ (error: CometChat.CometChatException) => {
+ console.error("Feed fetch error:", error.message);
+ }
+);
+```
+
+
+
+Call `fetchNext()` repeatedly for pagination. When the server has no more items, subsequent calls return an empty array.
+
+### NotificationFeedItem Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `getId()` | string | Unique item identifier |
+| `getCategory()` | string | Notification category (e.g., "promotions") |
+| `getContent()` | object | Card Schema JSON — pass to CometChat Cards renderer |
+| `getReadAt()` | number \| null | Unix timestamp when read, or null if unread |
+| `getDeliveredAt()` | number \| null | Unix timestamp when delivered, or null |
+| `getSentAt()` | number | Unix timestamp when sent |
+| `getMetadata()` | `Record` | Custom key-value metadata |
+| `getTags()` | string[] | Tags for filtering |
+| `getSender()` | string | Sender identifier |
+| `getReceiver()` | string | Receiver identifier |
+| `getReceiverType()` | string | Receiver type |
+| `getIsRead()` | boolean | Whether the item has been read |
+
+---
+
+## Retrieve Notification Categories
+
+Use `NotificationCategoriesRequestBuilder` to fetch available categories for filter chips.
+
+
+
+```javascript
+const categoriesRequest = new CometChat.NotificationCategoriesRequestBuilder()
+ .setLimit(50)
+ .build();
+
+categoriesRequest.fetchNext().then(
+ (categories) => {
+ for (const category of categories) {
+ console.log("Category:", category.getLabel());
+ }
+ },
+ (error) => {
+ console.error("Categories fetch error:", error.message);
+ }
+);
+```
+
+
+```typescript
+const categoriesRequest: CometChat.NotificationCategoriesRequest =
+ new CometChat.NotificationCategoriesRequestBuilder()
+ .setLimit(50)
+ .build();
+
+categoriesRequest.fetchNext().then(
+ (categories: CometChat.NotificationCategory[]) => {
+ for (const category of categories) {
+ console.log("Category:", category.getLabel());
+ }
+ },
+ (error: CometChat.CometChatException) => {
+ console.error("Categories fetch error:", error.message);
+ }
+);
+```
+
+
+
+### NotificationCategory Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `getId()` | string | Category identifier |
+| `getLabel()` | string | Display name for filter UI |
+
+---
+
+## Real-Time Notification Feed Listener
+
+Listen for new feed items arriving via WebSocket. This listener is independent from `MessageListener`, `GroupListener`, and `CallListener`.
+
+
+
+```javascript
+CometChat.addNotificationFeedListener("feedListener", {
+ onFeedItemReceived: (feedItem) => {
+ console.log("New item:", feedItem.getId());
+ const cardJson = JSON.stringify(feedItem.getContent());
+ // Insert at top of feed and render
+ },
+});
+```
+
+
+```typescript
+CometChat.addNotificationFeedListener("feedListener", {
+ onFeedItemReceived: (feedItem: CometChat.NotificationFeedItem) => {
+ console.log("New item:", feedItem.getId());
+ const cardJson: string = JSON.stringify(feedItem.getContent());
+ // Insert at top of feed and render
+ },
+});
+```
+
+
+
+Remove the listener when no longer needed:
+
+```javascript
+CometChat.removeNotificationFeedListener("feedListener");
+```
+
+---
+
+## Mark Feed Item as Read
+
+Mark a single item as read. Idempotent — safe to call multiple times.
+
+
+
+```javascript
+CometChat.markFeedItemAsRead(feedItem).then(
+ () => { console.log("Marked as read"); },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.markFeedItemAsRead(feedItem).then(
+ () => { console.log("Marked as read"); },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+---
+
+## Mark Feed Item as Delivered
+
+Mark a single item as delivered. Idempotent.
+
+
+
+```javascript
+CometChat.markFeedItemAsDelivered(feedItem).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.markFeedItemAsDelivered(feedItem).then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+### Mark Multiple Items as Delivered (Batch)
+
+```javascript
+CometChat.markFeedItemsAsDelivered(feedItems).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+---
+
+## Report Engagement
+
+Report that a user engaged with a feed item (e.g., viewed, clicked, interacted). Idempotent.
+
+
+
+```javascript
+CometChat.reportFeedEngagement(feedItem, "clicked").then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.reportFeedEngagement(feedItem, "clicked").then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+The `interactionString` parameter is a free-form string describing the engagement (e.g., `"viewed"`, `"clicked"`, `"interacted"`).
+
+---
+
+## Get Unread Count
+
+Fetch the total number of unread notification feed items.
+
+
+
+```javascript
+CometChat.getNotificationFeedUnreadCount().then(
+ (result) => { console.log("Unread:", result.count); },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.getNotificationFeedUnreadCount().then(
+ (result: { count: number }) => { console.log("Unread:", result.count); },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+---
+
+## Fetch Single Feed Item
+
+Fetch a specific item by ID — useful for deep linking from push notifications.
+
+
+
+```javascript
+CometChat.getNotificationFeedItem("item-id-123").then(
+ (item) => {
+ const cardJson = JSON.stringify(item.getContent());
+ // Render the card
+ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.getNotificationFeedItem("item-id-123").then(
+ (item: CometChat.NotificationFeedItem) => {
+ const cardJson: string = JSON.stringify(item.getContent());
+ // Render the card
+ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+---
+
+## Push Notification Tracking
+
+When a campaign push notification arrives via FCM/APNs, use these methods to report delivery and click engagement.
+
+### Mark Push Notification as Delivered
+
+Call this when the push notification is received:
+
+
+
+```javascript
+const pushNotification = new CometChat.PushNotification(pushPayloadJson);
+
+CometChat.markPushNotificationDelivered(pushNotification).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+const pushNotification = new CometChat.PushNotification(pushPayloadJson);
+
+CometChat.markPushNotificationDelivered(pushNotification).then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+### Mark Push Notification as Clicked
+
+Call this when the user taps the push notification:
+
+
+
+```javascript
+CometChat.markPushNotificationClicked(pushNotification).then(
+ () => { /* Success */ },
+ (error) => { console.error("Error:", error.message); }
+);
+```
+
+
+```typescript
+CometChat.markPushNotificationClicked(pushNotification).then(
+ () => { /* Success */ },
+ (error: CometChat.CometChatException) => { console.error("Error:", error.message); }
+);
+```
+
+
+
+### PushNotification Fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `getId()` | string | Announcement ID from the push payload |
+| `getAnnouncementId()` | string | Same as id (for clarity) |
+| `getCampaignId()` | string \| null | Campaign ID if from a campaign |
+| `getSource()` | string | Always `"campaign"` for notification feed pushes |
+
+---
+
+## FeedReadState Enum
+
+| Value | Description |
+| --- | --- |
+| `FeedReadState.READ` | Only read items |
+| `FeedReadState.UNREAD` | Only unread items |
+| `FeedReadState.ALL` | All items (default) |
+
+---
+
+## Rendering Cards
+
+The `content` field of each `NotificationFeedItem` is a Card Schema JSON object. To render it natively, use the CometChat Cards library.
+
+### Add the Cards Dependency
+
+```bash
+npm install @cometchat/cards-react-native
+```
+
+
+If you're using `@cometchat/chat-uikit-react-native` v5.3.6+, the cards library is included automatically as a dependency.
+
+
+### Render a Card from a Feed Item
+
+```tsx lines
+import { CometChatCardView } from "@cometchat/cards-react-native";
+
+function NotificationCard({ item }) {
+ const cardJson = JSON.stringify(item.getContent());
+
+ return (
+ {
+ switch (event.type) {
+ case "openUrl":
+ // Open URL in browser
+ break;
+ case "chatWithUser":
+ // Navigate to chat with event.params.uid
+ break;
+ case "chatWithGroup":
+ // Navigate to group chat with event.params.guid
+ break;
+ }
+ }}
+ />
+ );
+}
+```
+
+
+The Cards library is a pure renderer — it does not execute actions. Your code must handle action callbacks (opening URLs, navigating to chats, making API calls, etc.).
+
+
+---
+
+## Supported Card Actions
+
+When a user taps a button or link inside a card, the action callback receives one of these action types:
+
+| Action Type | Parameters | Description |
+| --- | --- | --- |
+| `openUrl` | url, openIn | Open a URL in browser or webview |
+| `chatWithUser` | uid | Navigate to 1:1 chat |
+| `chatWithGroup` | guid | Navigate to group chat |
+| `sendMessage` | text, receiverUid, receiverGuid | Send a text message |
+| `copyToClipboard` | value | Copy text to clipboard |
+| `downloadFile` | url, filename | Download a file |
+| `initiateCall` | callType (audio/video), uid, guid | Start a call |
+| `apiCall` | url, method, headers, body | Make an HTTP request |
+| `customCallback` | callbackId, payload | App-specific logic |
diff --git a/ui-kit/android/v6/campaigns.mdx b/ui-kit/android/v6/campaigns.mdx
new file mode 100644
index 000000000..7eae80dea
--- /dev/null
+++ b/ui-kit/android/v6/campaigns.mdx
@@ -0,0 +1,232 @@
+---
+title: "Campaigns"
+description: "Deliver targeted, rich notifications to users via an in-app notification feed powered by the CometChat Cards renderer."
+---
+
+CometChat Campaigns enables you to send rich, interactive notifications to users through an in-app notification feed. Each notification is rendered as a native card using the **CometChat Cards** library — supporting images, text, buttons, layouts, and interactive actions.
+
+
+Before proceeding, ensure you have completed the [UI Kit integration](/ui-kit/android/v6/getting-started) and the [Dashboard Setup](/campaigns#setup-flow) for Campaigns.
+
+
+
+
+
+
+---
+
+## Overview
+
+Campaigns delivers notifications as **Card Schema JSON** — a structured format that defines the visual layout of each notification card. The system consists of three layers:
+
+1. **CometChat Chat SDK** — Fetches feed items, manages read/delivered state, provides real-time listeners, handles push notification tracking
+2. **CometChat Cards Library** — Renders Card Schema JSON into native Android views (Jetpack Compose and XML Views)
+3. **CometChat UI Kit** — Provides the ready-to-use `CometChatNotificationFeed` component that wires everything together
+
+### Architecture Flow
+
+```
+Dashboard / API → Campaign Created → Push + WebSocket Delivery
+ ↓
+ SDK: NotificationFeedRequest.fetchNext()
+ ↓
+ NotificationFeedItem.getContent() → Card Schema JSON
+ ↓
+ Cards Library: CometChatCardView / CometChatCardComposable
+ ↓
+ Native Rendered Card (images, text, buttons, layouts)
+ ↓
+ User taps button → ActionCallback → Your code handles it
+```
+
+---
+
+## How Cards Work
+
+Each `NotificationFeedItem` from the SDK contains a `content` field — a `JSONObject` holding the Card Schema JSON. This JSON is passed directly to the CometChat Cards renderer which produces a native view.
+
+The Cards library is a **pure renderer**:
+- **Input**: Card Schema JSON string + theme mode + optional action callback
+- **Output**: Native Android view hierarchy
+
+It does not execute actions, manage message state, or call any SDK methods. When users tap interactive elements (buttons, links), the library emits the action to your callback. You decide what happens — open a URL, navigate to a chat, make an API call, etc.
+
+### Card Schema JSON Example
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ {
+ "type": "column",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "gap": 5,
+ "items": [
+ {
+ "type": "text",
+ "content": "📢 Announcement",
+ "variant": "heading2",
+ "id": "txt_99323141-2459-4e33-88d3-ca39c5fd2f50"
+ },
+ {
+ "type": "text",
+ "content": "Your announcement message here.",
+ "variant": "body",
+ "id": "txt_61a417bc-5e4a-4ba2-bfe7-b7bc64dbaf35"
+ },
+ {
+ "type": "divider",
+ "id": "div_80f5c7fb-fd10-41d1-8c2f-51498f0f62d0"
+ },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "textColor": {
+ "light": "#6C5CE7",
+ "dark": "#6C5CE7"
+ },
+ "size": 40,
+ "fontSize": 13,
+ "borderRadius": 6,
+ "padding": {
+ "top": 0,
+ "right": 16,
+ "bottom": 0,
+ "left": 16
+ },
+ "action": {
+ "type": "openUrl",
+ "url": ""
+ },
+ "id": "btn_9b87a3f1-b0c6-45b9-a4c2-e22ea590f17f"
+ }
+ ],
+ "id": "col_98fed9bd-1a95-4cee-aa81-84a9016e41f2"
+ }
+ ],
+ "fallbackText": "",
+ "style": {
+ "background": {
+ "light": "#E8E8E8",
+ "dark": "#E8E8E8"
+ },
+ "borderRadius": 16,
+ "borderColor": {
+ "light": "#DFE6E9",
+ "dark": "#DFE6E9"
+ },
+ "padding": 12
+ }
+}
+```
+
+The schema supports **20 element types** (text, image, icon, avatar, badge, divider, spacer, chip, progressBar, codeBlock, markdown, row, column, grid, accordion, tabs, button, iconButton, link, table) and **9 action types** (openUrl, chatWithUser, chatWithGroup, sendMessage, copyToClipboard, downloadFile, initiateCall, apiCall, customCallback).
+
+Here's how the above JSON renders as a native card:
+
+
+
+
+
+---
+
+## How Cards Work in the UI Kit
+
+The `CometChatNotificationFeed` component uses the **CometChat Cards** library internally to render each notification. Here's what happens under the hood:
+
+1. The component fetches `NotificationFeedItem` objects from the SDK
+2. For each item, it extracts the `content` field (Card Schema JSON)
+3. It passes the JSON to `CometChatCardComposable` (Compose) or `CometChatCardView` (XML) from the Cards library
+4. The Cards renderer produces native UI — text, images, buttons, layouts — directly from the JSON
+5. When users tap buttons/links inside a card, the action is emitted back to the component which handles navigation (open URL, navigate to chat, etc.)
+
+You don't need to interact with the Cards library directly when using `CometChatNotificationFeed` — it's all wired up. But if you want to render cards outside the feed (e.g., a standalone card in a dialog), you can use the Cards library directly. See the [SDK Campaigns documentation](/sdk/android/v5/campaigns#rendering-cards) for standalone usage.
+
+---
+
+## Handling Push Notifications for Campaigns
+
+When a campaign push notification arrives via FCM, you should:
+
+1. **Report delivery** — Call `CometChat.markPushNotificationDelivered()` in your `FirebaseMessagingService`
+2. **Report click** — Call `CometChat.markPushNotificationClicked()` when the user taps the notification
+3. **Deep link** — Use the announcement ID from the push payload to fetch the full item via `CometChat.getNotificationFeedItem(id)` and display it
+
+```kotlin
+// In FirebaseMessagingService.onMessageReceived()
+val pushNotification = PushNotification.fromJson(data)
+CometChat.markPushNotificationDelivered(pushNotification, ...)
+
+// When user taps the notification
+CometChat.markPushNotificationClicked(pushNotification, ...)
+
+// Navigate to feed or show specific item
+CometChat.getNotificationFeedItem(pushNotification.id, ...)
+```
+
+See the [SDK Campaigns documentation](/sdk/android/v5/campaigns) for the complete push notification tracking API.
+
+---
+
+## Sending Campaigns
+
+Before integrating the frontend, you need to set up channels, categories, templates, and campaigns in the CometChat Dashboard. Follow the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions with screenshots.
+
+---
+
+## Using the UI Kit Component
+
+The easiest way to add a notification feed to your app is the `CometChatNotificationFeed` component. It handles fetching, rendering, pagination, filtering, real-time updates, and engagement reporting out of the box.
+
+
+
+```kotlin
+@Composable
+fun NotificationsScreen() {
+ CometChatNotificationFeed(
+ modifier = Modifier.fillMaxSize(),
+ onItemClick = { item ->
+ // Handle item tap
+ },
+ onBackPress = { /* navigate back */ }
+ )
+}
+```
+
+
+```xml
+
+```
+
+```kotlin
+val feed = findViewById(R.id.notificationFeed)
+feed.init(this) // Pass ViewModelStoreOwner
+feed.onItemClick = { item -> /* handle tap */ }
+```
+
+
+
+See the full [CometChatNotificationFeed component documentation](/ui-kit/android/v6/notification-feed) for all configuration options, styling, and customization.
+
+---
+
+## Next Steps
+
+
+
+ Full API reference for feed items, categories, engagement, and push tracking
+
+
+ Ready-to-use component with filtering, real-time updates, and styling
+
+
diff --git a/ui-kit/android/v6/notification-feed.mdx b/ui-kit/android/v6/notification-feed.mdx
new file mode 100644
index 000000000..c876dc303
--- /dev/null
+++ b/ui-kit/android/v6/notification-feed.mdx
@@ -0,0 +1,478 @@
+---
+title: "Notification Feed"
+description: "Full-screen notification feed component with category filtering, card rendering, real-time updates, and engagement reporting."
+---
+
+`CometChatNotificationFeed` displays a scrollable notification feed where each item is rendered as a native card using the CometChat Cards library. It handles fetching, pagination, category filtering, timestamp grouping, real-time updates, and read/delivered/engagement reporting automatically.
+
+
+
+
+
+---
+
+## Where It Fits
+
+`CometChatNotificationFeed` is a full-screen component. Drop it into an Activity, Fragment, or navigation destination. It manages its own data fetching, state, and real-time listeners — you just handle navigation callbacks.
+
+
+
+```kotlin
+@Composable
+fun NotificationsScreen(onBack: () -> Unit) {
+ CometChatNotificationFeed(
+ modifier = Modifier.fillMaxSize(),
+ showBackButton = true,
+ onBackPress = onBack,
+ onItemClick = { item ->
+ // Handle item tap (e.g., open detail or deep link)
+ }
+ )
+}
+```
+
+
+```xml
+
+```
+
+```kotlin
+val feed = findViewById(R.id.notificationFeed)
+feed.init(this) // Pass Activity or Fragment — needed to create the ViewModel
+feed.onItemClick = { item -> /* navigate */ }
+feed.onBackPress = { finish() }
+```
+
+
+
+---
+
+## Quick Start
+
+
+
+```kotlin
+@Composable
+fun NotificationsScreen() {
+ CometChatNotificationFeed(
+ modifier = Modifier.fillMaxSize()
+ )
+}
+```
+
+
+```kotlin
+override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val feed = CometChatNotificationFeed(this)
+ setContentView(feed)
+ feed.init(this) // Required — binds the ViewModel to this Activity's lifecycle
+}
+```
+
+
+
+Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()` and a user logged in.
+
+---
+
+## Filtering Feed Items
+
+Control what loads using custom request builders:
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ feedRequestBuilder = NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .setLimit(30)
+ .setReadState(FeedReadState.UNREAD)
+ .setCategory("promotions")
+)
+```
+
+
+```kotlin
+feed.setFeedRequestBuilder(
+ NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .setLimit(30)
+ .setReadState(FeedReadState.UNREAD)
+ .setCategory("promotions")
+)
+feed.init(this)
+```
+
+
+
+### Filter Options
+
+| Builder Method | Description |
+| --- | --- |
+| `.setLimit(int)` | Items per page (default 20, max 100) |
+| `.setReadState(FeedReadState)` | `READ`, `UNREAD`, or `ALL` |
+| `.setCategory(String)` | Filter by category ID |
+| `.setChannelId(String)` | Filter by channel |
+| `.setTags(List)` | Filter by tags |
+| `.setDateFrom(String)` | ISO 8601 date lower bound |
+| `.setDateTo(String)` | ISO 8601 date upper bound |
+
+
+Pass the builder object, not the result of `.build()`. The component calls `.build()` internally.
+
+
+---
+
+## Actions and Events
+
+### Callback Methods
+
+#### `onItemClick`
+
+Fires when a feed item card is tapped.
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ onItemClick = { item ->
+ // item.id, item.content (Card JSON), item.category
+ }
+)
+```
+
+
+```kotlin
+feed.onItemClick = { item ->
+ // Handle item tap
+}
+```
+
+
+
+#### `onActionClick`
+
+Fires when an interactive element (button, link) inside a card is tapped.
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ onActionClick = { item, actionMap ->
+ // actionMap contains action type and parameters
+ val actionType = actionMap["type"] as? String
+ when (actionType) {
+ "openUrl" -> openBrowser(actionMap["url"] as String)
+ "chatWithUser" -> navigateToChat(actionMap["uid"] as String)
+ }
+ }
+)
+```
+
+
+```kotlin
+feed.onActionClick = { item, actionMap ->
+ val actionType = actionMap["type"] as? String
+ // Handle action
+}
+```
+
+
+
+#### `onError`
+
+Fires when an internal error occurs (network failure, SDK exception).
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ onError = { exception ->
+ Log.e("Feed", "Error: ${exception.message}")
+ }
+)
+```
+
+
+```kotlin
+feed.onError = { exception ->
+ Log.e("Feed", "Error: ${exception.message}")
+}
+```
+
+
+
+#### `onBackPress`
+
+Fires when the back button in the header is tapped.
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ showBackButton = true,
+ onBackPress = { /* navigate back */ }
+)
+```
+
+
+```kotlin
+feed.setShowBackButton(true)
+feed.onBackPress = { finish() }
+```
+
+
+
+### Automatic Behaviors
+
+The component handles these automatically — no manual setup needed:
+
+| Behavior | Description |
+| --- | --- |
+| Real-time updates | New items appear at the top via WebSocket listener |
+| Delivery reporting | Items are reported as delivered when fetched |
+| Read reporting | Items are reported as read after 1 second of visibility |
+| Unread count polling | Polls unread count every 30 seconds to update badges |
+| Infinite scroll | Fetches next page when scrolling near the bottom |
+| Pull-to-refresh | Resets and fetches fresh data on pull |
+| Timestamp grouping | Groups items as "Today", "Yesterday", day name, or date |
+| Category filtering | Filter chips row for category-based filtering |
+
+---
+
+## Functionality
+
+| Method (XML Views) | Compose Parameter | Description |
+| --- | --- | --- |
+| `setTitle("Notifications")` | `title = "Notifications"` | Header title text |
+| `setShowHeader(true)` | `showHeader = true` | Toggle header visibility |
+| `setShowBackButton(false)` | `showBackButton = false` | Toggle back button |
+| `setShowFilterChips(true)` | `showFilterChips = true` | Toggle category filter chips |
+| `setFeedRequestBuilder(...)` | `feedRequestBuilder = ...` | Custom feed request |
+| `setCategoriesRequestBuilder(...)` | `categoriesRequestBuilder = ...` | Custom categories request |
+
+---
+
+## Custom View Slots
+
+### Header View
+
+Replace the entire header:
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ headerView = {
+ Row(
+ modifier = Modifier.fillMaxWidth().padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text("My Notifications", style = CometChatTheme.typography.heading1Bold)
+ }
+ }
+)
+```
+
+
+
+### State Views
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ emptyStateView = {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text("No notifications yet")
+ }
+ },
+ errorStateView = { exception, onRetry ->
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Text("Something went wrong")
+ Button(onClick = onRetry) { Text("Retry") }
+ }
+ },
+ loadingStateView = {
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ CircularProgressIndicator()
+ }
+ }
+)
+```
+
+
+
+---
+
+## Style
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ style = CometChatNotificationFeedStyle(
+ backgroundColor = Color(0xFFF5F5F5),
+ headerBackgroundColor = Color.White,
+ headerTitleColor = Color(0xFF141414),
+ chipActiveBackgroundColor = Color(0xFF3399FF),
+ chipActiveTextColor = Color.White,
+ chipInactiveBackgroundColor = Color.White,
+ chipInactiveTextColor = Color(0xFF727272),
+ cardBackgroundColor = Color.White,
+ cardBorderColor = Color(0xFFE0E0E0),
+ cardBorderRadius = 12.dp,
+ unreadIndicatorColor = Color(0xFF3399FF)
+ )
+)
+```
+
+
+```kotlin
+feed.setStyle(CometChatNotificationFeedStyle(
+ backgroundColor = Color.parseColor("#F5F5F5"),
+ headerTitleColor = Color.parseColor("#141414"),
+ chipActiveBackgroundColor = Color.parseColor("#3399FF"),
+ chipActiveTextColor = Color.WHITE,
+ chipInactiveBackgroundColor = Color.TRANSPARENT,
+ chipInactiveTextColor = Color.DKGRAY,
+ cardBackgroundColor = Color.WHITE,
+ cardBorderColor = Color.parseColor("#E0E0E0"),
+ unreadIndicatorColor = Color.parseColor("#3399FF")
+))
+```
+
+
+
+### Style Properties
+
+| Property | Description |
+| --- | --- |
+| `backgroundColor` | Screen background color |
+| `headerBackgroundColor` | Header bar background |
+| `headerTitleColor` | Header title text color |
+| `headerBorderColor` | Divider below header |
+| `chipActiveBackgroundColor` | Selected filter chip background |
+| `chipActiveTextColor` | Selected filter chip text |
+| `chipInactiveBackgroundColor` | Unselected filter chip background |
+| `chipInactiveTextColor` | Unselected filter chip text |
+| `chipBorderColor` | Filter chip border |
+| `badgeActiveBackgroundColor` | Active chip badge background |
+| `badgeActiveTextColor` | Active chip badge text |
+| `badgeInactiveBackgroundColor` | Inactive chip badge background |
+| `badgeInactiveTextColor` | Inactive chip badge text |
+| `timestampTextColor` | Section header timestamp color |
+| `cardBackgroundColor` | Card container background |
+| `cardBorderColor` | Card container border |
+| `cardBorderRadius` | Card corner radius |
+| `cardBorderWidth` | Card border width |
+| `unreadIndicatorColor` | Unread dot indicator color |
+| `separatorColor` | Separator between cards |
+
+All colors default to `Color.Unspecified` (Compose) or `0` (XML) to inherit from `CometChatTheme`. Override individual values without losing theme support.
+
+---
+
+## ViewModel Access
+
+The component uses `CometChatNotificationFeedViewModel` from the shared `chatuikit-core` module. You can provide a custom ViewModel for advanced scenarios:
+
+
+
+```kotlin
+val viewModel: CometChatNotificationFeedViewModel = viewModel(
+ factory = CometChatNotificationFeedViewModelFactory(
+ feedRequestBuilder = customBuilder,
+ pollingIntervalMs = 60_000L // 1 minute polling
+ )
+)
+
+CometChatNotificationFeed(
+ viewModel = viewModel
+)
+```
+
+
+
+### ViewModel Factory Parameters
+
+| Parameter | Default | Description |
+| --- | --- | --- |
+| `feedRequestBuilder` | null | Custom feed request builder |
+| `categoriesRequestBuilder` | null | Custom categories request builder |
+| `enableListeners` | true | Enable WebSocket listeners (false for testing) |
+| `pollingIntervalMs` | 30000 | Unread count polling interval in ms |
+
+---
+
+## Common Patterns
+
+### Show only unread items
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ feedRequestBuilder = NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .setReadState(FeedReadState.UNREAD)
+)
+```
+
+
+
+### Hide filter chips and header
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ showHeader = false,
+ showFilterChips = false
+)
+```
+
+
+```kotlin
+feed.setShowHeader(false)
+feed.setShowFilterChips(false)
+```
+
+
+
+### Custom categories request
+
+
+
+```kotlin
+CometChatNotificationFeed(
+ categoriesRequestBuilder = NotificationCategoriesRequest.NotificationCategoriesRequestBuilder()
+ .setLimit(10)
+)
+```
+
+
+
+---
+
+## Next Steps
+
+
+
+ Overview of how campaigns work end-to-end
+
+
+ Low-level SDK APIs for feed items, categories, and engagement
+
+
+ Full styling reference for all components
+
+
+ Custom ViewModels, repositories, and ListOperations
+
+
diff --git a/ui-kit/flutter/campaigns.mdx b/ui-kit/flutter/campaigns.mdx
new file mode 100644
index 000000000..ab5f864c2
--- /dev/null
+++ b/ui-kit/flutter/campaigns.mdx
@@ -0,0 +1,232 @@
+---
+title: "Campaigns"
+description: "Deliver targeted, rich notifications to users via an in-app notification feed powered by the CometChat Cards renderer."
+---
+
+CometChat Campaigns enables you to send rich, interactive notifications to users through an in-app notification feed. Each notification is rendered as a native card using the **CometChat Cards** library — supporting images, text, buttons, layouts, and interactive actions.
+
+
+Before proceeding, ensure you have completed the [UI Kit integration](/ui-kit/flutter/getting-started) and the [Dashboard Setup](/campaigns#setup-flow) for Campaigns.
+
+
+
+
+
+
+---
+
+## Overview
+
+Campaigns delivers notifications as **Card Schema JSON** — a structured format that defines the visual layout of each notification card. The system consists of three layers:
+
+1. **CometChat Chat SDK** — Fetches feed items, manages read/delivered state, provides real-time listeners, handles push notification tracking
+2. **CometChat Cards Library** — Renders Card Schema JSON into native Flutter widgets
+3. **CometChat UI Kit** — Provides the ready-to-use `CometChatNotificationFeed` component that wires everything together
+
+### Architecture Flow
+
+```
+Dashboard / API → Campaign Created → Push + WebSocket Delivery
+ ↓
+ SDK: NotificationFeedRequest.fetchNext()
+ ↓
+ NotificationFeedItem.content → Card Schema JSON
+ ↓
+ Cards Library: CometChatCardView
+ ↓
+ Native Rendered Card (images, text, buttons, layouts)
+ ↓
+ User taps button → ActionCallback → Your code handles it
+```
+
+---
+
+## How Cards Work
+
+Each `NotificationFeedItem` from the SDK contains a `content` field — a `Map` holding the Card Schema JSON. This JSON is passed directly to the CometChat Cards renderer which produces a native Flutter widget.
+
+The Cards library is a **pure renderer**:
+- **Input**: Card Schema JSON string + theme mode + optional action callback
+- **Output**: Flutter widget tree
+
+It does not execute actions, manage message state, or call any SDK methods. When users tap interactive elements (buttons, links), the library emits the action to your callback. You decide what happens — open a URL, navigate to a chat, make an API call, etc.
+
+### Card Schema JSON Example
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ {
+ "type": "column",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "gap": 5,
+ "items": [
+ {
+ "type": "text",
+ "content": "📢 Announcement",
+ "variant": "heading2",
+ "id": "txt_99323141-2459-4e33-88d3-ca39c5fd2f50"
+ },
+ {
+ "type": "text",
+ "content": "Your announcement message here.",
+ "variant": "body",
+ "id": "txt_61a417bc-5e4a-4ba2-bfe7-b7bc64dbaf35"
+ },
+ {
+ "type": "divider",
+ "id": "div_80f5c7fb-fd10-41d1-8c2f-51498f0f62d0"
+ },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "textColor": {
+ "light": "#6C5CE7",
+ "dark": "#6C5CE7"
+ },
+ "size": 40,
+ "fontSize": 13,
+ "borderRadius": 6,
+ "padding": {
+ "top": 0,
+ "right": 16,
+ "bottom": 0,
+ "left": 16
+ },
+ "action": {
+ "type": "openUrl",
+ "url": ""
+ },
+ "id": "btn_9b87a3f1-b0c6-45b9-a4c2-e22ea590f17f"
+ }
+ ],
+ "id": "col_98fed9bd-1a95-4cee-aa81-84a9016e41f2"
+ }
+ ],
+ "fallbackText": "",
+ "style": {
+ "background": {
+ "light": "#E8E8E8",
+ "dark": "#E8E8E8"
+ },
+ "borderRadius": 16,
+ "borderColor": {
+ "light": "#DFE6E9",
+ "dark": "#DFE6E9"
+ },
+ "padding": 12
+ }
+}
+```
+
+The schema supports **20 element types** (text, image, icon, avatar, badge, divider, spacer, chip, progressBar, codeBlock, markdown, row, column, grid, accordion, tabs, button, iconButton, link, table) and **9 action types** (openUrl, chatWithUser, chatWithGroup, sendMessage, copyToClipboard, downloadFile, initiateCall, apiCall, customCallback).
+
+Here's how the above JSON renders as a native card:
+
+
+
+
+
+---
+
+## How Cards Work in the UI Kit
+
+The `CometChatNotificationFeed` component uses the **CometChat Cards** library internally to render each notification. Here's what happens under the hood:
+
+1. The component fetches `NotificationFeedItem` objects from the SDK
+2. For each item, it extracts the `content` field (Card Schema JSON)
+3. It passes the JSON to `CometChatCardView` from the Cards library
+4. The Cards renderer produces native Flutter widgets — text, images, buttons, layouts — directly from the JSON
+5. When users tap buttons/links inside a card, the action is emitted back to the component which handles navigation (open URL, navigate to chat, etc.)
+
+You don't need to interact with the Cards library directly when using `CometChatNotificationFeed` — it's all wired up. But if you want to render cards outside the feed (e.g., a standalone card in a dialog), you can use the Cards library directly. See the [SDK Campaigns documentation](/sdk/flutter/campaigns#rendering-cards) for standalone usage.
+
+---
+
+## Handling Push Notifications for Campaigns
+
+When a campaign push notification arrives via FCM/APNs, you should:
+
+1. **Report delivery** — Call `CometChat.markPushNotificationDelivered()` in your push notification handler
+2. **Report click** — Call `CometChat.markPushNotificationClicked()` when the user taps the notification
+3. **Deep link** — Use the announcement ID from the push payload to fetch the full item via `CometChat.getNotificationFeedItem(id)` and display it
+
+```dart
+// When push notification is received
+final pushNotification = PushNotification(
+ id: payload['announcementId'],
+ announcementId: payload['announcementId'],
+ campaignId: payload['campaignId'],
+ source: "campaign",
+);
+CometChat.markPushNotificationDelivered(pushNotification, onSuccess: (_) {}, onError: (_) {});
+
+// When user taps the notification
+CometChat.markPushNotificationClicked(pushNotification, onSuccess: (_) {}, onError: (_) {});
+
+// Navigate to feed or show specific item
+CometChat.getNotificationFeedItem(
+ pushNotification.id,
+ onSuccess: (item) { /* render card */ },
+ onError: (e) { /* handle error */ },
+);
+```
+
+See the [SDK Campaigns documentation](/sdk/flutter/campaigns) for the complete push notification tracking API.
+
+---
+
+## Sending Campaigns
+
+Before integrating the frontend, you need to set up channels, categories, templates, and campaigns in the CometChat Dashboard. Follow the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions with screenshots.
+
+---
+
+## Using the UI Kit Component
+
+The easiest way to add a notification feed to your app is the `CometChatNotificationFeed` component. It handles fetching, rendering, pagination, filtering, real-time updates, and engagement reporting out of the box.
+
+
+
+```dart
+import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';
+
+class NotificationsScreen extends StatelessWidget {
+ const NotificationsScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return CometChatNotificationFeed(
+ onItemClick: (NotificationFeedItem item) {
+ // Handle item tap
+ },
+ onBackPress: () => Navigator.of(context).pop(),
+ );
+ }
+}
+```
+
+
+
+See the full [CometChatNotificationFeed component documentation](/ui-kit/flutter/notification-feed) for all configuration options, styling, and customization.
+
+---
+
+## Next Steps
+
+
+
+ Full API reference for feed items, categories, engagement, and push tracking
+
+
+ Ready-to-use component with filtering, real-time updates, and styling
+
+
diff --git a/ui-kit/flutter/notification-feed.mdx b/ui-kit/flutter/notification-feed.mdx
new file mode 100644
index 000000000..8705df47c
--- /dev/null
+++ b/ui-kit/flutter/notification-feed.mdx
@@ -0,0 +1,409 @@
+---
+title: "Notification Feed"
+description: "Full-screen notification feed component with category filtering, card rendering, real-time updates, and engagement reporting."
+---
+
+`CometChatNotificationFeed` displays a scrollable notification feed where each item is rendered as a native card using the CometChat Cards library. It handles fetching, pagination, category filtering, timestamp grouping, real-time updates, and read/delivered/engagement reporting automatically.
+
+
+
+
+
+---
+
+## Where It Fits
+
+`CometChatNotificationFeed` is a full-screen component. Drop it into a route or screen. It manages its own data fetching, state, and real-time listeners — you just handle navigation callbacks.
+
+
+
+```dart
+import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';
+
+class NotificationsScreen extends StatelessWidget {
+ const NotificationsScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return CometChatNotificationFeed(
+ showBackButton: true,
+ onBackPress: () => Navigator.of(context).pop(),
+ onItemClick: (NotificationFeedItem item) {
+ // Handle item tap (e.g., open detail or deep link)
+ },
+ );
+ }
+}
+```
+
+
+
+---
+
+## Quick Start
+
+
+
+```dart
+import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';
+
+@override
+Widget build(BuildContext context) {
+ return const CometChatNotificationFeed();
+}
+```
+
+
+
+Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()` and a user logged in.
+
+---
+
+## Filtering Feed Items
+
+Control what loads using custom request builders:
+
+
+
+```dart
+CometChatNotificationFeed(
+ notificationFeedRequestBuilder: NotificationFeedRequestBuilder()
+ ..setLimit(30)
+ ..setReadState(FeedReadState.unread)
+ ..setCategory("promotions"),
+)
+```
+
+
+
+### Filter Options
+
+| Builder Method | Description |
+| --- | --- |
+| `.setLimit(int)` | Items per page (default 20, max 100) |
+| `.setReadState(FeedReadState)` | `read`, `unread`, or `all` |
+| `.setCategory(String)` | Filter by category ID |
+| `.setChannelId(String)` | Filter by channel |
+| `.setTags(List)` | Filter by tags |
+| `.setDateFrom(String)` | ISO 8601 date lower bound |
+| `.setDateTo(String)` | ISO 8601 date upper bound |
+
+
+Pass the builder object (without calling `.build()`). The component calls `.build()` internally.
+
+
+---
+
+## Actions and Events
+
+### Callback Methods
+
+#### `onItemClick`
+
+Fires when a feed item card is tapped.
+
+
+
+```dart
+CometChatNotificationFeed(
+ onItemClick: (NotificationFeedItem item) {
+ // item.id, item.content (Card JSON), item.category
+ },
+)
+```
+
+
+
+#### `onActionClick`
+
+Fires when an interactive element (button, link) inside a card is tapped.
+
+
+
+```dart
+CometChatNotificationFeed(
+ onActionClick: (NotificationFeedItem item, CometChatCardActionEvent action) {
+ if (action.action is CometChatCardOpenUrlAction) {
+ // Open URL in browser
+ } else if (action.action is CometChatCardChatWithUserAction) {
+ // Navigate to chat
+ }
+ },
+)
+```
+
+
+
+#### `onError`
+
+Fires when an internal error occurs (network failure, SDK exception).
+
+
+
+```dart
+CometChatNotificationFeed(
+ onError: (String error) {
+ debugPrint("Feed error: $error");
+ },
+)
+```
+
+
+
+#### `onBackPress`
+
+Fires when the back button in the header is tapped.
+
+
+
+```dart
+CometChatNotificationFeed(
+ showBackButton: true,
+ onBackPress: () => Navigator.of(context).pop(),
+)
+```
+
+
+
+### Automatic Behaviors
+
+The component handles these automatically — no manual setup needed:
+
+| Behavior | Description |
+| --- | --- |
+| Real-time updates | New items appear at the top via WebSocket listener |
+| Delivery reporting | Items are reported as delivered when fetched |
+| Read reporting | Items are reported as read after 1 second of visibility |
+| Infinite scroll | Fetches next page when scrolling near the bottom |
+| Pull-to-refresh | Resets and fetches fresh data on pull |
+| Timestamp grouping | Groups items as "Today", "Yesterday", day name, or date |
+| Category filtering | Filter chips row for category-based filtering |
+
+---
+
+## Properties
+
+| Property | Type | Default | Description |
+| --- | --- | --- | --- |
+| `title` | String | `"Notifications"` | Header title text |
+| `showHeader` | bool | `true` | Toggle header visibility |
+| `showBackButton` | bool | `false` | Toggle back button |
+| `showFilterChips` | bool | `true` | Toggle category filter chips |
+| `headerView` | Widget? | null | Custom header widget |
+| `scrollToItemId` | String? | null | Deep link to a specific item |
+| `notificationFeedRequestBuilder` | NotificationFeedRequestBuilder? | null | Custom feed request |
+| `notificationCategoriesRequestBuilder` | NotificationCategoriesRequestBuilder? | null | Custom categories request |
+| `cardThemeMode` | CometChatCardThemeMode? | null | Card renderer theme mode |
+| `cardThemeOverride` | CometChatCardThemeOverride? | null | Card renderer theme override |
+
+---
+
+## Custom View Slots
+
+### Header View
+
+Replace the entire header:
+
+
+
+```dart
+CometChatNotificationFeed(
+ headerView: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ children: [
+ Text(
+ "My Notifications",
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ ],
+ ),
+ ),
+)
+```
+
+
+
+### State Views
+
+
+
+```dart
+CometChatNotificationFeed(
+ emptyStateView: Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(Icons.notifications_off, size: 64, color: Colors.grey),
+ SizedBox(height: 16),
+ Text("No notifications yet"),
+ ],
+ ),
+ ),
+ errorStateView: Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text("Something went wrong"),
+ ElevatedButton(
+ onPressed: () { /* retry logic */ },
+ child: Text("Retry"),
+ ),
+ ],
+ ),
+ ),
+ loadingStateView: const Center(
+ child: CircularProgressIndicator(),
+ ),
+)
+```
+
+
+
+---
+
+## Style
+
+
+
+```dart
+CometChatNotificationFeed(
+ style: CometChatNotificationFeedStyle(
+ backgroundColor: Color(0xFFF5F5F5),
+ headerTitleColor: Color(0xFF141414),
+ chipActiveBackgroundColor: Color(0xFF3399FF),
+ chipActiveTextColor: Colors.white,
+ chipInactiveBackgroundColor: Colors.transparent,
+ chipInactiveTextColor: Color(0xFF727272),
+ chipBorderColor: Color(0xFFE0E0E0),
+ cardBackgroundColor: Colors.white,
+ cardBorderColor: Color(0xFFE0E0E0),
+ cardBorderRadius: 12,
+ unreadIndicatorColor: Color(0xFF3399FF),
+ ),
+)
+```
+
+
+
+### Style Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| `backgroundColor` | Color? | Screen background color |
+| `headerTitleColor` | Color? | Header title text color |
+| `headerTitleTextStyle` | TextStyle? | Header title text style |
+| `backIconColor` | Color? | Back button icon color |
+| `chipActiveBackgroundColor` | Color? | Selected filter chip background |
+| `chipActiveTextColor` | Color? | Selected filter chip text |
+| `chipInactiveBackgroundColor` | Color? | Unselected filter chip background |
+| `chipInactiveTextColor` | Color? | Unselected filter chip text |
+| `chipBorderColor` | Color? | Filter chip border |
+| `chipTextStyle` | TextStyle? | Filter chip text style |
+| `badgeBackgroundColor` | Color? | Badge background |
+| `badgeTextColor` | Color? | Badge text |
+| `badgeTextStyle` | TextStyle? | Badge text style |
+| `timestampTextColor` | Color? | Item timestamp color |
+| `timestampTextStyle` | TextStyle? | Item timestamp style |
+| `timestampHeaderTextStyle` | TextStyle? | Section header timestamp style |
+| `timestampHeaderTextColor` | Color? | Section header timestamp color |
+| `cardBackgroundColor` | Color? | Card container background |
+| `cardBorderColor` | Color? | Card container border |
+| `cardBorderRadius` | double? | Card corner radius |
+| `cardBorderWidth` | double? | Card border width |
+| `unreadIndicatorColor` | Color? | Unread dot indicator color |
+| `separatorColor` | Color? | Separator between cards |
+
+All colors default to `null` to inherit from `CometChatTheme`. Override individual values without losing theme support.
+
+---
+
+## Deep Linking
+
+Navigate directly to a specific feed item using `scrollToItemId`:
+
+
+
+```dart
+CometChatNotificationFeed(
+ scrollToItemId: "announcement-id-from-push",
+)
+```
+
+
+
+If the item is already loaded, the feed scrolls to it. If not, it fetches the item by ID and inserts it at the top.
+
+---
+
+## Common Patterns
+
+### Show only unread items
+
+
+
+```dart
+CometChatNotificationFeed(
+ notificationFeedRequestBuilder: NotificationFeedRequestBuilder()
+ ..setReadState(FeedReadState.unread),
+)
+```
+
+
+
+### Hide filter chips and header
+
+
+
+```dart
+CometChatNotificationFeed(
+ showHeader: false,
+ showFilterChips: false,
+)
+```
+
+
+
+### Custom categories request
+
+
+
+```dart
+CometChatNotificationFeed(
+ notificationCategoriesRequestBuilder: NotificationCategoriesRequestBuilder()
+ ..setLimit(10),
+)
+```
+
+
+
+### Card theme mode override
+
+
+
+```dart
+CometChatNotificationFeed(
+ cardThemeMode: CometChatCardThemeMode.dark,
+)
+```
+
+
+
+---
+
+## Next Steps
+
+
+
+ Overview of how campaigns work end-to-end
+
+
+ Low-level SDK APIs for feed items, categories, and engagement
+
+
+ Full styling reference for all components
+
+
+ Custom BLoCs, repositories, and data sources
+
+
diff --git a/ui-kit/flutter/v5/getting-started.mdx b/ui-kit/flutter/v5/getting-started.mdx
index 96032dc2b..2b9496dcf 100644
--- a/ui-kit/flutter/v5/getting-started.mdx
+++ b/ui-kit/flutter/v5/getting-started.mdx
@@ -86,8 +86,8 @@ Add to your `pubspec.yaml`:
dependencies:
flutter:
sdk: flutter
- cometchat_chat_uikit: ^5.2.14
- cometchat_calls_uikit: ^5.0.15 # Optional: for voice/video calling
+ cometchat_chat_uikit: ^5.2.16
+ cometchat_calls_uikit: ^5.0.16 # Optional: for voice/video calling
```
Then run:
diff --git a/ui-kit/ios/campaigns.mdx b/ui-kit/ios/campaigns.mdx
new file mode 100644
index 000000000..1ba5268ee
--- /dev/null
+++ b/ui-kit/ios/campaigns.mdx
@@ -0,0 +1,179 @@
+---
+title: "Campaigns"
+description: "Deliver targeted, rich notifications to users via an in-app notification feed powered by the CometChat Cards renderer."
+---
+
+CometChat Campaigns enables you to send rich, interactive notifications to users through an in-app notification feed. Each notification is rendered as a native card using the CometChat Cards library — supporting images, text, buttons, layouts, and interactive actions.
+
+
+Before proceeding, ensure you have completed the [UI Kit integration](/ui-kit/ios/getting-started) and the [Dashboard Setup](/campaigns#setup-flow) for Campaigns.
+
+
+---
+
+## Overview
+
+Campaigns delivers notifications as Card Schema JSON — a structured format that defines the visual layout of each notification card. The system consists of three layers:
+
+- **CometChat Chat SDK** — Fetches feed items, manages read/delivered state, provides real-time listeners, handles push notification tracking
+- **CometChat Cards Library** — Renders Card Schema JSON into native UIKit views
+- **CometChat UI Kit** — Provides the ready-to-use `CometChatNotificationFeed` component that wires everything together
+
+---
+
+## Sending Campaigns
+
+Before integrating the frontend, you need to set up channels, categories, templates, and campaigns in the CometChat Dashboard. Follow the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions with screenshots.
+
+---
+
+## How Cards Work
+
+Each `NotificationFeedItem` from the SDK contains a `content` field — a `[String: Any]` dictionary holding the Card Schema JSON. This JSON is passed directly to the CometChat Cards renderer which produces a native UIKit view.
+
+The Cards library is a pure renderer:
+
+- **Input:** Card Schema JSON string + theme mode + optional action callback
+- **Output:** Native UIKit view hierarchy
+
+It does not execute actions, manage message state, or call any SDK methods. When users tap interactive elements (buttons, links), the library emits the action to your callback. You decide what happens — open a URL, navigate to a chat, make an API call, etc.
+
+---
+
+## Card Schema JSON Example
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ { "type": "image", "id": "img_1", "url": "https://cdn.example.com/sale.jpg", "height": 180, "fit": "cover", "borderRadius": 8 },
+ { "type": "text", "id": "txt_1", "content": "🎉 Flash Sale — 40% Off!", "variant": "heading2" },
+ { "type": "text", "id": "txt_2", "content": "Limited time offer on all premium plans.", "variant": "body" },
+ { "type": "button", "id": "btn_1", "label": "Claim Offer", "action": { "type": "openUrl", "url": "https://example.com/offer" }, "fullWidth": true }
+ ],
+ "style": { "background": {"light": "#FFFFFF", "dark": "#1E1E1E"}, "borderRadius": 12, "padding": 16 },
+ "fallbackText": "Flash Sale — 40% Off! Claim your offer: https://example.com/offer"
+}
+```
+
+The schema supports 20 element types (text, image, icon, avatar, badge, divider, spacer, chip, progressBar, codeBlock, markdown, row, column, grid, accordion, tabs, button, iconButton, link, table) and 9 action types (openUrl, chatWithUser, chatWithGroup, sendMessage, copyToClipboard, downloadFile, initiateCall, apiCall, customCallback).
+
+---
+
+## Notification Feed Component
+
+The `CometChatNotificationFeed` component renders campaign notifications as a full-screen scrollable feed. It handles fetching, card rendering, pagination, filtering, real-time updates, and engagement reporting automatically.
+
+
+
+```swift
+let notificationFeed = CometChatNotificationFeed()
+let navController = UINavigationController(rootViewController: notificationFeed)
+self.present(navController, animated: true)
+```
+
+
+
+See the full [CometChatNotificationFeed](/ui-kit/ios/notification-feed) documentation for all configuration options, actions, filtering, and customization.
+
+---
+
+## Setting Up Notification Service Extension
+
+To track push notification delivery for campaigns, you need a Notification Service Extension. This runs when a push arrives on the device (even if the app is in the background).
+
+### Step 1: Create the Extension
+
+1. In Xcode, go to **File → New → Target**.
+2. Select **Notification Service Extension**.
+3. Name it (e.g., `NotificationService`).
+4. Click **Finish**.
+
+### Step 2: Configure App Groups
+
+Both your main app and the extension need to share data via an App Group:
+
+1. Select your **main app target** → Signing & Capabilities → **+ Capability** → **App Groups**.
+2. Add a group (e.g., `group.com.yourapp.cometchat`).
+3. Select your **extension target** → repeat the same steps with the same group ID.
+
+### Step 3: Add CometChatSDK to the Extension
+
+Add `CometChatSDK` as a dependency to the extension target (via SPM or CocoaPods).
+
+### Step 4: Implement the Extension
+
+
+
+```swift
+import UserNotifications
+import CometChatSDK
+
+class NotificationService: UNNotificationServiceExtension {
+ override func didReceive(_ request: UNNotificationRequest,
+ withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
+ let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)!
+
+ // Share auth state via App Group
+ CometChat.setExtensionGroupID(id: "group.com.yourapp.cometchat")
+
+ let userInfo = bestAttemptContent.userInfo
+ if let cometchat = userInfo["cometchat"] as? [String: Any],
+ let type = cometchat["type"] as? String,
+ type == "business_messaging" {
+ let push = PushNotification.fromPayload(cometchat)
+ CometChat.markPushNotificationDelivered(push, onSuccess: {}, onError: { _ in })
+ }
+
+ contentHandler(bestAttemptContent)
+ }
+}
+```
+
+
+
+---
+
+## Handling Push Notification Clicks
+
+When the user taps a campaign push notification, report the click and navigate to the feed:
+
+
+
+```swift
+func userNotificationCenter(_ center: UNUserNotificationCenter,
+ didReceive response: UNNotificationResponse,
+ withCompletionHandler completionHandler: @escaping () -> Void) {
+ let userInfo = response.notification.request.content.userInfo
+
+ if let cometchat = userInfo["cometchat"] as? [String: Any],
+ let type = cometchat["type"] as? String,
+ type == "business_messaging" {
+ let push = PushNotification.fromPayload(cometchat)
+ CometChat.markPushNotificationClicked(push, onSuccess: {}, onError: { _ in })
+
+ // Navigate to notification feed
+ let notificationFeed = CometChatNotificationFeed()
+ let navController = UINavigationController(rootViewController: notificationFeed)
+ window?.rootViewController?.present(navController, animated: true)
+
+ completionHandler()
+ return
+ }
+
+ completionHandler()
+}
+```
+
+
+
+See the [SDK Campaigns documentation](/sdk/ios/campaigns) for the complete push notification tracking API.
+
+---
+
+## Managing Campaigns
+
+Campaigns are created and managed from the CometChat Dashboard or via the REST API. The SDK and UI Kit are consumer-side — they display and interact with campaigns, not create them.
+
+- **Dashboard:** Navigate to Campaigns → follow the flow above (Channel → Category → Template → Bubble Builder → Send).
+- **REST API:** Use the Campaigns API to programmatically create and schedule campaigns.
diff --git a/ui-kit/ios/notification-feed.mdx b/ui-kit/ios/notification-feed.mdx
new file mode 100644
index 000000000..923461846
--- /dev/null
+++ b/ui-kit/ios/notification-feed.mdx
@@ -0,0 +1,288 @@
+---
+title: "Notification Feed"
+description: "Full-screen notification feed component with category filtering, card rendering, real-time updates, and engagement reporting."
+---
+
+`CometChatNotificationFeed` displays a scrollable notification feed where each item is rendered as a native card using the CometChat Cards library. It handles fetching, pagination, category filtering, real-time updates, and read/delivered/engagement reporting automatically. Present it in a `UINavigationController`, push it onto an existing navigation stack, or embed it in a tab bar.
+
+Each notification gets its own section header with the category label on the left and a relative timestamp on the right:
+
+| Condition | Display |
+| --------- | ------- |
+| Today (< 24 hours ago) | Time only — 2:35 PM |
+| Yesterday (24–48 hours ago) | Yesterday |
+| This week (2–7 days ago) | Day name — Monday, Tuesday, etc. |
+| Older (> 7 days) | Full date — 25/05/2026 |
+
+
+
+```swift
+let notificationFeed = CometChatNotificationFeed()
+notificationFeed.set(onItemClick: { feedItem in
+ // Handle item tap (e.g., open detail or deep link)
+})
+
+let navController = UINavigationController(rootViewController: notificationFeed)
+self.present(navController, animated: true)
+```
+
+
+
+---
+
+## Quick Start
+
+
+
+```swift
+let notificationFeed = CometChatNotificationFeed()
+let navController = UINavigationController(rootViewController: notificationFeed)
+self.present(navController, animated: true)
+```
+
+
+
+Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()` and a user logged in.
+
+---
+
+## Filtering Feed Items
+
+Control what loads using custom request builders:
+
+
+
+```swift
+let notificationFeed = CometChatNotificationFeed()
+notificationFeed.set(notificationFeedRequestBuilder:
+ NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .set(limit: 30)
+ .set(readState: .unread)
+ .set(category: "promotions")
+)
+```
+
+
+
+### Filter Options
+
+| Builder Method | Description |
+| -------------- | ----------- |
+| `.set(limit: Int)` | Items per page (default 20). |
+| `.set(readState: FeedReadState)` | `.read`, `.unread`, or omit for all. |
+| `.set(category: String)` | Filter by category name. |
+| `.set(categoryId: String)` | Filter by category ID. |
+| `.set(channelId: String)` | Filter by channel. |
+| `.set(tags: [String])` | Filter by tags. |
+| `.set(dateFrom: String)` | ISO 8601 date lower bound. |
+| `.set(dateTo: String)` | ISO 8601 date upper bound. |
+
+Pass the builder object, not the result of `.build()`. The component calls `.build()` internally.
+
+---
+
+## Actions and Events
+
+### onItemClick
+
+Fires when a feed item card is tapped.
+
+
+
+```swift
+notificationFeed.set(onItemClick: { feedItem in
+ // feedItem.id, feedItem.content (Card JSON), feedItem.category
+ print("Item tapped: \(feedItem.id)")
+})
+```
+
+
+
+---
+
+### onActionClick
+
+Fires when an interactive element (button, link) inside a card is tapped.
+
+
+
+```swift
+notificationFeed.set(onActionClick: { feedItem, actionEvent in
+ // Report engagement on every action
+ CometChat.reportFeedEngagement(feedItem, interactionString: "button_clicked", onSuccess: {}, onError: { _ in })
+
+ // Handle the action
+ switch actionEvent.action {
+ case .openUrl(let url, _):
+ if let link = URL(string: url) {
+ UIApplication.shared.open(link)
+ }
+ case .chatWithUser(let uid):
+ // Navigate to 1-on-1 chat
+ break
+ case .chatWithGroup(let guid):
+ // Navigate to group chat
+ break
+ case .customCallback(let callbackId, let payload):
+ // Custom app logic
+ break
+ default:
+ break
+ }
+})
+```
+
+
+
+---
+
+### onError
+
+Fires when an internal error occurs (network failure, SDK exception).
+
+
+
+```swift
+notificationFeed.set(onError: { error in
+ print("Feed error: \(error.errorDescription)")
+})
+```
+
+
+
+---
+
+## Automatic Behaviors
+
+The component handles these automatically — no manual setup needed:
+
+| Behavior | Description |
+| -------- | ----------- |
+| Real-time updates | New items appear at the top via WebSocket listener. |
+| Delivery reporting | Items are reported as delivered when fetched. |
+| Read reporting | Items are reported as read after 1 second of visibility. |
+| Unread count polling | Polls unread count every 30 seconds to update badges. |
+| Infinite scroll | Fetches next page when scrolling near the bottom. |
+| Pull-to-refresh | Resets and fetches fresh data on pull. |
+| Timestamp grouping | Groups items as "Today", "Yesterday", day name, or date. |
+| Category filtering | Filter chips row for category-based filtering. |
+| Connection recovery | Automatically refreshes when the app returns from background. |
+
+---
+
+## Functionality
+
+| Method | Description |
+| ------ | ----------- |
+| `set(title: String)` | Header title text. Default "Notifications". |
+| `set(showBackButton: Bool)` | Toggle back button visibility. Default `true`. |
+| `set(showFilterChips: Bool)` | Toggle category filter chips. Default `true`. |
+| `set(cardThemeMode: String)` | Card rendering theme: "auto", "light", or "dark". |
+| `set(notificationFeedRequestBuilder:)` | Custom feed request builder. |
+| `set(notificationCategoriesRequestBuilder:)` | Custom categories request builder. |
+
+
+
+```swift
+let notificationFeed = CometChatNotificationFeed()
+notificationFeed.set(title: "Activity")
+notificationFeed.set(showBackButton: false)
+notificationFeed.set(showFilterChips: true)
+notificationFeed.set(cardThemeMode: "dark")
+```
+
+
+
+---
+
+## Common Patterns
+
+### Show only unread items
+
+
+
+```swift
+notificationFeed.set(notificationFeedRequestBuilder:
+ NotificationFeedRequest.NotificationFeedRequestBuilder()
+ .set(readState: .unread)
+)
+```
+
+
+
+### Hide filter chips
+
+
+
+```swift
+notificationFeed.set(showFilterChips: false)
+```
+
+
+
+### Custom categories request
+
+
+
+```swift
+notificationFeed.set(notificationCategoriesRequestBuilder:
+ NotificationCategoriesRequest.NotificationCategoriesRequestBuilder()
+ .set(limit: 10)
+)
+```
+
+
+
+### Embed in a Tab Bar
+
+
+
+```swift
+let notificationFeed = CometChatNotificationFeed()
+notificationFeed.set(showBackButton: false)
+notificationFeed.tabBarItem = UITabBarItem(
+ title: "Notifications",
+ image: UIImage(systemName: "bell"),
+ selectedImage: UIImage(systemName: "bell.fill")
+)
+
+// Add to tab bar controller
+let navController = UINavigationController(rootViewController: notificationFeed)
+tabBarController.viewControllers?.append(navController)
+```
+
+
+
+### Handle action + report engagement
+
+
+
+```swift
+notificationFeed.set(onActionClick: { feedItem, actionEvent in
+ // Always report engagement
+ CometChat.reportFeedEngagement(feedItem, interactionString: actionEvent.elementId, onSuccess: {}, onError: { _ in })
+
+ switch actionEvent.action {
+ case .openUrl(let url, _):
+ if let link = URL(string: url) {
+ UIApplication.shared.open(link)
+ }
+ case .chatWithUser(let uid):
+ // Open CometChat messages with user
+ break
+ default:
+ break
+ }
+})
+```
+
+
+
+---
+
+## Next Steps
+
+| Topic | Description |
+| ----- | ----------- |
+| [Campaigns Feature](/ui-kit/ios/campaigns) | Overview of how campaigns work end-to-end. |
+| [SDK Campaigns API](/sdk/ios/campaigns) | Low-level SDK APIs for feed items, categories, and engagement. |
diff --git a/ui-kit/react-native/campaigns.mdx b/ui-kit/react-native/campaigns.mdx
new file mode 100644
index 000000000..ff6d26c24
--- /dev/null
+++ b/ui-kit/react-native/campaigns.mdx
@@ -0,0 +1,184 @@
+---
+title: "Campaigns"
+description: "Deliver targeted, rich notifications to users via an in-app notification feed powered by the CometChat Cards renderer."
+---
+
+CometChat Campaigns enables you to send rich, interactive notifications to users through an in-app notification feed. Each notification is rendered as a native card using the **CometChat Cards** library — supporting images, text, buttons, layouts, and interactive actions.
+
+
+Before proceeding, ensure you have completed the [UI Kit integration](/ui-kit/react-native/getting-started) and the [Dashboard Setup](/campaigns#setup-flow) for Campaigns.
+
+
+
+
+
+
+---
+
+## Overview
+
+Campaigns delivers notifications as **Card Schema JSON** — a structured format that defines the visual layout of each notification card. The system consists of three layers:
+
+1. **CometChat Chat SDK** — Fetches feed items, manages read/delivered state, provides real-time listeners, handles push notification tracking
+2. **CometChat Cards Library** — Renders Card Schema JSON into native React Native views
+3. **CometChat UI Kit** — Provides the ready-to-use `CometChatNotificationFeed` component that wires everything together
+
+### Architecture Flow
+
+```
+Dashboard / API → Campaign Created → Push + WebSocket Delivery
+ ↓
+ SDK: NotificationFeedRequestBuilder.fetchNext()
+ ↓
+ NotificationFeedItem.getContent() → Card Schema JSON
+ ↓
+ Cards Library: CometChatCardView
+ ↓
+ Native Rendered Card (images, text, buttons, layouts)
+ ↓
+ User taps button → onAction callback → Your code handles it
+```
+
+---
+
+## How Cards Work
+
+Each `NotificationFeedItem` from the SDK contains a `content` field — an object holding the Card Schema JSON. This JSON is passed directly to the CometChat Cards renderer which produces a native view.
+
+The Cards library is a **pure renderer**:
+- **Input**: Card Schema JSON string + theme mode + optional action callback
+- **Output**: Native React Native view hierarchy
+
+It does not execute actions, manage message state, or call any SDK methods. When users tap interactive elements (buttons, links), the library emits the action to your callback. You decide what happens — open a URL, navigate to a chat, make an API call, etc.
+
+### Card Schema JSON Example
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ {
+ "type": "column",
+ "backgroundColor": { "light": "transparent", "dark": "transparent" },
+ "gap": 5,
+ "items": [
+ { "type": "text", "content": "📢 Announcement", "variant": "heading2", "id": "txt_99323141-2459-4e33-88d3-ca39c5fd2f50" },
+ { "type": "text", "content": "Your announcement message here.", "variant": "body", "id": "txt_61a417bc-5e4a-4ba2-bfe7-b7bc64dbaf35" },
+ { "type": "divider", "id": "div_80f5c7fb-fd10-41d1-8c2f-51498f0f62d0" },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "backgroundColor": { "light": "transparent", "dark": "transparent" },
+ "textColor": { "light": "#6C5CE7", "dark": "#6C5CE7" },
+ "size": 40,
+ "fontSize": 13,
+ "borderRadius": 6,
+ "padding": { "top": 0, "right": 16, "bottom": 0, "left": 16 },
+ "action": { "type": "openUrl", "url": "" },
+ "id": "btn_9b87a3f1-b0c6-45b9-a4c2-e22ea590f17f"
+ }
+ ],
+ "id": "col_98fed9bd-1a95-4cee-aa81-84a9016e41f2"
+ }
+ ],
+ "fallbackText": "",
+ "style": {
+ "background": { "light": "#E8E8E8", "dark": "#E8E8E8" },
+ "borderRadius": 16,
+ "borderColor": { "light": "#DFE6E9", "dark": "#DFE6E9" },
+ "padding": 12
+ }
+}
+```
+
+The above JSON renders as:
+
+
+
+
+
+The schema supports **20 element types** (text, image, icon, avatar, badge, divider, spacer, chip, progressBar, codeBlock, markdown, row, column, grid, accordion, tabs, button, iconButton, link, table) and **9 action types** (openUrl, chatWithUser, chatWithGroup, sendMessage, copyToClipboard, downloadFile, initiateCall, apiCall, customCallback).
+
+---
+
+## How Cards Work in the UI Kit
+
+The `CometChatNotificationFeed` component uses the **CometChat Cards** library internally to render each notification. Here's what happens under the hood:
+
+1. The component fetches `NotificationFeedItem` objects from the SDK
+2. For each item, it extracts the `content` field (Card Schema JSON)
+3. It passes the JSON to `CometChatCardView` from the Cards library
+4. The Cards renderer produces native UI — text, images, buttons, layouts — directly from the JSON
+5. When users tap buttons/links inside a card, the action is emitted back to the component which handles navigation (open URL, navigate to chat, etc.)
+
+You don't need to interact with the Cards library directly when using `CometChatNotificationFeed` — it's all wired up. But if you want to render cards outside the feed (e.g., a standalone card in a modal), you can use the Cards library directly. See the [SDK Campaigns documentation](/sdk/react-native/campaigns#rendering-cards) for standalone usage.
+
+---
+
+## Handling Push Notifications for Campaigns
+
+When a campaign push notification arrives via FCM/APNs, you should:
+
+1. **Report delivery** — Call `CometChat.markPushNotificationDelivered()` when the notification is received
+2. **Report click** — Call `CometChat.markPushNotificationClicked()` when the user taps the notification
+3. **Deep link** — Use the announcement ID from the push payload to fetch the full item via `CometChat.getNotificationFeedItem(id)` and display it
+
+```tsx lines
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+
+// When push notification is received
+const pushNotification = new CometChat.PushNotification(pushPayloadData);
+CometChat.markPushNotificationDelivered(pushNotification);
+
+// When user taps the notification
+CometChat.markPushNotificationClicked(pushNotification);
+
+// Navigate to feed or show specific item
+const item = await CometChat.getNotificationFeedItem(pushNotification.getId());
+```
+
+See the [SDK Campaigns documentation](/sdk/react-native/campaigns) for the complete push notification tracking API.
+
+---
+
+## Sending Campaigns
+
+Before integrating the frontend, you need to set up channels, categories, templates, and campaigns in the CometChat Dashboard. Follow the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions with screenshots.
+
+---
+
+## Using the UI Kit Component
+
+The easiest way to add a notification feed to your app is the `CometChatNotificationFeed` component. It handles fetching, rendering, pagination, filtering, real-time updates, and engagement reporting out of the box.
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react-native";
+
+function NotificationsScreen() {
+ return (
+ {
+ // Handle item tap
+ }}
+ onBackPress={() => {
+ // Navigate back
+ }}
+ />
+ );
+}
+```
+
+See the full [CometChatNotificationFeed component documentation](/ui-kit/react-native/notification-feed) for all configuration options, styling, and customization.
+
+---
+
+## Next Steps
+
+
+
+ Full API reference for feed items, categories, engagement, and push tracking
+
+
+ Ready-to-use component with filtering, real-time updates, and styling
+
+
diff --git a/ui-kit/react-native/notification-feed.mdx b/ui-kit/react-native/notification-feed.mdx
new file mode 100644
index 000000000..33dbd9c32
--- /dev/null
+++ b/ui-kit/react-native/notification-feed.mdx
@@ -0,0 +1,544 @@
+---
+title: "Notification Feed"
+description: "Full-screen notification feed component with category filtering, card rendering, real-time updates, and engagement reporting."
+---
+
+
+```json
+{
+ "component": "CometChatNotificationFeed",
+ "package": "@cometchat/chat-uikit-react-native",
+ "import": "import { CometChatNotificationFeed } from \"@cometchat/chat-uikit-react-native\";",
+ "description": "Full-screen notification feed with category filtering, timestamp grouping, card rendering via @cometchat/cards-react-native, real-time updates, and automatic engagement reporting.",
+ "props": {
+ "data": {
+ "title": { "type": "string", "default": "\"Notifications\"" },
+ "scrollToItemId": { "type": "string", "default": "undefined", "note": "Deep link to a specific feed item" },
+ "notificationFeedRequestBuilder": { "type": "NotificationFeedRequestBuilder", "default": "SDK default (20 per page)" },
+ "notificationCategoriesRequestBuilder": { "type": "NotificationCategoriesRequestBuilder", "default": "SDK default (50 per page)" }
+ },
+ "callbacks": {
+ "onItemClick": "(feedItem: NotificationFeedItem) => void",
+ "onActionClick": "(feedItem: NotificationFeedItem, action: { type: string, params: object, elementId: string }) => void",
+ "onError": "(error: CometChat.CometChatException) => void",
+ "onBackPress": "() => void"
+ },
+ "visibility": {
+ "showHeader": { "type": "boolean", "default": true },
+ "showBackButton": { "type": "boolean", "default": false },
+ "showFilterChips": { "type": "boolean", "default": true }
+ },
+ "viewSlots": {
+ "HeaderView": "() => JSX.Element",
+ "EmptyView": "() => JSX.Element",
+ "ErrorView": "() => JSX.Element",
+ "LoadingView": "() => JSX.Element"
+ },
+ "cards": {
+ "cardThemeMode": { "type": "\"auto\" | \"light\" | \"dark\"", "default": "\"auto\"" },
+ "cardThemeOverride": { "type": "CometChatCardThemeOverride", "default": "undefined" }
+ }
+ },
+ "automaticBehaviors": [
+ "Real-time updates via WebSocket listener",
+ "Delivery reporting on fetch",
+ "Read reporting on viewport visibility",
+ "Unread count polling every 30 seconds",
+ "Infinite scroll pagination",
+ "Pull-to-refresh",
+ "Timestamp grouping (Today, Yesterday, day name, date)",
+ "Category filter chips"
+ ]
+}
+```
+
+
+`CometChatNotificationFeed` displays a scrollable notification feed where each item is rendered as a native card using the CometChat Cards library. It handles fetching, pagination, category filtering, timestamp grouping, real-time updates, and read/delivered/engagement reporting automatically.
+
+---
+
+## Where It Fits
+
+`CometChatNotificationFeed` is a full-screen component. Drop it into a screen or navigation destination. It manages its own data fetching, state, and real-time listeners — you just handle navigation callbacks.
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react-native";
+
+function NotificationsScreen({ navigation }) {
+ return (
+ navigation.goBack()}
+ onItemClick={(item) => {
+ // Handle item tap (e.g., open detail or deep link)
+ }}
+ />
+ );
+}
+```
+
+---
+
+## Minimal Render
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react-native";
+
+function NotificationsDemo() {
+ return ;
+}
+
+export default NotificationsDemo;
+```
+
+Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()` and a user logged in.
+
+---
+
+## Filtering Feed Items
+
+Control what loads using custom request builders:
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+
+function UnreadNotifications() {
+ return (
+
+ );
+}
+```
+
+### Filter Options
+
+| Builder Method | Description |
+| --- | --- |
+| `.setLimit(number)` | Items per page (default 20, max 100) |
+| `.setReadState(FeedReadState)` | `READ`, `UNREAD`, or `ALL` |
+| `.setCategory(string)` | Filter by category label |
+| `.setChannelId(string)` | Filter by channel |
+| `.setTags(string[])` | Filter by tags |
+| `.setDateFrom(string)` | ISO 8601 date lower bound |
+| `.setDateTo(string)` | ISO 8601 date upper bound |
+
+---
+
+## Actions and Events
+
+### Callback Props
+
+#### onItemClick
+
+Fires when a feed item card is tapped.
+
+```tsx lines
+ {
+ console.log("Item tapped:", item.getId());
+ }}
+/>
+```
+
+#### onActionClick
+
+Fires when an interactive element (button, link) inside a card is tapped.
+
+```tsx lines
+ {
+ const { type, params, elementId } = action;
+ switch (type) {
+ case "openUrl":
+ // Open params.url in browser
+ break;
+ case "chatWithUser":
+ // Navigate to chat with params.uid
+ break;
+ }
+ }}
+/>
+```
+
+#### onError
+
+Fires when an internal error occurs (network failure, SDK exception).
+
+```tsx lines
+ {
+ console.error("Feed error:", error.message);
+ }}
+/>
+```
+
+#### onBackPress
+
+Fires when the back button in the header is tapped.
+
+```tsx lines
+ navigation.goBack()}
+/>
+```
+
+### Automatic Behaviors
+
+The component handles these automatically — no manual setup needed:
+
+| Behavior | Description |
+| --- | --- |
+| Real-time updates | New items appear at the top via WebSocket listener |
+| Delivery reporting | Items are reported as delivered when fetched |
+| Read reporting | Items are reported as read when visible in viewport |
+| Unread count polling | Polls unread count every 30 seconds to update badges |
+| Infinite scroll | Fetches next page when scrolling near the bottom |
+| Pull-to-refresh | Resets and fetches fresh data on pull |
+| Timestamp grouping | Groups items as "Today", "Yesterday", day name, or date |
+| Category filtering | Filter chips row for category-based filtering |
+
+---
+
+## Custom View Slots
+
+### HeaderView
+
+Replace the entire header:
+
+```tsx lines
+ (
+
+ My Notifications
+
+ )}
+/>
+```
+
+### State Views
+
+```tsx lines
+ (
+
+ No notifications yet
+
+ )}
+ ErrorView={() => (
+
+ Something went wrong
+
+ )}
+ LoadingView={() => (
+
+
+
+ )}
+/>
+```
+
+---
+
+## Styling
+
+The component uses the theme system from `CometChatThemeProvider`. Pass a `style` prop to customize the appearance.
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react-native";
+
+function StyledNotifications() {
+ return (
+
+ );
+}
+```
+
+### Style Properties
+
+| Property | Description |
+| --- | --- |
+| `containerStyle` | Root container style |
+| `headerContainerStyle` | Header bar style |
+| `titleStyle` | Header title text style |
+| `chipActiveStyle` | Selected filter chip (containerStyle + textStyle) |
+| `chipInactiveStyle` | Unselected filter chip (containerStyle + textStyle) |
+| `chipBadgeStyle` | Active chip badge (containerStyle + textStyle) |
+| `chipInactiveBadgeStyle` | Inactive chip badge (containerStyle + textStyle) |
+| `sectionHeaderStyle` | Timestamp section header (containerStyle + textStyle) |
+| `itemHeaderStyle` | Per-item header (containerStyle + categoryTextStyle + timestampTextStyle) |
+| `cardContainerStyle` | Card container style |
+| `cardBorderColor` | Card border color |
+| `cardBorderRadius` | Card corner radius |
+| `cardBorderWidth` | Card border width |
+| `unreadIndicatorStyle` | Unread dot style |
+| `unreadIndicatorColor` | Unread dot color |
+| `emptyStateStyle` | Empty state (containerStyle + titleStyle + subTitleStyle) |
+| `errorStateStyle` | Error state (containerStyle + titleStyle + subTitleStyle) |
+| `separatorStyle` | Separator between cards |
+
+---
+
+## Props
+
+All props are optional.
+
+### cardThemeMode
+
+Theme mode for the card renderer.
+
+| | |
+| --- | --- |
+| Type | `"auto" \| "light" \| "dark"` |
+| Default | `"auto"` |
+
+---
+
+### cardThemeOverride
+
+Custom theme override for the card renderer.
+
+| | |
+| --- | --- |
+| Type | `CometChatCardThemeOverride` |
+| Default | `undefined` |
+
+---
+
+### EmptyView
+
+Custom component displayed when there are no notifications.
+
+| | |
+| --- | --- |
+| Type | `() => JSX.Element` |
+| Default | Built-in empty state |
+
+---
+
+### ErrorView
+
+Custom component displayed when an error occurs.
+
+| | |
+| --- | --- |
+| Type | `() => JSX.Element` |
+| Default | Built-in error state |
+
+---
+
+### HeaderView
+
+Custom component replacing the entire header.
+
+| | |
+| --- | --- |
+| Type | `() => JSX.Element` |
+| Default | Built-in header |
+
+---
+
+### LoadingView
+
+Custom component displayed during the loading state.
+
+| | |
+| --- | --- |
+| Type | `() => JSX.Element` |
+| Default | Built-in loading indicator |
+
+---
+
+### notificationCategoriesRequestBuilder
+
+Custom request builder for fetching categories.
+
+| | |
+| --- | --- |
+| Type | `NotificationCategoriesRequestBuilder` |
+| Default | SDK default (50 per page) |
+
+---
+
+### notificationFeedRequestBuilder
+
+Custom request builder for fetching feed items.
+
+| | |
+| --- | --- |
+| Type | `NotificationFeedRequestBuilder` |
+| Default | SDK default (20 per page) |
+
+---
+
+### onActionClick
+
+Callback fired when an interactive element inside a card is tapped.
+
+| | |
+| --- | --- |
+| Type | `(feedItem: NotificationFeedItem, action: { type: string, params: object, elementId: string }) => void` |
+| Default | `undefined` |
+
+---
+
+### onBackPress
+
+Callback fired when the back button is pressed.
+
+| | |
+| --- | --- |
+| Type | `() => void` |
+| Default | `undefined` |
+
+---
+
+### onError
+
+Callback fired when the component encounters an error.
+
+| | |
+| --- | --- |
+| Type | `(error: CometChat.CometChatException) => void` |
+| Default | `undefined` |
+
+---
+
+### onItemClick
+
+Callback fired when a feed item card is tapped.
+
+| | |
+| --- | --- |
+| Type | `(feedItem: NotificationFeedItem) => void` |
+| Default | `undefined` |
+
+---
+
+### scrollToItemId
+
+Deep link to a specific feed item by ID.
+
+| | |
+| --- | --- |
+| Type | `string` |
+| Default | `undefined` |
+
+---
+
+### showBackButton
+
+Shows/hides the back button in the header.
+
+| | |
+| --- | --- |
+| Type | `boolean` |
+| Default | `false` |
+
+---
+
+### showFilterChips
+
+Shows/hides the category filter chips row.
+
+| | |
+| --- | --- |
+| Type | `boolean` |
+| Default | `true` |
+
+---
+
+### showHeader
+
+Shows/hides the entire header.
+
+| | |
+| --- | --- |
+| Type | `boolean` |
+| Default | `true` |
+
+---
+
+### title
+
+Header title text.
+
+| | |
+| --- | --- |
+| Type | `string` |
+| Default | `"Notifications"` |
+
+---
+
+## Common Patterns
+
+### Show only unread items
+
+```tsx lines
+
+```
+
+### Hide filter chips and header
+
+```tsx lines
+
+```
+
+### Deep link to a specific notification
+
+```tsx lines
+
+```
+
+---
+
+## Next Steps
+
+
+
+ Overview of how campaigns work end-to-end
+
+
+ Low-level SDK APIs for feed items, categories, and engagement
+
+
+ Full styling reference for all components
+
+
+ Customize colors, fonts, and appearance
+
+
diff --git a/ui-kit/react/campaigns.mdx b/ui-kit/react/campaigns.mdx
new file mode 100644
index 000000000..989cdb05e
--- /dev/null
+++ b/ui-kit/react/campaigns.mdx
@@ -0,0 +1,215 @@
+---
+title: "Campaigns"
+description: "Deliver targeted, rich notifications to users via an in-app notification feed powered by the CometChat Cards renderer."
+---
+
+CometChat Campaigns enables you to send rich, interactive notifications to users through an in-app notification feed. Each notification is rendered as a native card using the **CometChat Cards** library — supporting images, text, buttons, layouts, and interactive actions.
+
+
+Before proceeding, ensure you have completed the [UI Kit integration](/ui-kit/react/getting-started) and the [Dashboard Setup](/campaigns#setup-flow) for Campaigns.
+
+
+
+
+
+
+---
+
+## Overview
+
+Campaigns delivers notifications as **Card Schema JSON** — a structured format that defines the visual layout of each notification card. The system consists of three layers:
+
+1. **CometChat Chat SDK** — Fetches feed items, manages read/delivered state, provides real-time listeners, handles push notification tracking
+2. **CometChat Cards Library** (`@cometchat/cards-react`) — Renders Card Schema JSON into native DOM elements
+3. **CometChat UI Kit** — Provides the ready-to-use `CometChatNotificationFeed` component that wires everything together
+
+### Architecture Flow
+
+```
+Dashboard / API → Campaign Created → Push + WebSocket Delivery
+ ↓
+ SDK: NotificationFeedRequestBuilder.fetchNext()
+ ↓
+ NotificationFeedItem.getContent() → Card Schema JSON
+ ↓
+ Cards Library: CometChatCardView
+ ↓
+ Native Rendered Card (images, text, buttons, layouts)
+ ↓
+ User clicks button → onAction callback → Your code handles it
+```
+
+---
+
+## How Cards Work
+
+Each `NotificationFeedItem` from the SDK contains a `content` field — an object holding the Card Schema JSON. This JSON is passed directly to the CometChat Cards renderer which produces a DOM element.
+
+The Cards library is a **pure renderer**:
+- **Input**: Card Schema JSON string + theme mode + optional action callback
+- **Output**: Native DOM element hierarchy
+
+It does not execute actions, manage message state, or call any SDK methods. When users click interactive elements (buttons, links), the library emits the action to your callback. You decide what happens — open a URL, navigate to a chat, make an API call, etc.
+
+### Card Schema JSON Example
+
+```json
+{
+ "version": "1.0",
+ "body": [
+ {
+ "type": "column",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "gap": 5,
+ "items": [
+ {
+ "type": "text",
+ "content": "📢 Announcement",
+ "variant": "heading2",
+ "id": "txt_99323141-2459-4e33-88d3-ca39c5fd2f50"
+ },
+ {
+ "type": "text",
+ "content": "Your announcement message here.",
+ "variant": "body",
+ "id": "txt_61a417bc-5e4a-4ba2-bfe7-b7bc64dbaf35"
+ },
+ {
+ "type": "divider",
+ "id": "div_80f5c7fb-fd10-41d1-8c2f-51498f0f62d0"
+ },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "backgroundColor": {
+ "light": "transparent",
+ "dark": "transparent"
+ },
+ "textColor": {
+ "light": "#6C5CE7",
+ "dark": "#6C5CE7"
+ },
+ "size": 40,
+ "fontSize": 13,
+ "borderRadius": 6,
+ "padding": {
+ "top": 0,
+ "right": 16,
+ "bottom": 0,
+ "left": 16
+ },
+ "action": {
+ "type": "openUrl",
+ "url": ""
+ },
+ "id": "btn_9b87a3f1-b0c6-45b9-a4c2-e22ea590f17f"
+ }
+ ],
+ "id": "col_98fed9bd-1a95-4cee-aa81-84a9016e41f2"
+ }
+ ],
+ "fallbackText": "",
+ "style": {
+ "background": {
+ "light": "#E8E8E8",
+ "dark": "#E8E8E8"
+ },
+ "borderRadius": 16,
+ "borderColor": {
+ "light": "#DFE6E9",
+ "dark": "#DFE6E9"
+ },
+ "padding": 12
+ }
+}
+```
+
+The schema supports **20 element types** (text, image, icon, avatar, badge, divider, spacer, chip, progressBar, codeBlock, markdown, row, column, grid, accordion, tabs, button, iconButton, link, table) and **9 action types** (openUrl, chatWithUser, chatWithGroup, sendMessage, copyToClipboard, downloadFile, initiateCall, apiCall, customCallback).
+
+---
+
+## How Cards Work in the UI Kit
+
+The `CometChatNotificationFeed` component uses the **CometChat Cards** library internally to render each notification. Here's what happens under the hood:
+
+1. The component fetches `NotificationFeedItem` objects from the SDK
+2. For each item, it extracts the `content` field (Card Schema JSON)
+3. It passes the JSON to `CometChatCardView` from `@cometchat/cards-react`
+4. The Cards renderer produces native UI — text, images, buttons, layouts — directly from the JSON
+5. When users click buttons/links inside a card, the action is emitted back to the component which handles navigation (open URL, navigate to chat, etc.)
+
+You don't need to interact with the Cards library directly when using `CometChatNotificationFeed` — it's all wired up. But if you want to render cards outside the feed (e.g., a standalone card in a modal), you can use the Cards library directly. See the [SDK Campaigns documentation](/sdk/javascript/campaigns#rendering-cards) for standalone usage.
+
+---
+
+## Handling Push Notifications for Campaigns
+
+When a campaign push notification arrives via Web Push or FCM, you should:
+
+1. **Report delivery** — Call `CometChat.markPushNotificationDelivered()` when the notification is received
+2. **Report click** — Call `CometChat.markPushNotificationClicked()` when the user clicks the notification
+3. **Deep link** — Use the announcement ID from the push payload to fetch the full item via `CometChat.getNotificationFeedItem(id)` and display it
+
+```tsx lines
+import { CometChat } from "@cometchat/chat-sdk-javascript";
+
+// When push notification is received (e.g., in service worker)
+const pushNotification = new CometChat.PushNotification(pushPayloadData);
+CometChat.markPushNotificationDelivered(pushNotification);
+
+// When user clicks the notification
+CometChat.markPushNotificationClicked(pushNotification);
+
+// Navigate to feed or show specific item
+const item = await CometChat.getNotificationFeedItem(pushNotification.getId());
+```
+
+See the [SDK Campaigns documentation](/sdk/javascript/campaigns) for the complete push notification tracking API.
+
+---
+
+## Sending Campaigns
+
+Before integrating the frontend, you need to set up channels, categories, templates, and campaigns in the CometChat Dashboard. Follow the [Dashboard Setup Guide](/campaigns#setup-flow) for step-by-step instructions with screenshots.
+
+---
+
+## Using the UI Kit Component
+
+The easiest way to add a notification feed to your app is the `CometChatNotificationFeed` component. It handles fetching, rendering, pagination, filtering, real-time updates, and engagement reporting out of the box.
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react";
+import "@cometchat/chat-uikit-react/css-variables.css";
+
+function NotificationsScreen() {
+ return (
+ {
+ // Handle item click
+ }}
+ onBackPress={() => {
+ // Navigate back
+ }}
+ />
+ );
+}
+```
+
+See the full [CometChatNotificationFeed component documentation](/ui-kit/react/notification-feed) for all configuration options, styling, and customization.
+
+---
+
+## Next Steps
+
+
+
+ Full API reference for feed items, categories, engagement, and push tracking
+
+
+ Ready-to-use component with filtering, real-time updates, and styling
+
+
diff --git a/ui-kit/react/notification-feed.mdx b/ui-kit/react/notification-feed.mdx
new file mode 100644
index 000000000..f963cfc39
--- /dev/null
+++ b/ui-kit/react/notification-feed.mdx
@@ -0,0 +1,758 @@
+---
+title: "Notification Feed"
+description: "Full-screen notification feed component with category filtering, card rendering, real-time updates, and engagement reporting."
+---
+
+
+```json
+{
+ "component": "CometChatNotificationFeed",
+ "package": "@cometchat/chat-uikit-react",
+ "import": "import { CometChatNotificationFeed } from \"@cometchat/chat-uikit-react\";",
+ "cssImport": "import \"@cometchat/chat-uikit-react/css-variables.css\";",
+ "description": "Full-screen notification feed with category filtering, timestamp grouping, card rendering via @cometchat/cards-react, real-time updates, and automatic engagement reporting.",
+ "cssRootClass": ".cometchat-notification-feed",
+ "props": {
+ "data": {
+ "title": { "type": "string", "default": "\"Notifications\"" },
+ "scrollToItemId": { "type": "string", "default": "undefined", "note": "Deep link to a specific feed item" },
+ "notificationFeedRequestBuilder": { "type": "NotificationFeedRequestBuilder", "default": "SDK default (20 per page)" },
+ "notificationCategoriesRequestBuilder": { "type": "NotificationCategoriesRequestBuilder", "default": "SDK default (50 per page)" }
+ },
+ "callbacks": {
+ "onItemClick": "(feedItem: NotificationFeedItem) => void",
+ "onActionClick": "(feedItem: NotificationFeedItem, action: CardAction) => void",
+ "onError": "(error: CometChat.CometChatException) => void",
+ "onBackPress": "() => void"
+ },
+ "visibility": {
+ "showHeader": { "type": "boolean", "default": true },
+ "showBackButton": { "type": "boolean", "default": false },
+ "showFilterChips": { "type": "boolean", "default": true }
+ },
+ "viewSlots": {
+ "headerView": "React.ReactNode",
+ "emptyStateView": "React.ReactNode",
+ "errorStateView": "React.ReactNode",
+ "loadingStateView": "React.ReactNode"
+ },
+ "cards": {
+ "cardThemeMode": { "type": "\"auto\" | \"light\" | \"dark\"", "default": "\"auto\"" },
+ "cardThemeOverride": { "type": "Record", "default": "undefined" }
+ },
+ "style": {
+ "type": "CometChatNotificationFeedStyle",
+ "properties": {
+ "backgroundColor": "string",
+ "width": "string",
+ "height": "string",
+ "headerTitleColor": "string",
+ "headerTitleFont": "string",
+ "chipActiveBackgroundColor": "string",
+ "chipActiveTextColor": "string",
+ "chipInactiveBackgroundColor": "string",
+ "chipInactiveTextColor": "string",
+ "chipBorderColor": "string",
+ "badgeBackgroundColor": "string",
+ "badgeTextColor": "string",
+ "separatorColor": "string",
+ "timestampTextColor": "string",
+ "timestampFont": "string",
+ "cardBackgroundColor": "string",
+ "cardBorderColor": "string",
+ "cardBorderRadius": "number",
+ "cardBorderWidth": "number",
+ "unreadIndicatorColor": "string"
+ }
+ }
+ },
+ "automaticBehaviors": [
+ "Real-time updates via WebSocket listener",
+ "Delivery reporting on fetch",
+ "Read reporting on viewport visibility (IntersectionObserver)",
+ "Unread count polling every 30 seconds",
+ "Infinite scroll pagination",
+ "Timestamp grouping (Today, Yesterday, day name, date)",
+ "Category filter chips with unread badges",
+ "Mark all read button",
+ "Offline connectivity banner"
+ ],
+ "additionalExports": {
+ "CometChatNotificationBadge": "Standalone unread count badge component",
+ "useNotificationUnreadCount": "Hook for tracking unread count with shared polling"
+ }
+}
+```
+
+
+`CometChatNotificationFeed` displays a scrollable notification feed where each item is rendered as a card using `@cometchat/cards-react`. It handles fetching, pagination, category filtering, timestamp grouping, real-time updates, and read/delivered/engagement reporting automatically.
+
+
+
+
+
+---
+
+## Where It Fits
+
+`CometChatNotificationFeed` is a full-screen component. Drop it into a page or route. It manages its own data fetching, state, and real-time listeners — you just handle navigation callbacks.
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react";
+import "@cometchat/chat-uikit-react/css-variables.css";
+
+function NotificationsPage() {
+ return (
+ window.history.back()}
+ onItemClick={(item) => {
+ // Handle item click (e.g., open detail or deep link)
+ }}
+ />
+ );
+}
+```
+
+---
+
+## Minimal Render
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react";
+import "@cometchat/chat-uikit-react/css-variables.css";
+
+function NotificationsDemo() {
+ return (
+
+
+
+ );
+}
+
+export default NotificationsDemo;
+```
+
+Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()` and a user logged in.
+
+Root CSS class: `.cometchat-notification-feed`
+
+---
+
+## Filtering Feed Items
+
+Control what loads using custom request builders:
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react";
+import { CometChat } from "@cometchat/chat-sdk-javascript";
+
+function UnreadNotifications() {
+ return (
+
+ );
+}
+```
+
+### Filter Options
+
+| Builder Method | Description |
+| --- | --- |
+| `.setLimit(number)` | Items per page (default 20, max 100) |
+| `.setReadState(state)` | `"read"`, `"unread"`, or `"all"` |
+| `.setCategory(string)` | Filter by category label |
+| `.setChannelId(string)` | Filter by channel |
+| `.setTags(string[])` | Filter by tags |
+| `.setDateFrom(string)` | ISO 8601 date lower bound |
+| `.setDateTo(string)` | ISO 8601 date upper bound |
+
+---
+
+## Actions and Events
+
+### Callback Props
+
+#### onItemClick
+
+Fires when a feed item card is clicked.
+
+```tsx lines
+ {
+ console.log("Item clicked:", item.getId());
+ }}
+/>
+```
+
+#### onActionClick
+
+Fires when an interactive element (button, link) inside a card is clicked. The `action` object contains the action type, parameters, and the element ID that triggered it.
+
+```tsx lines
+ {
+ const { type, params, elementId } = action;
+ switch (type) {
+ case "openUrl":
+ window.open(params.url, "_blank");
+ break;
+ case "chatWithUser":
+ // Navigate to chat with params.uid
+ break;
+ case "chatWithGroup":
+ // Navigate to group chat with params.guid
+ break;
+ }
+ }}
+/>
+```
+
+#### onError
+
+Fires when an internal error occurs (network failure, SDK exception).
+
+```tsx lines
+ {
+ console.error("Feed error:", error.message);
+ }}
+/>
+```
+
+#### onBackPress
+
+Fires when the back button in the header is clicked.
+
+```tsx lines
+ window.history.back()}
+/>
+```
+
+### Automatic Behaviors
+
+The component handles these automatically — no manual setup needed:
+
+| Behavior | Description |
+| --- | --- |
+| Real-time updates | New items appear at the top via WebSocket `NotificationFeedListener` |
+| Delivery reporting | Items are reported as delivered when fetched |
+| Read reporting | Items are reported as read when visible in viewport (IntersectionObserver) |
+| Unread count polling | Polls unread count every 30 seconds to update badges |
+| Infinite scroll | Fetches next page when scrolling near the bottom |
+| Timestamp grouping | Groups items as "Today", "Yesterday", day name, or date |
+| Category filtering | Filter chips row with per-category unread badges |
+| Mark all read | Header button to mark all notifications as read |
+| Offline banner | Shows connectivity warning when offline |
+
+---
+
+## Custom View Slots
+
+### headerView
+
+Replace the entire header:
+
+```tsx lines
+
+ My Notifications
+
+ }
+/>
+```
+
+### State Views
+
+```tsx lines
+
+ No notifications yet
+
+ }
+ errorStateView={
+
+ }
+ loadingStateView={
+
+ }
+/>
+```
+
+---
+
+## Styling
+
+The component uses CSS variables from the CometChat theme system. Pass a `style` prop for programmatic overrides, or use CSS classes for full control.
+
+### Style Prop
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react";
+
+function StyledNotifications() {
+ return (
+
+ );
+}
+```
+
+### Style Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| `backgroundColor` | string | Root container background |
+| `width` | string | Container width |
+| `height` | string | Container height |
+| `headerTitleColor` | string | Header title text color |
+| `headerTitleFont` | string | Header title font |
+| `chipActiveBackgroundColor` | string | Selected filter chip background |
+| `chipActiveTextColor` | string | Selected filter chip text color |
+| `chipInactiveBackgroundColor` | string | Unselected filter chip background |
+| `chipInactiveTextColor` | string | Unselected filter chip text color |
+| `chipBorderColor` | string | Inactive chip border color |
+| `badgeBackgroundColor` | string | Badge background color |
+| `badgeTextColor` | string | Badge text color |
+| `separatorColor` | string | Separator between items |
+| `timestampTextColor` | string | Timestamp text color |
+| `timestampFont` | string | Timestamp font |
+| `cardBackgroundColor` | string | Card container background |
+| `cardBorderColor` | string | Card border color |
+| `cardBorderRadius` | number | Card corner radius |
+| `cardBorderWidth` | number | Card border width |
+| `unreadIndicatorColor` | string | Unread dot color |
+
+### CSS Classes
+
+Override styles using these CSS classes:
+
+| Class | Description |
+| --- | --- |
+| `.cometchat-notification-feed` | Root container |
+| `.cometchat-notification-feed__header` | Header bar |
+| `.cometchat-notification-feed__header-title` | Header title text |
+| `.cometchat-notification-feed__header-back` | Back button |
+| `.cometchat-notification-feed__mark-all-read` | Mark all read button |
+| `.cometchat-notification-feed__chips` | Filter chips container |
+| `.cometchat-notification-feed__chip` | Individual filter chip |
+| `.cometchat-notification-feed__chip--active` | Active filter chip |
+| `.cometchat-notification-feed__chip--inactive` | Inactive filter chip |
+| `.cometchat-notification-feed__chip--inactive-with-badge` | Inactive chip with unread badge |
+| `.cometchat-notification-feed__chip-badge` | Chip unread badge |
+| `.cometchat-notification-feed__chip-badge--active` | Active chip badge |
+| `.cometchat-notification-feed__chip-badge--inactive` | Inactive chip badge |
+| `.cometchat-notification-feed__content` | Scrollable content area |
+| `.cometchat-notification-feed__item` | Feed item container |
+| `.cometchat-notification-feed__item--unread` | Unread feed item |
+| `.cometchat-notification-feed__unread-indicator` | Unread dot indicator |
+| `.cometchat-notification-feed__item-meta` | Item metadata row (category + time) |
+| `.cometchat-notification-feed__item-category` | Category label |
+| `.cometchat-notification-feed__item-time` | Timestamp |
+| `.cometchat-notification-feed__card-container` | Card wrapper |
+| `.cometchat-notification-feed__loading` | Loading state |
+| `.cometchat-notification-feed__empty` | Empty state |
+| `.cometchat-notification-feed__error` | Error state |
+| `.cometchat-notification-feed__connectivity-banner` | Offline banner |
+
+---
+
+## Props
+
+All props are optional.
+
+### cardThemeMode
+
+Theme mode for the card renderer (`@cometchat/cards-react`).
+
+| | |
+| --- | --- |
+| Type | `"auto" \| "light" \| "dark"` |
+| Default | `"auto"` |
+
+---
+
+### cardThemeOverride
+
+Custom theme override passed to the card renderer.
+
+| | |
+| --- | --- |
+| Type | `Record` |
+| Default | `undefined` |
+
+---
+
+### emptyStateView
+
+Custom component displayed when there are no notifications.
+
+| | |
+| --- | --- |
+| Type | `React.ReactNode` |
+| Default | Built-in empty state with illustration |
+
+---
+
+### errorStateView
+
+Custom component displayed when an error occurs.
+
+| | |
+| --- | --- |
+| Type | `React.ReactNode` |
+| Default | Built-in error state with retry button |
+
+---
+
+### headerView
+
+Custom component replacing the entire header.
+
+| | |
+| --- | --- |
+| Type | `React.ReactNode` |
+| Default | Built-in header with title and mark-all-read button |
+
+---
+
+### loadingStateView
+
+Custom component displayed during the initial loading state.
+
+| | |
+| --- | --- |
+| Type | `React.ReactNode` |
+| Default | Built-in loading spinner |
+
+---
+
+### notificationCategoriesRequestBuilder
+
+Custom request builder for fetching categories.
+
+| | |
+| --- | --- |
+| Type | `CometChat.NotificationCategoriesRequestBuilder` |
+| Default | SDK default (50 per page) |
+
+---
+
+### notificationFeedRequestBuilder
+
+Custom request builder for fetching feed items.
+
+| | |
+| --- | --- |
+| Type | `CometChat.NotificationFeedRequestBuilder` |
+| Default | SDK default (20 per page) |
+
+---
+
+### onActionClick
+
+Callback fired when an interactive element inside a card is clicked.
+
+| | |
+| --- | --- |
+| Type | `(feedItem: NotificationFeedItem, action: CardAction) => void` |
+| Default | `undefined` |
+
+The `CardAction` object contains:
+- `type` — Action type (e.g., `"openUrl"`, `"chatWithUser"`)
+- `params` — Action parameters (e.g., `{ url: "..." }`, `{ uid: "..." }`)
+- `elementId` — ID of the element that triggered the action
+
+---
+
+### onBackPress
+
+Callback fired when the back button is pressed.
+
+| | |
+| --- | --- |
+| Type | `() => void` |
+| Default | `undefined` |
+
+---
+
+### onError
+
+Callback fired when the component encounters an error.
+
+| | |
+| --- | --- |
+| Type | `(error: CometChat.CometChatException) => void` |
+| Default | `undefined` |
+
+---
+
+### onItemClick
+
+Callback fired when a feed item card is clicked.
+
+| | |
+| --- | --- |
+| Type | `(feedItem: NotificationFeedItem) => void` |
+| Default | `undefined` |
+
+---
+
+### scrollToItemId
+
+Deep link to a specific feed item by ID. The component scrolls to the item once loaded.
+
+| | |
+| --- | --- |
+| Type | `string` |
+| Default | `undefined` |
+
+---
+
+### showBackButton
+
+Shows/hides the back button in the header.
+
+| | |
+| --- | --- |
+| Type | `boolean` |
+| Default | `false` |
+
+---
+
+### showFilterChips
+
+Shows/hides the category filter chips row.
+
+| | |
+| --- | --- |
+| Type | `boolean` |
+| Default | `true` |
+
+---
+
+### showHeader
+
+Shows/hides the entire header.
+
+| | |
+| --- | --- |
+| Type | `boolean` |
+| Default | `true` |
+
+---
+
+### style
+
+Style customization object.
+
+| | |
+| --- | --- |
+| Type | `CometChatNotificationFeedStyle` |
+| Default | `undefined` |
+
+---
+
+### title
+
+Header title text.
+
+| | |
+| --- | --- |
+| Type | `string` |
+| Default | `"Notifications"` |
+
+---
+
+## Additional Exports
+
+### CometChatNotificationBadge
+
+A standalone badge component that displays the unread notification count. Uses a shared polling singleton — multiple badges share one interval and listener.
+
+```tsx lines
+import { CometChatNotificationBadge } from "@cometchat/chat-uikit-react";
+
+function NavBar() {
+ return (
+
+ );
+}
+```
+
+#### Props
+
+| Prop | Type | Default | Description |
+| --- | --- | --- | --- |
+| `category` | string | undefined | Filter count by category |
+| `max` | number | 99 | Maximum count before showing "N+" |
+| `style.backgroundColor` | string | `"#6852D6"` | Badge background color |
+| `style.textColor` | string | `"#fff"` | Badge text color |
+| `style.fontSize` | string | `"11px"` | Badge font size |
+| `style.borderRadius` | string | `"9999px"` | Badge border radius |
+
+---
+
+### useNotificationUnreadCount
+
+A React hook for tracking unread notification count. Uses a shared singleton — multiple components share one polling interval and WebSocket listener.
+
+```tsx lines
+import { useNotificationUnreadCount } from "@cometchat/chat-uikit-react";
+
+function NotificationIcon() {
+ const { count, refresh, isLoading } = useNotificationUnreadCount();
+
+ return (
+
+ );
+}
+```
+
+#### Return Value
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `count` | number | Current unread count |
+| `refresh` | `() => Promise` | Manually refresh the count |
+| `isLoading` | boolean | Whether the initial fetch is in progress |
+
+#### Options
+
+| Option | Type | Default | Description |
+| --- | --- | --- | --- |
+| `category` | string | undefined | Filter count by category |
+| `pollingInterval` | number | 30000 | Polling interval in milliseconds |
+
+---
+
+## Common Patterns
+
+### Show only unread items
+
+```tsx lines
+
+```
+
+### Hide filter chips and header
+
+```tsx lines
+
+```
+
+### Deep link to a specific notification
+
+```tsx lines
+
+```
+
+### Embed in a sidebar
+
+```tsx lines
+import { CometChatNotificationFeed } from "@cometchat/chat-uikit-react";
+import "@cometchat/chat-uikit-react/css-variables.css";
+
+function NotificationsSidebar() {
+ return (
+
+
+
+ );
+}
+```
+
+### Add notification badge to navigation
+
+```tsx lines
+import {
+ CometChatNotificationFeed,
+ CometChatNotificationBadge,
+} from "@cometchat/chat-uikit-react";
+import "@cometchat/chat-uikit-react/css-variables.css";
+
+function App() {
+ const [showFeed, setShowFeed] = useState(false);
+
+ return (
+ <>
+
+ {showFeed && (
+ setShowFeed(false)}
+ showBackButton={true}
+ />
+ )}
+ >
+ );
+}
+```
+
+---
+
+## Next Steps
+
+
+
+ Overview of how campaigns work end-to-end
+
+
+ Low-level SDK APIs for feed items, categories, and engagement
+
+
+ Customize colors, fonts, and appearance
+
+
+ Browse all prebuilt UI components
+
+